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:
marian 2013-06-03 07:50:14 +00:00
parent 67c5b6a96a
commit a5385c2f47
6 changed files with 458 additions and 91 deletions

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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;
}

View File

@ -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

View File

@ -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,&params);
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);