Fixed timestamps for sent media: send timestamps using media source timestamp, sync audio timestamps with transaction timestamp.
git-svn-id: http://yate.null.ro/svn/yate/trunk@5511 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
67c5b6a96a
commit
a5385c2f47
|
@ -30,6 +30,39 @@
|
|||
; Defaults to yes
|
||||
;calltoken_rejectmissing=yes
|
||||
|
||||
; adjust_ts_out_threshold: integer: The difference, in milliseconds, between sent audio data
|
||||
; timestamp and transaction timestamp at which audio data timestamp will be adjusted
|
||||
; Its value will be rouded up to a multiple of 10
|
||||
; This value is applied on reload for new calls only
|
||||
; It can be overridden from routing
|
||||
; Defaults to 120
|
||||
; Allowed interval: 20 .. 300
|
||||
;adjust_ts_out_threshold=120
|
||||
|
||||
; adjust_ts_out_over: integer: Interval, in milliseconds, to adjust sent audio data
|
||||
; timestamp on data overrun (the sender transmits data on a rate greater then expected)
|
||||
; When applied the packets will be dropped until data timestamp will be at least at the
|
||||
; value of last sent packet timestamp
|
||||
; NOTE: Choose values greater the packet time to drop more packets at a time.
|
||||
; Lower values will drop less packets in a row but this will happen more frequently
|
||||
; Its value will be rouded up to a multiple of 10
|
||||
; This value is applied on reload for new calls only
|
||||
; It can be overridden from routing
|
||||
; It can't be greater then adjust_ts_out_threshold
|
||||
; Defaults to 120
|
||||
; Allowed interval: 10 .. adjust_ts_out_threshold
|
||||
;adjust_ts_out_over=120
|
||||
|
||||
; adjust_ts_out_under: integer: Interval, in milliseconds, to adjust sent audio data
|
||||
; timestamp on data underrun (the sender transmits data on a rate less then expected)
|
||||
; Its value will be rouded up to a multiple of 10
|
||||
; This value is applied on reload for new calls only
|
||||
; It can be overridden from routing
|
||||
; It can't be greater then 2 * adjust_ts_out_threshold - 1
|
||||
; Defaults to 60
|
||||
; Allowed interval: 10 .. 2 * adjust_ts_out_threshold - 1
|
||||
;adjust_ts_out_under=60
|
||||
|
||||
; tos: keyword: Type Of Service to set in outgoing UDP packets
|
||||
; numeric TOS value or: lowdelay, throughput, reliability, mincost
|
||||
;tos=0
|
||||
|
|
|
@ -37,6 +37,12 @@ using namespace TelEngine;
|
|||
// Minimum value for local call numbers
|
||||
#define IAX2_MIN_CALLNO 2
|
||||
|
||||
// Outgoing data adjust timestamp defaults
|
||||
#define IAX2_ADJUSTTSOUT_THRES 120
|
||||
#define IAX2_ADJUSTTSOUT_OVER 120
|
||||
#define IAX2_ADJUSTTSOUT_UNDER 60
|
||||
|
||||
|
||||
// Build an MD5 digest from secret, address, integer value and engine run id
|
||||
// MD5(addr.host() + secret + addr.port() + t)
|
||||
static void buildSecretDigest(String& buf, const String& secret, unsigned int t,
|
||||
|
@ -70,6 +76,9 @@ IAXEngine::IAXEngine(const char* iface, int port, u_int16_t transListCount, u_in
|
|||
m_format(format),
|
||||
m_formatVideo(0),
|
||||
m_capability(capab),
|
||||
m_adjustTsOutThreshold(IAX2_ADJUSTTSOUT_THRES),
|
||||
m_adjustTsOutOverrun(IAX2_ADJUSTTSOUT_OVER),
|
||||
m_adjustTsOutUnderrun(IAX2_ADJUSTTSOUT_UNDER),
|
||||
m_mutexTrunk(true,"IAXEngine::Trunk"),
|
||||
m_trunkSendInterval(trunkSendInterval)
|
||||
{
|
||||
|
@ -312,6 +321,81 @@ bool IAXEngine::process()
|
|||
return ok;
|
||||
}
|
||||
|
||||
static inline void roundUp10(unsigned int& value)
|
||||
{
|
||||
unsigned int rest = value % 10;
|
||||
if (rest)
|
||||
value += 10 - rest;
|
||||
}
|
||||
|
||||
// Initialize outgoing data timestamp adjust values
|
||||
void IAXEngine::initOutDataAdjust(const NamedList& params, IAXTransaction* tr)
|
||||
{
|
||||
const String* thresS = 0;
|
||||
const String* overS = 0;
|
||||
const String* underS = 0;
|
||||
NamedIterator iter(params);
|
||||
for (const NamedString* ns = 0; 0 != (ns = iter.get());) {
|
||||
if (ns->name() == YSTRING("adjust_ts_out_threshold"))
|
||||
thresS = ns;
|
||||
else if (ns->name() == YSTRING("adjust_ts_out_over"))
|
||||
overS = ns;
|
||||
else if (ns->name() == YSTRING("adjust_ts_out_under"))
|
||||
underS = ns;
|
||||
}
|
||||
// No need to set transaction's data if no parameter found
|
||||
if (tr && !(thresS || overS || underS))
|
||||
return;
|
||||
Lock lck(tr ? (Mutex*)tr : (Mutex*)this);
|
||||
unsigned int thresDef = IAX2_ADJUSTTSOUT_THRES;
|
||||
unsigned int overDef = IAX2_ADJUSTTSOUT_OVER;
|
||||
unsigned int underDef = IAX2_ADJUSTTSOUT_UNDER;
|
||||
if (tr) {
|
||||
thresDef = tr->m_adjustTsOutThreshold;
|
||||
overDef = tr->m_adjustTsOutOverrun;
|
||||
underDef = tr->m_adjustTsOutUnderrun;
|
||||
}
|
||||
unsigned int thres = thresS ? thresS->toInteger(thresDef,0,20,300) : thresDef;
|
||||
unsigned int over = overS ? overS->toInteger(overDef,0,10) : overDef;
|
||||
unsigned int under = underS ? underS->toInteger(underDef,0,10) : underDef;
|
||||
bool adjusted = false;
|
||||
// Round down to multiple of 10
|
||||
roundUp10(thres);
|
||||
roundUp10(over);
|
||||
roundUp10(under);
|
||||
// Overrun must not be greater then threshold
|
||||
if (over > thres) {
|
||||
over = thres;
|
||||
adjusted = true;
|
||||
}
|
||||
// Underrun must be less then 2 * threshold
|
||||
unsigned int doubleThres = 2 * thres;
|
||||
if (under >= doubleThres) {
|
||||
under = doubleThres - 10;
|
||||
adjusted = true;
|
||||
}
|
||||
if (tr) {
|
||||
tr->m_adjustTsOutThreshold = thres;
|
||||
tr->m_adjustTsOutOverrun = over;
|
||||
tr->m_adjustTsOutUnderrun = under;
|
||||
Debug(this,DebugAll,
|
||||
"Transaction(%u,%u) adjust ts out set to thres=%u over=%u under=%u [%p]",
|
||||
tr->localCallNo(),tr->remoteCallNo(),thres,over,under,tr);
|
||||
return;
|
||||
}
|
||||
m_adjustTsOutThreshold = thres;
|
||||
m_adjustTsOutOverrun = over;
|
||||
m_adjustTsOutUnderrun = under;
|
||||
if (adjusted)
|
||||
Debug(this,DebugConf,
|
||||
"Adjust ts out set to thres=%u over=%u under=%u from thres=%s over=%s under=%s",
|
||||
thres,over,under,TelEngine::c_safe(thresS),
|
||||
TelEngine::c_safe(overS),TelEngine::c_safe(underS));
|
||||
else
|
||||
Debug(this,DebugAll,"Adjust ts out set to thres=%u over=%u under=%u",
|
||||
thres,over,under);
|
||||
}
|
||||
|
||||
// (Re)Initialize the engine
|
||||
void IAXEngine::initialize(const NamedList& params)
|
||||
{
|
||||
|
@ -324,6 +408,7 @@ void IAXEngine::initialize(const NamedList& params)
|
|||
m_showCallTokenFailures = params.getBoolValue("calltoken_printfailure");
|
||||
m_rejectMissingCallToken = params.getBoolValue("calltoken_rejectmissing",true);
|
||||
m_printMsg = params.getBoolValue("printmsg",true);
|
||||
initOutDataAdjust(params);
|
||||
}
|
||||
|
||||
void IAXEngine::readSocket(SocketAddr& addr)
|
||||
|
|
|
@ -640,6 +640,37 @@ void IAXAuthMethod::authList(String& dest, u_int16_t auth, char sep)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IAXFormatDesc
|
||||
*/
|
||||
// Set the format
|
||||
void IAXFormatDesc::setFormat(u_int32_t fmt, int type)
|
||||
{
|
||||
m_format = IAXFormat::mask(fmt,type);
|
||||
if (!m_format) {
|
||||
m_multiplier = 1;
|
||||
return;
|
||||
}
|
||||
if (type == IAXFormat::Audio) {
|
||||
switch (m_format) {
|
||||
case IAXFormat::G722:
|
||||
// 16kHz samplig rate
|
||||
m_multiplier = 16;
|
||||
break;
|
||||
default:
|
||||
// Assume 8kHz sampling rate
|
||||
m_multiplier = 8;
|
||||
}
|
||||
}
|
||||
else if (type == IAXFormat::Video)
|
||||
// Assume 90kHz sampling rate for video
|
||||
m_multiplier = 90;
|
||||
else
|
||||
m_multiplier = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IAXFormat
|
||||
*/
|
||||
|
@ -680,11 +711,11 @@ const String IAXFormat::s_typesList[IAXFormat::TypeCount] = { "audio", "video",
|
|||
void IAXFormat::set(u_int32_t* fmt, u_int32_t* fmtIn, u_int32_t* fmtOut)
|
||||
{
|
||||
if (fmt)
|
||||
m_format = mask(*fmt,m_type);
|
||||
m_format.setFormat(*fmt,m_type);
|
||||
if (fmtIn)
|
||||
m_formatIn = mask(*fmtIn,m_type);
|
||||
m_formatIn.setFormat(*fmtIn,m_type);
|
||||
if (fmtOut)
|
||||
m_formatOut = mask(*fmtOut,m_type);
|
||||
m_formatOut.setFormat(*fmtOut,m_type);
|
||||
}
|
||||
|
||||
void IAXFormat::formatList(String& dest, u_int32_t formats, const TokenDict* dict,
|
||||
|
|
|
@ -38,10 +38,11 @@ unsigned char IAXTransaction::m_maxInFrames = 100;
|
|||
// Print statistics
|
||||
void IAXMediaData::print(String& buf)
|
||||
{
|
||||
Lock lck(this);
|
||||
Lock2 lck(m_inMutex,m_outMutex);
|
||||
buf << "PS=" << m_sent << ",OS=" << m_sentBytes;
|
||||
buf << ",PR=" << m_recv << ",OR=" << m_recvBytes;
|
||||
buf << ",PL=" << m_ooPackets << ",OL=" << m_ooBytes;
|
||||
buf << ",PD=" << m_dropOut << ",OD=" << m_dropOutBytes;
|
||||
}
|
||||
|
||||
|
||||
|
@ -76,6 +77,9 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t
|
|||
m_expire(60),
|
||||
m_format(IAXFormat::Audio), m_formatVideo(IAXFormat::Video),
|
||||
m_capability(0), m_callToken(false),
|
||||
m_adjustTsOutThreshold(0),
|
||||
m_adjustTsOutOverrun(0),
|
||||
m_adjustTsOutUnderrun(0),
|
||||
m_trunkFrame(0),
|
||||
m_trunkInOffsetTimeMs(0), m_trunkInLastTs(0), m_warnTrunkInTimestamp(true)
|
||||
{
|
||||
|
@ -103,6 +107,8 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t
|
|||
localCallNo(),remoteCallNo(),frame->subclass(),this);
|
||||
return;
|
||||
}
|
||||
engine->getOutDataAdjust(m_adjustTsOutThreshold,m_adjustTsOutOverrun,
|
||||
m_adjustTsOutUnderrun);
|
||||
// Append frame to incoming list
|
||||
Lock lock(this);
|
||||
m_inFrames.append(frame);
|
||||
|
@ -140,6 +146,9 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
|
|||
m_expire(60),
|
||||
m_format(IAXFormat::Audio), m_formatVideo(IAXFormat::Video),
|
||||
m_capability(0), m_callToken(false),
|
||||
m_adjustTsOutThreshold(0),
|
||||
m_adjustTsOutOverrun(0),
|
||||
m_adjustTsOutUnderrun(0),
|
||||
m_trunkFrame(0),
|
||||
m_trunkInOffsetTimeMs(0), m_trunkInLastTs(0), m_warnTrunkInTimestamp(true)
|
||||
{
|
||||
|
@ -189,6 +198,8 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
|
|||
m_type = Incorrect;
|
||||
return;
|
||||
}
|
||||
engine->getOutDataAdjust(m_adjustTsOutThreshold,m_adjustTsOutOverrun,
|
||||
m_adjustTsOutUnderrun);
|
||||
postFrameIes(IAXFrame::IAX,frametype,ies);
|
||||
changeState(NewLocalInvite);
|
||||
}
|
||||
|
@ -219,26 +230,6 @@ IAXTransaction* IAXTransaction::factoryOut(IAXEngine* engine, Type type, u_int16
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Retrieve the media of a given type
|
||||
IAXFormat* IAXTransaction::getFormat(int type)
|
||||
{
|
||||
if (type == IAXFormat::Audio)
|
||||
return &m_format;
|
||||
if (type == IAXFormat::Video)
|
||||
return &m_formatVideo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Retrieve the media data for a given type
|
||||
IAXMediaData* IAXTransaction::getData(int type)
|
||||
{
|
||||
if (type == IAXFormat::Audio)
|
||||
return &m_dataAudio;
|
||||
if (type == IAXFormat::Video)
|
||||
return &m_dataVideo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
IAXTransaction* IAXTransaction::processFrame(IAXFrame* frame)
|
||||
{
|
||||
if (!frame)
|
||||
|
@ -325,8 +316,9 @@ IAXTransaction* IAXTransaction::processMedia(DataBlock& data, u_int32_t tStamp,
|
|||
IAXFormat::typeName(type));
|
||||
return 0;
|
||||
}
|
||||
Lock lck(d);
|
||||
if (!fmt->in()) {
|
||||
Lock lck(d->m_inMutex);
|
||||
const IAXFormatDesc& desc = fmt->formatDesc(true);
|
||||
if (!desc.format()) {
|
||||
if (d->m_showInNoFmt) {
|
||||
Debug(m_engine,DebugInfo,
|
||||
"Transaction(%u,%u) received %s data without format [%p]",
|
||||
|
@ -335,6 +327,11 @@ IAXTransaction* IAXTransaction::processMedia(DataBlock& data, u_int32_t tStamp,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
if (!d->m_startedIn) {
|
||||
d->m_startedIn = true;
|
||||
Debug(m_engine,DebugAll,"Transaction(%u,%u) started incoming media '%s' [%p]",
|
||||
localCallNo(),remoteCallNo(),fmt->typeName(),this);
|
||||
}
|
||||
d->m_showInNoFmt = true;
|
||||
d->m_recv++;
|
||||
d->m_recvBytes += data.length();
|
||||
|
@ -379,7 +376,7 @@ IAXTransaction* IAXTransaction::processMedia(DataBlock& data, u_int32_t tStamp,
|
|||
"Transaction(%u,%u) forwarding %u %s data mark=%u ts=%u [%p]",
|
||||
localCallNo(),remoteCallNo(),data.length(),fmt->typeName(),
|
||||
mark,tStamp,this);
|
||||
m_engine->processMedia(this,data,tStamp,type,mark);
|
||||
m_engine->processMedia(this,data,tStamp * desc.multiplier(),type,mark);
|
||||
return 0;
|
||||
}
|
||||
d->m_ooPackets++;
|
||||
|
@ -391,8 +388,8 @@ IAXTransaction* IAXTransaction::processMedia(DataBlock& data, u_int32_t tStamp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
unsigned int IAXTransaction::sendMedia(const DataBlock& data, u_int32_t format,
|
||||
int type, bool mark)
|
||||
unsigned int IAXTransaction::sendMedia(const DataBlock& data, unsigned int tStamp,
|
||||
u_int32_t format, int type, bool mark)
|
||||
{
|
||||
if (!data.length())
|
||||
return 0;
|
||||
|
@ -406,15 +403,94 @@ unsigned int IAXTransaction::sendMedia(const DataBlock& data, u_int32_t format,
|
|||
IAXFormat::typeName(type));
|
||||
return 0;
|
||||
}
|
||||
d->m_sent++;
|
||||
d->m_sentBytes += data.length();
|
||||
u_int32_t ts = (u_int32_t)timeStamp();
|
||||
// Avoid sending the same timestamp twice for non video
|
||||
if (type != IAXFormat::Video && d->m_lastOut && ts == d->m_lastOut)
|
||||
ts++;
|
||||
Lock lck(d->m_outMutex);
|
||||
u_int64_t msecNow = Time::msecNow();
|
||||
u_int32_t transTs = (u_int32_t)(msecNow - m_timeStamp);
|
||||
// Check format change
|
||||
bool fmtChanged = (fmt->out() != format);
|
||||
if (fmtChanged) {
|
||||
Debug(m_engine,DebugNote,
|
||||
"Transaction(%u,%u). Outgoing %s format changed %u --> %u [%p]",
|
||||
localCallNo(),remoteCallNo(),fmt->typeName(),fmt->out(),format,this);
|
||||
fmt->set(0,0,&format);
|
||||
}
|
||||
const IAXFormatDesc& desc = fmt->formatDesc(false);
|
||||
u_int32_t ts = 0;
|
||||
unsigned int delta = 0;
|
||||
if (d->m_startedOut) {
|
||||
if (desc.multiplier() > 1) {
|
||||
if (d->m_outFirstSrcTs > tStamp) {
|
||||
if (d->m_showOutOldTs) {
|
||||
Debug(m_engine,DebugNote,
|
||||
"Transaction(%u,%u) dropping outgoing %s %u bytes with old tStamp=%u (first=%u) [%p]",
|
||||
localCallNo(),remoteCallNo(),fmt->typeName(),data.length(),
|
||||
tStamp,d->m_outFirstSrcTs,this);
|
||||
d->m_showOutOldTs = false;
|
||||
}
|
||||
d->dropOut(data.length());
|
||||
return 0;
|
||||
}
|
||||
d->m_showOutOldTs = true;
|
||||
unsigned int srcTsDelta = (tStamp - d->m_outFirstSrcTs) / desc.multiplier();
|
||||
ts = d->m_outStartTransTs + srcTsDelta;
|
||||
// Audio
|
||||
if (type == IAXFormat::Audio) {
|
||||
if (ts > transTs) {
|
||||
// Voice timestamp is past transaction timestamp
|
||||
// Packets arrived on intervals shorter then expected
|
||||
// Data overrun: decrease timestamp
|
||||
delta = ts - transTs;
|
||||
if (delta >= m_adjustTsOutThreshold) {
|
||||
d->dropOut(data.length());
|
||||
d->m_outStartTransTs -= m_adjustTsOutOverrun;
|
||||
DDebug(m_engine,DebugNote,
|
||||
"Transaction(%u,%u) voice overrun ts=%u transTs=%u [%p]",
|
||||
localCallNo(),remoteCallNo(),ts,transTs,this);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (ts < transTs) {
|
||||
// Voice timestamp is behind transaction timestamp
|
||||
// Packets arrived on intervals longer then expected
|
||||
// Data underrun: increase timestamp
|
||||
delta = transTs - ts;
|
||||
if (delta >= m_adjustTsOutThreshold) {
|
||||
d->m_outStartTransTs += m_adjustTsOutUnderrun;
|
||||
DDebug(m_engine,DebugInfo,
|
||||
"Transaction(%u,%u) voice underrun ts=%u transTs=%u [%p]",
|
||||
localCallNo(),remoteCallNo(),ts,transTs,this);
|
||||
}
|
||||
}
|
||||
// Avoid sending the same timestamp twice
|
||||
if (ts == d->m_lastOut)
|
||||
ts++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ts = transTs;
|
||||
// Audio: avoid sending the same timestamp twice
|
||||
if (type == IAXFormat::Audio && ts == d->m_lastOut)
|
||||
ts++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
d->m_startedOut = true;
|
||||
d->m_outStartTransTs = transTs;
|
||||
d->m_outFirstSrcTs = tStamp;
|
||||
ts = d->m_outStartTransTs;
|
||||
Debug(m_engine,DebugAll,"Transaction(%u,%u) started outgoing media '%s' [%p]",
|
||||
localCallNo(),remoteCallNo(),fmt->typeName(),this);
|
||||
}
|
||||
if (ts < d->m_lastOut) {
|
||||
d->dropOut(data.length());
|
||||
DDebug(m_engine,DebugNote,
|
||||
"Transaction(%u,%u) %s ts %u less then last sent %u [%p]",
|
||||
localCallNo(),remoteCallNo(),fmt->typeName(),ts,d->m_lastOut,this);
|
||||
return 0;
|
||||
}
|
||||
// Format changed or timestamp wrapped around
|
||||
// Send a full frame
|
||||
bool fullFrame = (fmt->out() != format) || !d->m_lastOut;
|
||||
bool fullFrame = fmtChanged || !d->m_lastOut;
|
||||
if (!fullFrame) {
|
||||
// Voice: timestamp is lowest 16 bits
|
||||
// Video: timestamp is lowest 15 bits
|
||||
|
@ -425,20 +501,12 @@ unsigned int IAXTransaction::sendMedia(const DataBlock& data, u_int32_t format,
|
|||
// we had a media gap greater then mask
|
||||
fullFrame = ((ts & mask) < (d->m_lastOut & mask)) || ((ts - d->m_lastOut) > mask);
|
||||
}
|
||||
if (fullFrame) {
|
||||
if (fmt->out() != format) {
|
||||
Debug(m_engine,DebugNote,
|
||||
"Transaction(%u,%u). Outgoing %s format changed %u --> %u [%p]",
|
||||
localCallNo(),remoteCallNo(),fmt->typeName(),fmt->out(),format,this);
|
||||
fmt->set(0,0,&format);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else
|
||||
Debug(m_engine,DebugInfo,
|
||||
"Transaction(%u,%u). Sending full frame for media '%s': ts=%u last=%u [%p]",
|
||||
localCallNo(),remoteCallNo(),fmt->typeName(),ts,d->m_lastOut,this);
|
||||
if (fullFrame && !fmtChanged)
|
||||
Debug(m_engine,DebugInfo,
|
||||
"Transaction(%u,%u). Sending full frame for media '%s': ts=%u last=%u [%p]",
|
||||
localCallNo(),remoteCallNo(),fmt->typeName(),ts,d->m_lastOut,this);
|
||||
#endif
|
||||
}
|
||||
d->m_lastOut = ts;
|
||||
unsigned int sent = 0;
|
||||
if (type == IAXFormat::Audio) {
|
||||
|
@ -483,10 +551,12 @@ unsigned int IAXTransaction::sendMedia(const DataBlock& data, u_int32_t format,
|
|||
else
|
||||
Debug(m_engine,DebugStub,
|
||||
"IAXTransaction::sendMedia() not implemented for type '%s'",fmt->typeName());
|
||||
d->m_sent++;
|
||||
d->m_sentBytes += sent;
|
||||
XDebug(m_engine,sent == data.length() ? DebugAll : DebugNote,
|
||||
"Transaction(%u,%u) sent %u/%u media=%s mark=%u ts=%u [%p]",
|
||||
"Transaction(%u,%u) sent %u/%u media=%s mark=%u ts=%u tStamp=%u transTs=%u [%p]",
|
||||
localCallNo(),remoteCallNo(),sent,data.length(),
|
||||
fmt->typeName(),mark,ts,this);
|
||||
fmt->typeName(),mark,ts,tStamp,transTs,this);
|
||||
return sent;
|
||||
}
|
||||
|
||||
|
@ -515,7 +585,7 @@ IAXEvent* IAXTransaction::getEvent(u_int64_t time)
|
|||
}
|
||||
// Time to Ping remote peer ?
|
||||
if (time > m_timeToNextPing && state() != Terminating) {
|
||||
postFrame(IAXFrame::IAX,IAXControl::Ping,0,0,(u_int32_t)timeStamp(),false);
|
||||
postFrame(IAXFrame::IAX,IAXControl::Ping,0,0,0,false);
|
||||
m_timeToNextPing = time + m_pingInterval;
|
||||
}
|
||||
// Process outgoing frames
|
||||
|
@ -884,16 +954,10 @@ bool IAXTransaction::updateTrunkRecvTs(u_int32_t& frameTs, u_int32_t ts, u_int64
|
|||
void IAXTransaction::print(bool printStats, bool printFrames, const char* location)
|
||||
{
|
||||
if (m_engine && !m_engine->debugAt(DebugAll))
|
||||
return;
|
||||
String stats;
|
||||
if (printStats && m_type == New) {
|
||||
stats << " audio: ";
|
||||
m_dataAudio.print(stats);
|
||||
stats << " video: ";
|
||||
m_dataVideo.print(stats);
|
||||
}
|
||||
printFrames = false;
|
||||
String buf;
|
||||
if (printFrames) {
|
||||
buf << "\r\n-----";
|
||||
SocketAddr addr;
|
||||
ObjList* l;
|
||||
buf << "\r\nOutgoing frames: " << m_outFrames.count();
|
||||
|
@ -906,12 +970,44 @@ void IAXTransaction::print(bool printStats, bool printFrames, const char* locati
|
|||
IAXFullFrame* frame = static_cast<IAXFullFrame*>(l->get());
|
||||
frame->toString(buf,addr,remoteAddr(),true);
|
||||
}
|
||||
buf << "\r\n-----";
|
||||
}
|
||||
Debug(m_engine,DebugAll,
|
||||
"Transaction(%u,%u) %s remote=%s:%u type=%u state=%u timestamp=" FMT64U "%s [%p]%s%s%s",
|
||||
if (m_type != New) {
|
||||
Debug(m_engine,DebugAll,
|
||||
"Transaction(%u,%u) %s remote=%s:%d type=%u state=%u timestamp=" FMT64U " [%p]%s",
|
||||
localCallNo(),remoteCallNo(),location,remoteAddr().host().c_str(),remoteAddr().port(),
|
||||
type(),state(),(u_int64_t)timeStamp(),this,buf.safe());
|
||||
return;
|
||||
}
|
||||
String stats;
|
||||
int level = DebugAll;
|
||||
if (printStats) {
|
||||
stats << " audio: ";
|
||||
m_dataAudio.print(stats);
|
||||
if (m_formatVideo.format()) {
|
||||
stats << " video: ";
|
||||
m_dataVideo.print(stats);
|
||||
}
|
||||
}
|
||||
if (m_dataAudio.m_dropOut) {
|
||||
Lock lck(m_dataAudio.m_outMutex);
|
||||
unsigned int total = m_dataAudio.m_dropOut + m_dataAudio.m_sent;
|
||||
float percent = (float)m_dataAudio.m_dropOut / (float)total * 100;
|
||||
if (percent > 0.5) {
|
||||
if (percent < 3)
|
||||
level = DebugInfo;
|
||||
else if (percent < 5)
|
||||
level = DebugNote;
|
||||
else
|
||||
level = DebugMild;
|
||||
}
|
||||
if (!printStats)
|
||||
stats << " dropped audio packets=" << m_dataAudio.m_dropOut << "/" << total;
|
||||
}
|
||||
Debug(m_engine,level,
|
||||
"Transaction(%u,%u) %s remote=%s:%d type=%u state=%u timestamp=" FMT64U "%s [%p]%s",
|
||||
localCallNo(),remoteCallNo(),location,remoteAddr().host().c_str(),remoteAddr().port(),
|
||||
type(),state(),(u_int64_t)timeStamp(),stats.safe(),this,
|
||||
buf ? "\r\n-----" : "",buf.safe(),buf ? "\r\n-----" : "");
|
||||
type(),state(),(u_int64_t)timeStamp(),stats.safe(),this,buf.safe());
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
|
@ -1056,7 +1152,8 @@ void IAXTransaction::postFrame(IAXFrame::Type type, u_int32_t subclass, void* da
|
|||
if (state() == Terminated)
|
||||
return;
|
||||
// Pong and LagRp don't need timestamp to be adjusted
|
||||
if (type != IAXFrame::IAX ||
|
||||
// Don't adjust for video
|
||||
if ((type != IAXFrame::IAX && type == IAXFrame::Video) ||
|
||||
(subclass != IAXControl::Pong && subclass != IAXControl::LagRp))
|
||||
adjustTStamp(tStamp);
|
||||
IAXFrameOut* frame = new IAXFrameOut(type,subclass,m_lCallNo,m_rCallNo,m_oSeqNo,m_iSeqNo,tStamp,
|
||||
|
@ -1790,14 +1887,19 @@ void IAXTransaction::eventTerminated(IAXEvent* event)
|
|||
|
||||
void IAXTransaction::adjustTStamp(u_int32_t& tStamp)
|
||||
{
|
||||
if (!tStamp)
|
||||
if (!tStamp) {
|
||||
tStamp = (u_int32_t)timeStamp();
|
||||
if (m_lastFullFrameOut) {
|
||||
// adjust timestamp to be different from the last sent
|
||||
int32_t delta = tStamp - m_lastFullFrameOut;
|
||||
if (delta <= 0)
|
||||
tStamp = m_lastFullFrameOut + 1;
|
||||
// Make sure we don't send old timestamp
|
||||
IAXMediaData* d = getData(IAXFormat::Audio);
|
||||
if (d) {
|
||||
Lock lck(d->m_outMutex);
|
||||
if (tStamp <= d->m_lastOut)
|
||||
tStamp = d->m_lastOut + 1;
|
||||
}
|
||||
}
|
||||
// Adjust timestamp to be different from the last sent
|
||||
if (tStamp <= m_lastFullFrameOut)
|
||||
tStamp = m_lastFullFrameOut + 1;
|
||||
m_lastFullFrameOut = tStamp;
|
||||
}
|
||||
|
||||
|
|
|
@ -541,6 +541,47 @@ public:
|
|||
static TokenDict s_texts[];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This class holds IAX format description
|
||||
* @short IAX format description
|
||||
*/
|
||||
class YIAX_API IAXFormatDesc
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
inline IAXFormatDesc()
|
||||
: m_format(0), m_multiplier(1)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Get the format
|
||||
* @return The format
|
||||
*/
|
||||
inline u_int32_t format() const
|
||||
{ return m_format; }
|
||||
|
||||
/**
|
||||
* Get the format multiplier used to translate timestamps
|
||||
* @return The format multiplier (always greater then 0)
|
||||
*/
|
||||
inline unsigned int multiplier() const
|
||||
{ return m_multiplier; }
|
||||
|
||||
/**
|
||||
* Set the format
|
||||
* @param fmt The format
|
||||
* @param type Format type as IAXFormat::Media enumeration
|
||||
*/
|
||||
void setFormat(u_int32_t fmt, int type);
|
||||
|
||||
protected:
|
||||
u_int32_t m_format; // The format
|
||||
unsigned int m_multiplier; // Format multiplier derived from sampling rate
|
||||
};
|
||||
|
||||
/**
|
||||
* This class holds the enumeration values for audio and video formats
|
||||
* @short Wrapper class for audio and video formats
|
||||
|
@ -593,7 +634,7 @@ public:
|
|||
* @param type Media type
|
||||
*/
|
||||
inline IAXFormat(int type = Audio)
|
||||
: m_type(type), m_format(0), m_formatIn(0), m_formatOut(0)
|
||||
: m_type(type)
|
||||
{}
|
||||
|
||||
/**
|
||||
|
@ -608,28 +649,36 @@ public:
|
|||
* @return The format
|
||||
*/
|
||||
inline u_int32_t format() const
|
||||
{ return m_format; }
|
||||
{ return m_format.format(); }
|
||||
|
||||
/**
|
||||
* Get the incoming format
|
||||
* @return The incoming format
|
||||
*/
|
||||
inline u_int32_t in() const
|
||||
{ return m_formatIn; }
|
||||
{ return m_formatIn.format(); }
|
||||
|
||||
/**
|
||||
* Get the outgoing format
|
||||
* @return The outgoing format
|
||||
*/
|
||||
inline u_int32_t out() const
|
||||
{ return m_formatOut; }
|
||||
{ return m_formatOut.format(); }
|
||||
|
||||
/**
|
||||
* Get the incoming or outgoing format description
|
||||
* @param in True to retrieve the incoming format, false to retrieve the outgoing one
|
||||
* @return Requested format desc
|
||||
*/
|
||||
inline const IAXFormatDesc& formatDesc(bool in) const
|
||||
{ return in ? m_formatIn : m_formatOut; }
|
||||
|
||||
/**
|
||||
* Get the text associated with the format
|
||||
* @return Format name
|
||||
*/
|
||||
inline const char* formatName() const
|
||||
{ return formatName(m_format); }
|
||||
{ return formatName(format()); }
|
||||
|
||||
/**
|
||||
* Get the text associated with the media type
|
||||
|
@ -746,9 +795,9 @@ public:
|
|||
|
||||
protected:
|
||||
int m_type;
|
||||
u_int32_t m_format;
|
||||
u_int32_t m_formatIn;
|
||||
u_int32_t m_formatOut;
|
||||
IAXFormatDesc m_format;
|
||||
IAXFormatDesc m_formatIn;
|
||||
IAXFormatDesc m_formatOut;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1346,10 +1395,10 @@ private:
|
|||
|
||||
/**
|
||||
* This class holds data used by transaction to sync media.
|
||||
* The mutex is not reentrant
|
||||
* The mutexes are not reentrant
|
||||
* @short IAX2 transaction media data
|
||||
*/
|
||||
class YIAX_API IAXMediaData : public Mutex
|
||||
class YIAX_API IAXMediaData
|
||||
{
|
||||
friend class IAXTransaction;
|
||||
public:
|
||||
|
@ -1357,12 +1406,27 @@ public:
|
|||
* Constructor
|
||||
*/
|
||||
inline IAXMediaData()
|
||||
: Mutex(true,"IAXTransaction::InMedia"),
|
||||
: m_inMutex(false,"IAXTransaction::InMedia"),
|
||||
m_outMutex(false,"IAXTransaction::OutMedia"),
|
||||
m_startedIn(false), m_startedOut(false),
|
||||
m_outStartTransTs(0), m_outFirstSrcTs(0),
|
||||
m_lastOut(0), m_lastIn(0), m_sent(0), m_sentBytes(0),
|
||||
m_recv(0), m_recvBytes(0), m_ooPackets(0), m_ooBytes(0),
|
||||
m_showInNoFmt(true)
|
||||
m_showInNoFmt(true), m_showOutOldTs(true),
|
||||
m_dropOut(0), m_dropOutBytes(0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Increase drop out data
|
||||
* @param len The number of dropped bytes
|
||||
*/
|
||||
inline void dropOut(unsigned int len) {
|
||||
if (len) {
|
||||
m_dropOut++;
|
||||
m_dropOutBytes += len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print statistics
|
||||
* @param buf Destination buffer
|
||||
|
@ -1370,6 +1434,12 @@ public:
|
|||
void print(String& buf);
|
||||
|
||||
protected:
|
||||
Mutex m_inMutex;
|
||||
Mutex m_outMutex;
|
||||
bool m_startedIn; // Incoming media started
|
||||
bool m_startedOut; // Outgoing media started
|
||||
int m_outStartTransTs; // Transaction timestamp where media send started
|
||||
unsigned int m_outFirstSrcTs; // First outgoing source packet timestamp as received from source
|
||||
u_int32_t m_lastOut; // Last transmitted mini timestamp
|
||||
u_int32_t m_lastIn; // Last received timestamp
|
||||
unsigned int m_sent; // Packets sent
|
||||
|
@ -1379,6 +1449,9 @@ protected:
|
|||
unsigned int m_ooPackets; // Dropped received out of order packets
|
||||
unsigned int m_ooBytes; // Dropped received out of order bytes
|
||||
bool m_showInNoFmt; // Show incoming media arrival without format debug
|
||||
bool m_showOutOldTs; // Show dropped media out debug message
|
||||
unsigned int m_dropOut; // The number of dropped outgoing packets
|
||||
unsigned int m_dropOutBytes; // The number of dropped outgoing bytes
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1565,14 +1638,26 @@ public:
|
|||
* @param type Media type to retrieve
|
||||
* @return IAXFormat pointer or 0 for invalid type
|
||||
*/
|
||||
IAXFormat* getFormat(int type);
|
||||
inline IAXFormat* getFormat(int type) {
|
||||
if (type == IAXFormat::Audio)
|
||||
return &m_format;
|
||||
if (type == IAXFormat::Video)
|
||||
return &m_formatVideo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the media data for a given type
|
||||
* @param type Media type to retrieve
|
||||
* @return IAXMediaData pointer or 0 for invalid type
|
||||
*/
|
||||
IAXMediaData* getData(int type);
|
||||
inline IAXMediaData* getData(int type) {
|
||||
if (type == IAXFormat::Audio)
|
||||
return &m_dataAudio;
|
||||
if (type == IAXFormat::Video)
|
||||
return &m_dataVideo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the media format used during initialization
|
||||
|
@ -1636,7 +1721,7 @@ public:
|
|||
/**
|
||||
* Process received media data
|
||||
* @param data Received data
|
||||
* @param tStamp Mini frame timestamp
|
||||
* @param tStamp Mini frame timestamp multiplied by format multiplier
|
||||
* @param type Media type
|
||||
* @param full True if received in a full frame
|
||||
* @param mark Mark flag
|
||||
|
@ -1648,12 +1733,13 @@ public:
|
|||
/**
|
||||
* Send media data to remote peer. Update the outgoing media format if changed
|
||||
* @param data Data to send
|
||||
* @param tStamp Data timestamp
|
||||
* @param format Data format
|
||||
* @param type Media type
|
||||
* @param mark Mark flag
|
||||
* @return The number of bytes sent
|
||||
*/
|
||||
unsigned int sendMedia(const DataBlock& data, u_int32_t format,
|
||||
unsigned int sendMedia(const DataBlock& data, unsigned int tStamp, u_int32_t format,
|
||||
int type = IAXFormat::Audio, bool mark = false);
|
||||
|
||||
/**
|
||||
|
@ -2243,6 +2329,11 @@ private:
|
|||
IAXFormat m_formatVideo; // Video format
|
||||
u_int32_t m_capability; // Media capability of this transaction
|
||||
bool m_callToken; // Call token supported/expected
|
||||
unsigned int m_adjustTsOutThreshold; // Adjust outgoing data timestamp threshold
|
||||
unsigned int m_adjustTsOutOverrun; // Value used to adjust outgoing data timestamp on data
|
||||
// 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)
|
||||
// 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
|
||||
|
@ -2530,6 +2621,27 @@ public:
|
|||
inline u_int32_t capability() const
|
||||
{ return m_capability; }
|
||||
|
||||
/**
|
||||
* Retrieve outgoing data timestamp adjust values
|
||||
* @param thres Adjust outgoing data timestamp threshold
|
||||
* @param over Value used to adjust outgoing data timestamp on data overrun
|
||||
* @param under Value used to adjust outgoing data timestamp on data underrun
|
||||
*/
|
||||
inline void getOutDataAdjust(unsigned int& thres, unsigned int& over,
|
||||
unsigned int& under) const {
|
||||
thres = m_adjustTsOutThreshold;
|
||||
over = m_adjustTsOutOverrun;
|
||||
under = m_adjustTsOutUnderrun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize outgoing data timestamp adjust values.
|
||||
* This method is thread safe
|
||||
* @param params Parameters list
|
||||
* @param tr Optional transaction to init, initialize the engine's data if 0
|
||||
*/
|
||||
void initOutDataAdjust(const NamedList& params, IAXTransaction* tr = 0);
|
||||
|
||||
/**
|
||||
* (Re)Initialize the engine
|
||||
* @param params Parameter list
|
||||
|
@ -2785,6 +2897,11 @@ private:
|
|||
u_int32_t m_format; // The default media format
|
||||
u_int32_t m_formatVideo; // Default video format
|
||||
u_int32_t m_capability; // The media capability
|
||||
unsigned int m_adjustTsOutThreshold; // Adjust outgoing data timestamp threshold
|
||||
unsigned int m_adjustTsOutOverrun; // Value used to adjust outgoing data timestamp on data
|
||||
// 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)
|
||||
// Trunking
|
||||
Mutex m_mutexTrunk; // Mutex for trunk operations
|
||||
ObjList m_trunkList; // Trunk frames list
|
||||
|
|
|
@ -1014,13 +1014,10 @@ void YIAXEngine::processMedia(IAXTransaction* transaction, DataBlock& data,
|
|||
if (conn) {
|
||||
DataSource* src = conn->getSourceMedia(type);
|
||||
if (src) {
|
||||
unsigned long ts = tStamp;
|
||||
if (IAXFormat::Audio == type)
|
||||
ts *= 8; // assume 8kHz sampling
|
||||
unsigned long flags = 0;
|
||||
if (mark)
|
||||
flags = DataNode::DataMark;
|
||||
src->Forward(data,ts,flags);
|
||||
src->Forward(data,tStamp,flags);
|
||||
}
|
||||
else
|
||||
DDebug(this,DebugAll,"processMedia. No media source");
|
||||
|
@ -1474,6 +1471,7 @@ bool YIAXDriver::msgExecute(Message& msg, String& dest)
|
|||
IAXTransaction* tr = m_iaxEngine->call(addr,params);
|
||||
if (!tr)
|
||||
return false;
|
||||
tr->getEngine()->initOutDataAdjust(msg,tr);
|
||||
YIAXConnection* conn = new YIAXConnection(m_iaxEngine,tr,&msg,¶ms);
|
||||
conn->initChan();
|
||||
tr->setUserData(conn);
|
||||
|
@ -1660,7 +1658,7 @@ unsigned long YIAXConsumer::Consume(const DataBlock& data, unsigned long tStamp,
|
|||
m_total += data.length();
|
||||
if (m_connection->transaction()) {
|
||||
bool mark = (flags & DataMark) != 0;
|
||||
sent = m_connection->transaction()->sendMedia(data,format(),m_type,mark);
|
||||
sent = m_connection->transaction()->sendMedia(data,tStamp,format(),m_type,mark);
|
||||
}
|
||||
}
|
||||
return sent;
|
||||
|
@ -1734,6 +1732,7 @@ void YIAXConnection::callAccept(Message& msg)
|
|||
DDebug(this,DebugCall,"callAccept [%p]",this);
|
||||
m_mutexTrans.lock();
|
||||
if (m_transaction) {
|
||||
m_transaction->getEngine()->initOutDataAdjust(msg,m_transaction);
|
||||
u_int32_t codecs = iplugin.getEngine()->capability();
|
||||
if (msg.getValue("formats")) {
|
||||
u_int32_t ca = IAXFormat::mask(codecs,IAXFormat::Audio);
|
||||
|
|
Loading…
Reference in New Issue