Added video support to iax channel. Added/changed debug.

git-svn-id: http://yate.null.ro/svn/yate/trunk@4644 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
marian 2011-10-12 14:23:19 +00:00
parent 22a7a05cc9
commit 04b7b695a1
5 changed files with 1324 additions and 596 deletions

View File

@ -68,6 +68,7 @@ IAXEngine::IAXEngine(const char* iface, int port, u_int16_t transListCount, u_in
m_showCallTokenFailures(false),
m_printMsg(true),
m_format(format),
m_formatVideo(0),
m_capability(capab),
m_mutexTrunk(true,"IAXEngine::Trunk"),
m_trunkSendInterval(trunkSendInterval)
@ -320,7 +321,8 @@ void IAXEngine::readSocket(SocketAddr& addr)
}
}
bool IAXEngine::writeSocket(const void* buf, int len, const SocketAddr& addr, IAXFullFrame* frame)
bool IAXEngine::writeSocket(const void* buf, int len, const SocketAddr& addr,
IAXFullFrame* frame, unsigned int* sent)
{
if (m_printMsg && frame && debugAt(DebugInfo)) {
String s;
@ -338,12 +340,17 @@ bool IAXEngine::writeSocket(const void* buf, int len, const SocketAddr& addr, IA
tmp.c_str(),m_socket.error());
}
#ifdef DEBUG
else
else {
String tmp;
Thread::errorString(tmp,m_socket.error());
Debug(this,DebugMild,"Socket temporary unavailable: %s (%d)",
::strerror(m_socket.error()),m_socket.error());
tmp.c_str(),m_socket.error());
}
#endif
return false;
}
if (sent)
*sent = (unsigned int)len;
return true;
}
@ -573,66 +580,58 @@ bool IAXEngine::checkCallToken(const SocketAddr& addr, IAXFullFrame& frame)
return false;
}
bool IAXEngine::acceptFormatAndCapability(IAXTransaction* trans, unsigned int* caps)
bool IAXEngine::acceptFormatAndCapability(IAXTransaction* trans, unsigned int* caps, int type)
{
if (!trans)
return false;
if (caps)
trans->m_capability &= (u_int32_t)*caps;
u_int32_t format = trans->format();
u_int32_t capability = m_capability & trans->capability();
#ifdef XDEBUG
if (!trans->outgoing()) {
String rec;
IAXFormat::formatList(rec,m_capability);
Debug(this,DebugAll,"acceptFormatAndCapability. Local: Format: %u(%s). Capabilities: '%s'.",
m_format,IAXFormat::audioText(m_format),rec.c_str());
IAXFormat::formatList(rec,trans->capability());
Debug(this,DebugAll,"acceptFormatAndCapability. Received: Format: %u(%s). Capabilities: '%s'.",
format,IAXFormat::audioText(format),rec.c_str());
u_int32_t transCapsNonType = IAXFormat::clear(trans->m_capability,type);
IAXFormat* fmt = trans->getFormat(type);
if (!fmt) {
DDebug(this,DebugStub,"acceptFormatAndCapability() No media %s in transaction",
IAXFormat::typeName(type));
trans->m_capability = transCapsNonType;
return false;
}
#endif
u_int32_t transCapsType = IAXFormat::mask(trans->m_capability,type);
u_int32_t capability = transCapsType & m_capability;
if (caps)
capability &= IAXFormat::mask(*caps,type);
trans->m_capability = transCapsNonType | capability;
XDebug(this,DebugAll,
"acceptFormatAndCapability trans(%u,%u) type=%s caps(trans/our/param/result)=%u/%u/%u/%u",
trans->localCallNo(),trans->remoteCallNo(),
fmt->typeName(),transCapsType,IAXFormat::mask(m_capability,type),
caps ? IAXFormat::mask(*caps,type) : 0,capability);
// Valid capability ?
if (!capability) {
trans->m_capability = 0;
trans->m_format = 0;
// Warn if we should have media
if (type == IAXFormat::Audio || 0 != (trans->outgoing() ? fmt->in() : fmt->out()))
Debug(this,DebugNote,"Transaction(%u,%u) no common format(s) for media '%s' [%p]",
trans->localCallNo(),trans->remoteCallNo(),fmt->typeName(),trans);
// capability is 0, use it to set format
if (trans->outgoing())
trans->m_formatIn = 0;
fmt->set(&capability,&capability,0);
else
trans->m_formatOut = 0;
Debug(this,DebugNote,"Transaction(%u,%u) without common media [%p]",
trans->localCallNo(),trans->remoteCallNo(),trans);
fmt->set(&capability,0,&capability);
return false;
}
for (;;) {
// Received format is valid ?
if (0 != (format & capability) && IAXFormat::audioText(format))
break;
// Local format is valid ?
format = m_format;
if (0 != (m_format & capability) && IAXFormat::audioText(format))
break;
// No valid format: choose one from capability
format = 0;
u_int32_t i = 0;
for (; IAXFormat::audioData[i].value; i++)
if (0 != (capability & IAXFormat::audioData[i].value))
break;
if (IAXFormat::audioData[i].value) {
format = IAXFormat::audioData[i].value;
break;
}
Debug(this,DebugNote,"Unable to choose a common format for transaction(%u,%u) [%p]",
trans->localCallNo(),trans->remoteCallNo(),trans);
return false;
u_int32_t format = fmt->format();
// Received format is valid ?
if (0 == (format & capability)) {
format = (type == IAXFormat::Audio) ? m_format : 0;
format = IAXFormat::pickFormat(capability,format);
}
DDebug(this,DebugAll,"acceptFormatAndCapability. Format %u: '%s'.",
format,IAXFormat::audioText(format));
trans->m_format = format;
trans->m_capability = capability;
trans->m_formatIn = format;
trans->m_formatOut = format;
return true;
if (format) {
fmt->set(&format,&format,&format);
Debug(this,DebugAll,"Transaction(%u,%u) set format %u (%s) for media '%s' [%p]",
trans->localCallNo(),trans->remoteCallNo(),format,fmt->formatName(),
fmt->typeName(),trans);
}
else
Debug(this,DebugNote,
"Transaction(%u,%u) failed to choose a common format for media '%s' [%p]",
trans->localCallNo(),trans->remoteCallNo(),fmt->typeName(),trans);
return format != 0;
}
void IAXEngine::defaultEventHandler(IAXEvent* event)

View File

@ -242,7 +242,7 @@ bool IAXIEList::createFromFrame(const IAXFullFrame* frame, bool incoming)
m_list.clear();
if (!frame)
return true;
if (frame->type() == IAXFrame::Voice)
if (frame->type() == IAXFrame::Voice || frame->type() == IAXFrame::Video)
return true;
unsigned char* data = (unsigned char*)(((IAXFullFrame*)frame)->data().data());
unsigned int len = ((IAXFullFrame*)frame)->data().length();
@ -484,7 +484,7 @@ void IAXIEList::toString(String& dest, const char* indent)
if (ie->type() == IAXInfoElement::AUTHMETHODS)
IAXAuthMethod::authList(tmp,val,',');
else
IAXFormat::formatList(tmp,val,',');
IAXFormat::formatList(tmp,val);
dest << " (" << tmp << ')';
}
break;
@ -617,45 +617,89 @@ void IAXAuthMethod::authList(String& dest, u_int16_t auth, char sep)
/*
* IAXFormat
*/
TokenDict IAXFormat::audioData[] = {
const TokenDict IAXFormat::s_formats[] = {
{"G.723.1", G723_1},
{"GSM", GSM},
{"G.711 mu-law", ULAW},
{"G.711 a-law", ALAW},
{"G.726", MP3},
{"G.726", G726},
{"IMA ADPCM", ADPCM},
{"SLIN", SLIN},
{"LPC10", LPC10},
{"G729", G729A},
{"G.729", G729},
{"SPEEX", SPEEX},
{"ILBC", ILBC},
{"G.726 AAL2", G726AAL2},
{"G.722", G722},
{"AMR", AMR},
{"JPEG", JPEG},
{"PNG", PNG},
{"H261", H261},
{"H263", H263},
{"H263p", H263p},
{"H264", H264},
{0,0}
};
TokenDict IAXFormat::videoData[] = {
{"JPEG", IAXFormat::JPEG},
{"PNG", IAXFormat::PNG},
{"H261", IAXFormat::H261},
{"H263", IAXFormat::H263},
const TokenDict IAXFormat::s_types[] = {
{"audio", Audio},
{"video", Video},
{"image", Image},
{0,0}
};
void IAXFormat::formatList(String& dest, u_int32_t formats, char sep)
// Set format
void IAXFormat::set(u_int32_t* fmt, u_int32_t* fmtIn, u_int32_t* fmtOut)
{
String s(sep);
u_int32_t i;
for (i = 0; audioData[i].value; i++) {
if (0 == (audioData[i].value & formats))
continue;
dest.append(audioData[i].token,s);
}
for (i = 0; videoData[i].value; i++) {
if (0 == (videoData[i].value & formats))
continue;
dest.append(videoData[i].token,s);
}
if (fmt)
m_format = mask(*fmt,m_type);
if (fmtIn)
m_formatIn = mask(*fmtIn,m_type);
if (fmtOut)
m_formatOut = mask(*fmtOut,m_type);
}
void IAXFormat::formatList(String& dest, u_int32_t formats, const TokenDict* dict,
const char* sep)
{
if (!dict)
dict = s_formats;
for (; dict->value; dict++)
if (0 != (dict->value & formats))
dest.append(dict->token,sep);
}
// Pick a format from a list of capabilities
u_int32_t IAXFormat::pickFormat(u_int32_t formats, u_int32_t format)
{
if (0 != (format & formats))
return format;
if (!formats)
return 0;
format = 1;
for (unsigned int i = 0; i < (8 * sizeof(u_int32_t)); i++, format = format << 1)
if (0 != (format & formats))
return format;
return 0;
}
// Encode a formats list
u_int32_t IAXFormat::encode(const String& formats, const TokenDict* dict, char sep)
{
if (!dict)
return 0;
u_int32_t mask = 0;
ObjList* list = formats.split(',',false);
for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
int fmt = lookup(o->get()->toString(),dict);
if (fmt > 0)
mask |= fmt;
}
TelEngine::destruct(list);
return mask;
}
/*
* IAXControl
*/
@ -718,9 +762,9 @@ TokenDict IAXFrame::s_types[] = {
};
IAXFrame::IAXFrame(Type type, u_int16_t sCallNo, u_int32_t tStamp, bool retrans,
const unsigned char* buf, unsigned int len)
const unsigned char* buf, unsigned int len, bool mark)
: m_data((char*)buf,len,true), m_retrans(retrans), m_type(type),
m_sCallNo(sCallNo), m_tStamp(tStamp)
m_sCallNo(sCallNo), m_tStamp(tStamp), m_mark(mark)
{
// XDebug(DebugAll,"IAXFrame::IAXFrame(%u) [%p]",type,this);
}
@ -746,9 +790,17 @@ IAXFrame* IAXFrame::parse(const unsigned char* buf, unsigned int len, IAXEngine*
retrans = true;
dcn &= 0x7fff;
}
u_int32_t sc = IAXFrame::unpackSubclass(buf[11]);
u_int32_t sc = 0;
bool mark = false;
if (buf[10] != IAXFrame::Video)
sc = IAXFrame::unpackSubclass(buf[11]);
else {
mark = 0 != (buf[11] & 0x40);
// Clear the mark flag
sc = IAXFrame::unpackSubclass(buf[11] & 0xbf);
}
u_int32_t ts = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
return new IAXFullFrame((IAXFrame::Type)buf[10],sc,scn,dcn,buf[8],buf[9],ts,retrans,buf+12,len-12);
return new IAXFullFrame((IAXFrame::Type)buf[10],sc,scn,dcn,buf[8],buf[9],ts,retrans,buf+12,len-12,mark);
}
// Meta frame ?
if (scn == 0) {
@ -756,13 +808,10 @@ IAXFrame* IAXFrame::parse(const unsigned char* buf, unsigned int len, IAXEngine*
// Meta video
if (len < 6)
return 0;
scn = (buf[4] << 8) | buf[5];
bool retrans = false;
if (scn & 0x8000) {
retrans = true;
scn &= 0x7fff;
}
return new IAXFrame(IAXFrame::Video,dcn & 0x7fff,scn,retrans,buf+6,len-6);
// Timestamp: lowest 15 bits of transmiter timestamp
scn = ((buf[4] & 0x7f) << 8) | buf[5];
bool mark = (0 != (buf[4] & 0x80));
return new IAXFrame(IAXFrame::Video,dcn & 0x7fff,scn & 0x7fff,false,buf+6,len-6,mark);
}
// Meta trunk frame - we need to push chunks into the engine
if (!(engine && addr))
@ -823,6 +872,30 @@ IAXFrame* IAXFrame::parse(const unsigned char* buf, unsigned int len, IAXEngine*
return new IAXFrame(IAXFrame::Voice,scn,dcn,false,buf+4,len-4);
}
// Build a miniframe buffer
void IAXFrame::buildMiniFrame(DataBlock& dest, u_int16_t sCallNo, u_int32_t tStamp,
void* data, unsigned int len)
{
unsigned char header[4] = {sCallNo >> 8,sCallNo & 0xff,tStamp >> 8,tStamp & 0xff};
dest.assign(header,4);
dest.append(data,len);
}
// Build a video meta frame buffer
void IAXFrame::buildVideoMetaFrame(DataBlock& dest, u_int16_t sCallNo, u_int32_t tStamp,
bool mark, void* data, unsigned int len)
{
unsigned char header[6] = {0,0};
header[2] = 0x80 | ((sCallNo >> 8) & 0x7f);
header[3] = (unsigned char)sCallNo;
header[4] = (tStamp >> 8) & 0x7f;
if (mark)
header[4] |= 0x80;
header[5] = tStamp;
dest.assign(header,6);
dest.append(data,len);
}
u_int8_t IAXFrame::packSubclass(u_int32_t value)
{
if (value < 0x80)
@ -830,8 +903,8 @@ u_int8_t IAXFrame::packSubclass(u_int32_t value)
if (value == 0x80)
return 0x87;
if ((value > 0x9f) && (value <= 0xff)) {
DDebug(DebugMild,"IAXFrame nonstandard pack %u",value);
return (u_int8_t)value;
Debug(DebugMild,"IAXFrame nonstandard pack %u",value);
return 0;
}
// No need to start from zero, we already know it's >= 2^8
u_int32_t v = 0x100;
@ -840,7 +913,7 @@ u_int8_t IAXFrame::packSubclass(u_int32_t value)
return i | 0x80;
v <<= 1;
}
Debug(DebugGoOn,"IAXFrame could not pack subclass %u (0x%08x)",value,value);
Debug(DebugGoOn,"IAXFrame could not pack subclass %u (0x%x)",value,value);
return 0;
}
@ -848,10 +921,10 @@ u_int32_t IAXFrame::unpackSubclass(u_int8_t value)
{
if (value > 0x9f) {
DDebug(DebugMild,"IAXFrame nonstandard unpack %u",value);
return value;
return 0;
}
if (value & 0x80)
return 1 << value & 0x7f;
return 1 << (value & 0x7f);
return value;
}
@ -885,25 +958,27 @@ TokenDict IAXFullFrame::s_controlTypes[] = {
IAXFullFrame::IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo,
u_int32_t tStamp, bool retrans,
const unsigned char* buf, unsigned int len)
: IAXFrame(type,sCallNo,tStamp,retrans,buf,len),
const unsigned char* buf, unsigned int len, bool mark)
: IAXFrame(type,sCallNo,tStamp,retrans,buf,len,mark),
m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass),
m_ieList(0)
{
// XDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]",
// type,subclass,this);
XDebug(DebugAll,
"IAXFullFrame() incoming type=%u subclass=%u callno=(%u,%u) seq=(%u,%u) ts=%u retrans=%u [%p]",
type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,retrans,this);
}
IAXFullFrame::IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo,
u_int32_t tStamp,
const unsigned char* buf, unsigned int len)
: IAXFrame(type,sCallNo,tStamp,false,0,0),
const unsigned char* buf, unsigned int len, bool mark)
: IAXFrame(type,sCallNo,tStamp,false,0,0,mark),
m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass),
m_ieList(0)
{
// XDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]",
// type,subclass,this);
XDebug(DebugAll,
"IAXFullFrame() outgoing type=%u subclass=%u callno=(%u,%u) seq=(%u,%u) ts=%u [%p]",
type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,this);
setDataHeader();
if (buf)
m_data.append((void*)buf,(unsigned int)len);
@ -912,13 +987,14 @@ IAXFullFrame::IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_i
// Constructor. Constructs an outgoing full frame
IAXFullFrame::IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo,
u_int32_t tStamp, IAXIEList* ieList, u_int16_t maxlen)
: IAXFrame(type,sCallNo,tStamp,false,0,0),
u_int32_t tStamp, IAXIEList* ieList, u_int16_t maxlen, bool mark)
: IAXFrame(type,sCallNo,tStamp,false,0,0,mark),
m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass),
m_ieList(ieList)
{
// XDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]",
// type,subclass,this);
XDebug(DebugAll,
"IAXFullFrame() outgoing type=%u subclass=%u callno=(%u,%u) seq=(%u,%u) ts=%u [%p]",
type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,this);
updateBuffer(maxlen);
}
@ -926,33 +1002,29 @@ void IAXFullFrame::toString(String& dest, const SocketAddr& local,
const SocketAddr& remote, bool incoming)
{
#define STARTLINE(indent) "\r\n" << indent
#define TMP_TEXT (tmp ? tmp : unk)
const char* enclose = "\r\n-----";
const char* unk = "???";
const char* tmp = 0;
dest << enclose;
String stmp;
setStringFromInteger(stmp,type(),1);
tmp = typeText(type());
dest << STARTLINE("") << TMP_TEXT << " (" << stmp << ")";
dest << STARTLINE("") << typeText(type()) << " (" << stmp << ")";
String extra;
// Subclass
String subc;
switch (type()) {
case IAXFrame::IAX:
case IAXFrame::Control:
tmp = (type() == IAXFrame::IAX ? IAXControl::typeText(subclass()) :
subc = (type() == IAXFrame::IAX ? IAXControl::typeText(subclass()) :
controlTypeText(subclass()));
subc = TMP_TEXT;
break;
case IAXFrame::DTMF:
subc << (char)subclass();
break;
case IAXFrame::Voice:
case IAXFrame::Video:
extra << "Mark: " << String::boolText(mark());
// fallthrough
case IAXFrame::Voice:
case IAXFrame::Image:
tmp = (type() == IAXFrame::Voice ? IAXFormat::audioText(subclass()) :
IAXFormat::videoText(subclass()));
subc = TMP_TEXT;
IAXFormat::formatList(subc,subclass());
break;
case IAXFrame::Null:
subc = "Subclass: ";
@ -967,10 +1039,10 @@ void IAXFullFrame::toString(String& dest, const SocketAddr& local,
subc << (unsigned int)(subclass()) << " -dBov";
break;
default:
subc = unk;
;
}
setStringFromInteger(stmp,subclass(),4);
dest << " - " << subc << " (" << stmp << ")";
dest << " - " << (subc ? subc.c_str() : "???") << " (" << stmp << ")";
// Addresses
if (incoming)
dest << STARTLINE(" ") << "Incoming from ";
@ -994,6 +1066,8 @@ void IAXFullFrame::toString(String& dest, const SocketAddr& local,
dest << ". Timestamp: " << (unsigned int)(timeStamp());
dest << ". Retrans: " << String::boolText(retrans());
dest << ". Sequence numbers: Out: " << oSeqNo() << " In: " << iSeqNo();
if (extra)
dest << STARTLINE(" ") << extra;
// IEs
updateIEList(incoming);
if (!m_ieList->empty()) {
@ -1009,7 +1083,6 @@ void IAXFullFrame::toString(String& dest, const SocketAddr& local,
dest << "No Information Element(s)";
}
dest << enclose;
#undef TMP_TEXT
#undef STARTLINE
}
@ -1052,8 +1125,7 @@ IAXIEList* IAXFullFrame::removeIEList(bool delObj)
IAXFullFrame::~IAXFullFrame()
{
// XDebug(DebugAll,"IAXFullFrame::~IAXFullFrame(%u,%u) [%p]",
// type(),m_subclass,this);
XDebug(DebugAll,"IAXFullFrame::~IAXFullFrame(%u,%u) [%p]",type(),m_subclass,this);
}
IAXFullFrame* IAXFullFrame::fullFrame()
@ -1090,6 +1162,8 @@ void IAXFullFrame::setDataHeader()
header[10] = type();
// Subclass
header[11] = packSubclass(m_subclass);
if (mark())
header[11] |= 0x40;
// Set data
m_data.assign(header,sizeof(header));
}

View File

@ -34,6 +34,17 @@ String IAXTransaction::s_iax_modNoUsername("Username is missing");
unsigned char IAXTransaction::m_maxInFrames = 100;
// Print statistics
void IAXMediaData::print(String& buf)
{
Lock lck(this);
buf << "PS=" << m_sent << ",OS=" << m_sentBytes;
buf << ",PR=" << m_recv << ",OR=" << m_recvBytes;
buf << ",PL=" << m_ooPackets << ",OL=" << m_ooBytes;
}
IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t lcallno,
const SocketAddr& addr, void* data)
: Mutex(true,"IAXTransaction"),
@ -51,10 +62,7 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t
m_engine(engine),
m_userdata(data),
m_lastFullFrameOut(0),
m_lastMiniFrameOut(0xFFFF),
m_lastMiniFrameIn(0),
m_lastAck(0xFFFF),
m_mutexInMedia(true,"IAXTransaction::InMedia"),
m_pendingEvent(0),
m_currentEvent(0),
m_retransCount(5),
@ -66,14 +74,12 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t
m_inDroppedFrames(0),
m_authmethod(IAXAuthMethod::MD5),
m_expire(60),
m_format(0),
m_formatIn(0),
m_formatOut(0),
m_format(IAXFormat::Audio), m_formatVideo(IAXFormat::Video),
m_capability(0), m_callToken(false),
m_trunkFrame(0)
{
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) incoming [%p]",
localCallNo(),remoteCallNo(),this);
Debug(m_engine,DebugAll,"Transaction(%u,%u) incoming type=%u remote=%s:%d [%p]",
localCallNo(),remoteCallNo(),m_type,m_addr.host().c_str(),m_addr.port(),this);
// Setup transaction
m_retransCount = engine->retransCount();
m_retransInterval = engine->retransInterval();
@ -92,8 +98,8 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t
m_type = Poke;
break;
default:
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) incoming [%p]. Unsupported type: %u",
localCallNo(),remoteCallNo(),this,frame->subclass());
Debug(m_engine,DebugNote,"Transaction(%u,%u) incoming with unsupported type %u [%p]",
localCallNo(),remoteCallNo(),frame->subclass(),this);
return;
}
// Append frame to incoming list
@ -119,10 +125,7 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
m_engine(engine),
m_userdata(data),
m_lastFullFrameOut(0),
m_lastMiniFrameOut(0xFFFF),
m_lastMiniFrameIn(0),
m_lastAck(0xFFFF),
m_mutexInMedia(true,"IAXTransaction::InMedia"),
m_pendingEvent(0),
m_currentEvent(0),
m_retransCount(5),
@ -134,14 +137,12 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
m_inDroppedFrames(0),
m_authmethod(IAXAuthMethod::MD5),
m_expire(60),
m_format(0),
m_formatIn(0),
m_formatOut(0),
m_format(IAXFormat::Audio), m_formatVideo(IAXFormat::Video),
m_capability(0), m_callToken(false),
m_trunkFrame(0)
{
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) outgoing. [%p]",
localCallNo(),remoteCallNo(),this);
Debug(m_engine,DebugAll,"Transaction(%u,%u) outgoing type=%u remote=%s:%d [%p]",
localCallNo(),remoteCallNo(),m_type,m_addr.host().c_str(),m_addr.port(),this);
// Init data members
if (!m_addr.port()) {
XDebug(m_engine,DebugAll,
@ -164,7 +165,7 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
ies->appendString(IAXInfoElement::CALLING_NAME,m_callingName);
ies->appendString(IAXInfoElement::CALLED_NUMBER,m_calledNo);
ies->appendString(IAXInfoElement::CALLED_CONTEXT,m_calledContext);
ies->appendNumeric(IAXInfoElement::FORMAT,m_format,4);
ies->appendNumeric(IAXInfoElement::FORMAT,m_format.format() | m_formatVideo.format(),4);
ies->appendNumeric(IAXInfoElement::CAPABILITY,m_capability,4);
if (m_callToken)
ies->appendBinary(IAXInfoElement::CALLTOKEN,0,0);
@ -180,7 +181,7 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
frametype = IAXControl::Poke;
break;
default:
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) outgoing. Unsupported type: %u. [%p]",
Debug(m_engine,DebugStub,"Transaction(%u,%u) outgoing with unsupported type %u [%p]",
localCallNo(),remoteCallNo(),m_type,this);
delete ies;
m_type = Incorrect;
@ -192,13 +193,6 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
IAXTransaction::~IAXTransaction()
{
#ifdef XDEBUG
print();
#endif
if (m_trunkFrame)
m_trunkFrame->deref();
if (state() != Terminating && state() != Terminated)
sendReject("Server shutdown");
XDebug(m_engine,DebugAll,"IAXTransaction::~IAXTransaction(%u,%u). [%p]",
localCallNo(),remoteCallNo(),this);
}
@ -223,6 +217,26 @@ 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)
@ -241,8 +255,16 @@ IAXTransaction* IAXTransaction::processFrame(IAXFrame* frame)
return 0;
}
// Mini frame
if (!frame->fullFrame())
return processMedia(frame->data(),frame->timeStamp());
if (!frame->fullFrame()) {
int t = 0;
if (frame->type() == IAXFrame::Voice)
t = IAXFormat::Audio;
else if (frame->type() == IAXFrame::Video)
t = IAXFormat::Video;
else
return 0;
return processMedia(frame->data(),frame->timeStamp(),t,false,frame->mark());
}
Lock lock(this);
m_inTotalFramesCount++;
// Frame is VNAK ?
@ -250,7 +272,8 @@ IAXTransaction* IAXTransaction::processFrame(IAXFrame* frame)
return retransmitOnVNAK(frame->fullFrame()->iSeqNo());
// Do we have enough space to keep this frame ?
if (m_inFrames.count() == m_maxInFrames) {
Debug(DebugWarn,"Transaction(%u,%u). processFrame. Buffer overrun! (MAX=%u)",localCallNo(),remoteCallNo(),m_maxInFrames);
Debug(m_engine,DebugWarn,"Transaction(%u,%u). processFrame. Buffer overrun! (MAX=%u)",
localCallNo(),remoteCallNo(),m_maxInFrames);
m_inDroppedFrames++;
return 0;
}
@ -258,13 +281,17 @@ IAXTransaction* IAXTransaction::processFrame(IAXFrame* frame)
if (!fAck && !isFrameAcceptable(frame->fullFrame()))
return 0;
incrementSeqNo(frame->fullFrame(),true);
// Voice full frame: process voice data & format
if (frame->type() == IAXFrame::Voice && type() == New) {
if (!processVoiceFrame(frame->fullFrame()))
// Video/Voice full frame: process data & format
if (type() == New && (frame->type() == IAXFrame::Voice ||
frame->type() == IAXFrame::Video)) {
int t = IAXFormat::Audio;
if (frame->type() == IAXFrame::Video)
t = IAXFormat::Video;
if (!processMediaFrame(frame->fullFrame(),t))
return 0;
// Frame accepted: process voice data
lock.drop();
return processMedia(frame->data(),frame->timeStamp(),true);
return processMedia(frame->data(),frame->timeStamp(),t,true,frame->mark());
}
// Process incoming Ping
if (frame->type() == IAXFrame::IAX && frame->fullFrame()->subclass() == IAXControl::Ping) {
@ -282,67 +309,182 @@ IAXTransaction* IAXTransaction::processFrame(IAXFrame* frame)
return this;
}
IAXTransaction* IAXTransaction::processMedia(DataBlock& data, u_int32_t tStamp, bool voice)
IAXTransaction* IAXTransaction::processMedia(DataBlock& data, u_int32_t tStamp, int type,
bool full, bool mark)
{
Lock lock(&m_mutexInMedia);
if (!(voice || (tStamp & 0xffff0000))) {
// Miniframe timestamp
int16_t delta = (int16_t)(tStamp - m_lastMiniFrameIn);
if (delta < 0)
if (state() == Terminated || state() == Terminating)
return 0;
IAXMediaData* d = getData(type);
IAXFormat* fmt = getFormat(type);
if (!(d && fmt)) {
Debug(m_engine,DebugStub,
"IAXTransaction::processMedia() no media data for type '%s'",
IAXFormat::typeName(type));
return 0;
}
Lock lck(d);
if (!fmt->in()) {
if (d->m_showInNoFmt) {
Debug(m_engine,DebugInfo,
"Transaction(%u,%u) received %s data without format [%p]",
localCallNo(),remoteCallNo(),fmt->typeName(),this);
d->m_showInNoFmt = false;
}
return 0;
}
d->m_showInNoFmt = true;
d->m_recv++;
d->m_recvBytes += data.length();
if (!full) {
// Miniframe or video meta frame timestamp
// Voice: timestamp is lowest 16 bits
// Video: timestamp is lowest 15 bits
u_int32_t mask = 0xffff;
if (type == IAXFormat::Video)
mask = 0x7fff;
tStamp &= mask;
// Interval between received timestamp and last one:
// Negative: wraparound if less then half mask
int delta = (int)tStamp - (int)(d->m_lastIn & mask);
if (delta < 0 && ((u_int32_t)-delta) < (mask / 2)) {
d->m_ooPackets++;
d->m_ooBytes += data.length();
DDebug(m_engine,DebugNote,
"Transaction(%u,%u) dropping %u %s mini data mark=%u ts=%u last=%u [%p]",
localCallNo(),remoteCallNo(),data.length(),fmt->typeName(),
mark,tStamp,d->m_lastIn & mask,this);
return 0;
// add upper bits from last frame
tStamp |= m_lastMiniFrameIn & 0xffff0000;
// check if timestamp wrapped around by a miniframe, adjust if so
if ((tStamp & 0xffff) < (m_lastMiniFrameIn & 0xffff)) {
DDebug(m_engine,DebugAll,"Timestamp wraparound, ts=%u last=%u [%p]",tStamp & 0xffff,m_lastMiniFrameIn,this);
tStamp += 0x10000;
}
// Add upper bits from last frame, adjust timestamp if wrapped around
tStamp |= d->m_lastIn & ~mask;
if (delta < 0) {
DDebug(m_engine,DebugInfo,
"Transaction(%u,%u) timestamp wraparound media=%s ts=%u last=%u [%p]",
localCallNo(),remoteCallNo(),fmt->typeName(),
tStamp & mask,d->m_lastIn & mask,this);
tStamp += mask + 1;
}
}
int32_t interval = (int32_t)tStamp - m_lastMiniFrameIn;
if (interval <= 0)
bool forward = false;
if (type != IAXFormat::Video)
forward = (tStamp > d->m_lastIn);
else
forward = (tStamp >= d->m_lastIn);
if (forward) {
d->m_lastIn = tStamp; // New frame is newer then the last one
XDebug(m_engine,DebugAll,
"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);
return 0;
if (voice || interval < 32767) {
m_lastMiniFrameIn = tStamp; // New frame is newer then the last one
m_engine->processMedia(this,data,tStamp);
}
d->m_ooPackets++;
d->m_ooBytes += data.length();
DDebug(m_engine,DebugNote,
"Transaction(%u,%u) dropping %u %s data full=%u mark=%u ts=%u last=%u [%p]",
localCallNo(),remoteCallNo(),data.length(),fmt->typeName(),
full,mark,tStamp,d->m_lastIn,this);
return 0;
}
IAXTransaction* IAXTransaction::sendMedia(const DataBlock& data, u_int32_t format)
unsigned int IAXTransaction::sendMedia(const DataBlock& data, u_int32_t format,
int type, bool mark)
{
if (!data.length())
return 0;
if (state() == Terminated || state() == Terminating)
return 0;
IAXFormat* fmt = getFormat(type);
IAXMediaData* d = getData(type);
if (!(fmt && d)) {
Debug(m_engine,DebugStub,
"IAXTransaction::sendMedia() no media desc for type '%s'",
IAXFormat::typeName(type));
return 0;
}
d->m_sent++;
d->m_sentBytes += data.length();
u_int32_t ts = (u_int32_t)timeStamp();
// Avoid to send the same timestamp twice
if ((u_int16_t)ts == m_lastMiniFrameOut)
// Avoid sending the same timestamp twice for non video
if (type != IAXFormat::Video && d->m_lastOut && ts == d->m_lastOut)
ts++;
// Format changed or timestamp wrapped around? Send Voice full frame
if ((u_int16_t)ts < m_lastMiniFrameOut || m_formatOut != format ) {
if (m_formatOut != format) {
DDebug(m_engine,DebugNote,"Outgoing format changed (New: %u, Old: %u). Send VOICE. [%p]",
format,m_formatOut,this);
m_formatOut = format;
// Format changed or timestamp wrapped around
// Send a full frame
bool fullFrame = (fmt->out() != format) || !d->m_lastOut;
if (!fullFrame) {
// Voice: timestamp is lowest 16 bits
// Video: timestamp is lowest 15 bits
u_int32_t mask = 0xffff;
if (type == IAXFormat::Video)
mask = 0x7fff;
// Timestamp wraparound if mini timestamp is less then last one or
// 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,DebugNote,"Transaction(%u,%u) Time to send VOICE: ts=%u last=%u [%p]",
localCallNo(),remoteCallNo(),ts,m_lastMiniFrameOut,this);
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
m_lastMiniFrameOut = (u_int16_t)ts;
postFrame(IAXFrame::Voice,m_formatOut,data.data(),data.length(),ts,true);
return this;
}
// Send mini frame
m_lastMiniFrameOut = (u_int16_t)ts;
if (m_trunkFrame)
m_trunkFrame->add(localCallNo(),data,m_lastMiniFrameOut);
else {
unsigned char b[4] = {localCallNo() >> 8,localCallNo() & 0xff,m_lastMiniFrameOut >> 8,m_lastMiniFrameOut & 0xff};
DataBlock buf(b,4);
buf += data;
m_engine->writeSocket(buf.data(),buf.length(),remoteAddr());
d->m_lastOut = ts;
unsigned int sent = 0;
if (type == IAXFormat::Audio) {
if (fullFrame) {
// Send trunked frame before full frame to keep the media order
if (m_trunkFrame)
m_engine->sendTrunkFrame(m_trunkFrame);
postFrame(IAXFrame::Voice,fmt->out(),data.data(),data.length(),ts,true);
sent = data.length();
}
else if (m_trunkFrame) {
m_trunkFrame->add(localCallNo(),data,ts);
sent = data.length();
}
else {
DataBlock buf;
IAXFrame::buildMiniFrame(buf,localCallNo(),ts,data.data(),data.length());
m_engine->writeSocket(buf.data(),buf.length(),remoteAddr(),0,&sent);
// Decrease with mini frame header
if (sent > 4)
sent -= 4;
else
sent = 0;
}
}
return this;
else if (type == IAXFormat::Video) {
if (fullFrame) {
postFrame(IAXFrame::Video,fmt->out(),data.data(),data.length(),ts,true,mark);
sent = data.length();
}
else {
DataBlock buf;
IAXFrame::buildVideoMetaFrame(buf,localCallNo(),ts,mark,data.data(),data.length());
m_engine->writeSocket(buf.data(),buf.length(),remoteAddr(),0,&sent);
// Decrease with mini frame header
if (sent > 6)
sent -= 6;
else
sent = 0;
}
}
else
Debug(m_engine,DebugStub,
"IAXTransaction::sendMedia() not implemented for type '%s'",fmt->typeName());
DDebug(m_engine,sent == data.length() ? DebugAll : DebugNote,
"Transaction(%u,%u) sent %u/%u media=%s mark=%u ts=%u [%p]",
localCallNo(),remoteCallNo(),sent,data.length(),
fmt->typeName(),mark,ts,this);
return sent;
}
IAXEvent* IAXTransaction::getEvent(u_int64_t time)
@ -472,7 +614,7 @@ bool IAXTransaction::sendAccept()
return false;
if (type() == New) {
IAXIEList* ies = new IAXIEList;
ies->appendNumeric(IAXInfoElement::FORMAT,m_format,4);
ies->appendNumeric(IAXInfoElement::FORMAT,m_format.format() | m_formatVideo.format(),4);
ies->appendNumeric(IAXInfoElement::CAPABILITY,m_capability,4);
postFrameIes(IAXFrame::IAX,IAXControl::Accept,ies,0,true);
changeState(Connected);
@ -690,28 +832,56 @@ void IAXTransaction::processCallToken(const DataBlock& callToken)
sendFrame(frame);
}
void IAXTransaction::print()
void IAXTransaction::print(bool printStats, bool printFrames, const char* location)
{
static SocketAddr addr;
ObjList* l;
String frames;
frames << "\r\nOutgoing frames: " << m_outFrames.count();
for(l = m_outFrames.skipNull(); l; l = l->skipNext()) {
IAXFrameOut* frame = static_cast<IAXFrameOut*>(l->get());
frame->toString(frames,addr,remoteAddr(),false);
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);
}
frames << "\r\nIncoming frames: " << m_inFrames.count();
for(l = m_inFrames.skipNull(); l; l = l->skipNext()) {
IAXFrameOut* frame = static_cast<IAXFrameOut*>(l->get());
frame->toString(frames,addr,remoteAddr(),true);
String buf;
if (printFrames) {
SocketAddr addr;
ObjList* l;
buf << "\r\nOutgoing frames: " << m_outFrames.count();
for(l = m_outFrames.skipNull(); l; l = l->skipNext()) {
IAXFrameOut* frame = static_cast<IAXFrameOut*>(l->get());
frame->toString(buf,addr,remoteAddr(),false);
}
buf << "\r\nIncoming frames: " << m_inFrames.count();
for(l = m_inFrames.skipNull(); l; l = l->skipNext()) {
IAXFullFrame* frame = static_cast<IAXFullFrame*>(l->get());
frame->toString(buf,addr,remoteAddr(),true);
}
}
Debug(m_engine,DebugInfo,"Transaction(%u,%u). Remote address: %s:%u. Type: %u. State: %u. Timestamp: " FMT64U " [%p]%s",
localCallNo(),remoteCallNo(),remoteAddr().host().c_str(),remoteAddr().port(),
type(),state(),(u_int64_t)timeStamp(),this,frames.c_str());
Debug(m_engine,DebugAll,
"Transaction(%u,%u) %s remote=%s:%u type=%u state=%u timestamp=" FMT64U "%s [%p]%s%s%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-----" : "");
}
// Cleanup
void IAXTransaction::destroyed()
{
#ifndef DEBUG
print(false,false,"destroyed");
#else
print(true,true,"destroyed");
#endif
TelEngine::destruct(m_trunkFrame);
if (state() != Terminating && state() != Terminated)
sendReject("Server shutdown");
RefObject::destroyed();
}
void IAXTransaction::init(IAXIEList& ieList)
{
u_int32_t fmt = 0;
switch (type()) {
case New:
ieList.getString(IAXInfoElement::USERNAME,m_username);
@ -719,14 +889,14 @@ void IAXTransaction::init(IAXIEList& ieList)
ieList.getString(IAXInfoElement::CALLING_NAME,m_callingName);
ieList.getString(IAXInfoElement::CALLED_NUMBER,m_calledNo);
ieList.getString(IAXInfoElement::CALLED_CONTEXT,m_calledContext);
ieList.getNumeric(IAXInfoElement::FORMAT,m_format);
ieList.getNumeric(IAXInfoElement::FORMAT,fmt);
ieList.getNumeric(IAXInfoElement::CAPABILITY,m_capability);
if (outgoing()) {
m_formatOut = m_format;
m_capability &= m_engine->capability();
fmt &= m_capability;
m_format.set(&fmt,&fmt,&fmt);
m_formatVideo.set(&fmt,&fmt,&fmt);
if (outgoing())
m_callToken = (0 != ieList.getIE(IAXInfoElement::CALLTOKEN));
}
else
m_formatIn = m_format;
break;
case RegReq:
ieList.getString(IAXInfoElement::CALLED_NUMBER,m_calledNo);
@ -826,14 +996,15 @@ IAXEvent* IAXTransaction::waitForTerminate(u_int8_t evType, bool local, IAXFullF
return ev;
}
void IAXTransaction::postFrame(IAXFrame::Type type, u_int32_t subclass, void* data, u_int16_t len, u_int32_t tStamp, bool ackOnly)
void IAXTransaction::postFrame(IAXFrame::Type type, u_int32_t subclass, void* data,
u_int16_t len, u_int32_t tStamp, bool ackOnly, bool mark)
{
Lock lock(this);
if (state() == Terminated)
return;
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);
(unsigned char*)data,len,m_retransCount,m_retransInterval,ackOnly,mark);
postFrame(frame);
}
@ -1007,12 +1178,13 @@ IAXEvent* IAXTransaction::processAccept(IAXEvent* event)
if (event->type() != IAXEvent::Accept)
return event;
Debug(m_engine,DebugAll,"Transaction(%u,%u). Accept received",localCallNo(),remoteCallNo());
// We might have a format received with a Voice frame
if (m_formatIn)
return event;
m_format = 0;
event->getList().getNumeric(IAXInfoElement::FORMAT,m_format);
if (m_engine->acceptFormatAndCapability(this))
u_int32_t fmt = 0;
event->getList().getNumeric(IAXInfoElement::FORMAT,fmt);
m_format.set(&fmt,0,0);
m_formatVideo.set(&fmt,0,0);
m_engine->acceptFormatAndCapability(this,0,IAXFormat::Audio);
m_engine->acceptFormatAndCapability(this,0,IAXFormat::Video);
if (m_format.format() || m_formatVideo.format())
return event;
delete event;
return internalReject(s_iax_modNoMediaFormat);
@ -1452,33 +1624,60 @@ IAXEvent* IAXTransaction::getEventTerminating(u_int64_t time)
return 0;
}
IAXTransaction* IAXTransaction::processVoiceFrame(const IAXFullFrame* frame)
IAXTransaction* IAXTransaction::processMediaFrame(const IAXFullFrame* frame, int type)
{
// Process format
DDebug(m_engine,DebugAll,"Transaction(%u,%u). Received Voice Frame(%u,%u) iseq=%u oseq=%u stamp=%u [%p]",
localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),
DDebug(m_engine,DebugAll,
"Transaction(%u,%u). Received %s (%u,%u) iseq=%u oseq=%u stamp=%u [%p]",
localCallNo(),remoteCallNo(),IAXFrame::typeText(frame->type()),
frame->type(),frame->subclass(),
frame->iSeqNo(),frame->oSeqNo(),frame->timeStamp(),this);
sendAck(frame);
IAXFormat* fmt = getFormat(type);
if (!fmt)
return this;
if (!frame->subclass())
return this;
// Check the format
u_int32_t recvFmt = IAXFormat::mask(frame->subclass(),type);
if (recvFmt == fmt->in())
return this;
if (!recvFmt) {
String tmp;
IAXFormat::formatList(tmp,frame->subclass());
Debug(m_engine,DebugInfo,
"IAXTransaction(%u,%u). Received %s frame with invalid format=%s (0x%x) [%p]",
localCallNo(),remoteCallNo(),IAXFrame::typeText(frame->type()),
tmp.c_str(),frame->subclass(),this);
return this;
}
if (!IAXFormat::formatName(recvFmt)) {
Debug(m_engine,DebugNote,
"IAXTransaction(%u,%u). Received %s frame with unknown format=0x%x [%p]",
localCallNo(),remoteCallNo(),IAXFrame::typeText(frame->type()),recvFmt,this);
m_pendingEvent = internalReject(s_iax_modNoMediaFormat);
return 0;
}
// We might have an incoming media format received with an Accept frame
if (m_formatIn) {
if (frame->subclass() && frame->subclass() != m_formatIn) {
// Format changed.
if (m_engine->voiceFormatChanged(this,frame->subclass()))
m_formatIn = frame->subclass();
else {
DDebug(m_engine,DebugAll,"IAXTransaction(%u,%u). Process Voice Frame. Media format (%u) change rejected!",
localCallNo(),remoteCallNo(),m_format);
m_pendingEvent = internalReject(s_iax_modNoMediaFormat);
return 0;
}
if (fmt->in()) {
// Format changed.
if (m_engine->mediaFormatChanged(this,type,recvFmt)) {
Debug(m_engine,DebugNote,
"Transaction(%u,%u). Incoming %s format changed %u --> %u [%p]",
localCallNo(),remoteCallNo(),fmt->typeName(),fmt->in(),recvFmt,this);
fmt->set(0,&recvFmt,0);
}
else {
DDebug(m_engine,DebugAll,
"IAXTransaction(%u,%u). Format change rejected media=%s current=%u [%p]",
localCallNo(),remoteCallNo(),fmt->typeName(),fmt->format(),this);
m_pendingEvent = internalReject(s_iax_modNoMediaFormat);
return 0;
}
}
else {
m_format = frame->subclass();
if (!m_engine->acceptFormatAndCapability(this)) {
m_pendingEvent = internalReject(s_iax_modNoMediaFormat);
fmt->set(&recvFmt,0,0);
if (!m_engine->acceptFormatAndCapability(this,0,type))
return 0;
}
}
return this;
}

View File

@ -51,6 +51,7 @@ class IAXInfoElement;
class IAXInfoElementString;
class IAXInfoElementNumeric;
class IAXInfoElementBinary;
class IAXFrame;
class IAXFullFrame;
class IAXFrameOut;
class IAXEvent;
@ -548,65 +549,193 @@ class YIAX_API IAXFormat
{
public:
/**
* Audio format enumeration types
* Format enumeration types
*/
enum Audio {
enum Formats {
G723_1 = (1 << 0),
GSM = (1 << 1),
ULAW = (1 << 2),
ALAW = (1 << 3),
MP3 = (1 << 4),
G726 = (1 << 4),
ADPCM = (1 << 5),
SLIN = (1 << 6),
LPC10 = (1 << 7),
G729A = (1 << 8),
G729 = (1 << 8),
SPEEX = (1 << 9),
ILBC = (1 << 10),
G726AAL2 = (1 << 11),
G722 = (1 << 12),
AMR = (1 << 13),
AudioMask = G723_1 | GSM | ULAW | ALAW | G726 | ADPCM | SLIN | LPC10 | G729 | SPEEX |
ILBC | G726AAL2 | G722 | AMR,
JPEG = (1 << 16),
PNG = (1 << 17),
ImageMask = JPEG | PNG,
H261 = (1 << 18),
H263 = (1 << 19),
H263p = (1 << 20),
H264 = (1 << 21),
VideoMask = H261 | H263 | H263p | H264,
};
/**
* Video format enumeration types
* Media type enumeration
*/
enum Video {
JPEG = (1 << 16),
PNG = (1 << 17),
H261 = (1 << 18),
H263 = (1 << 19),
enum Media {
Audio = 0,
Video,
Image,
TypeCount
};
/**
* Constructor. Build an audio format
* @param type Media type
*/
inline IAXFormat(int type = Audio)
: m_type(type), m_format(0), m_formatIn(0), m_formatOut(0)
{}
/**
* Get the media type
* @return Media type
*/
inline int type() const
{ return m_type; }
/**
* Get the format
* @return The format
*/
inline u_int32_t format() const
{ return m_format; }
/**
* Get the incoming format
* @return The incoming format
*/
inline u_int32_t in() const
{ return m_formatIn; }
/**
* Get the outgoing format
* @return The outgoing format
*/
inline u_int32_t out() const
{ return m_formatOut; }
/**
* Get the text associated with the format
* @return Format name
*/
inline const char* formatName() const
{ return formatName(m_format); }
/**
* Get the text associated with the media type
* @return Media name
*/
inline const char* typeName() const
{ return typeName(m_type); }
/**
* Set format
* @param fmt Optional pointer to format to set
* @param fmtIn Optional pointer to incoming format to set
* @param fmtOut Optional pointer to outgoing format to set
*/
void set(u_int32_t* fmt, u_int32_t* fmtIn, u_int32_t* fmtOut);
/**
* Create a string list from formats
* @param dest The destination
* @param formats The formats
* @param dict Optional dictionary to use, 0 to use s_formats
* @param sep The separator to use
*/
static void formatList(String& dest, u_int32_t formats, char sep = ',');
static void formatList(String& dest, u_int32_t formats, const TokenDict* dict = 0,
const char* sep = ",");
/**
* Get the text associated with an audio format
* @param audio The desired format
* Pick a format from a list of capabilities
* @param formats Capabilities list
* @param format Optional format to pick
* @return IAX format, 0 if not found
*/
static u_int32_t pickFormat(u_int32_t formats, u_int32_t format = 0);
/**
* Encode a formats list
* @param formats Formats list
* @param dict Dictionary to use
* @param sep Formats list separator
* @return Encoded formats
*/
static u_int32_t encode(const String& formats, const TokenDict* dict, char sep = ',');
/**
* Mask formats by type
* @param value Input format(s)
* @param type Media type to retrieve
* @return Media format(s) from input
*/
static inline u_int32_t mask(u_int32_t value, int type) {
if (type == Audio)
return value & AudioMask;
if (type == Video)
return value & VideoMask;
if (type == Image)
return value & ImageMask;
return 0;
}
/**
* Clear formats by type
* @param value Input format(s)
* @param type Media type to clear
* @return Cleared format(s) from input
*/
static inline u_int32_t clear(u_int32_t value, int type) {
if (type == Audio)
return value & ~AudioMask;
if (type == Video)
return value & ~VideoMask;
if (type == Image)
return value & ~ImageMask;
return value;
}
/**
* Get the text associated with a format
* @param fmt The desired format
* @return A pointer to the text associated with the format or 0 if the format doesn't exist
*/
static inline const char* audioText(u_int32_t audio)
{ return lookup(audio,audioData); }
static inline const char* formatName(u_int32_t fmt)
{ return lookup(fmt,s_formats); }
/**
* Get the text associated with a video format
* @param video The desired format
* @return A pointer to the text associated with the format or 0 if the format doesn't exist
* Get the text associated with a media type
* @param type The media type
* @return A pointer to the text associated with the media type
*/
static inline const char* videoText(u_int32_t video)
{ return lookup(video,videoData); }
static inline const char* typeName(int type)
{ return lookup(type,s_types); }
/**
* Keep the texts associated with the audio formats
* Keep the texts associated with the formats
*/
static TokenDict audioData[];
static const TokenDict s_formats[];
/**
* Keep the texts associated with the video formats
*/
static TokenDict videoData[];
* Keep the texts associated with type
*/
static const TokenDict s_types[];
protected:
int m_type;
u_int32_t m_format;
u_int32_t m_formatIn;
u_int32_t m_formatOut;
};
/**
@ -703,9 +832,10 @@ public:
* @param retrans Retransmission flag
* @param buf IE buffer
* @param len IE buffer length
* @param mark Mark flag
*/
IAXFrame(Type type, u_int16_t sCallNo, u_int32_t tStamp, bool retrans,
const unsigned char* buf, unsigned int len);
const unsigned char* buf, unsigned int len, bool mark = false);
/**
* Destructor
@ -747,6 +877,13 @@ public:
inline u_int32_t timeStamp() const
{ return m_tStamp; }
/**
* Get the mark flag
* @return The mark flag
*/
inline bool mark() const
{ return m_mark; }
/**
* Get a pointer to this frame if it is a full frame
* @return A pointer to this frame if it is a full frame or 0
@ -763,6 +900,29 @@ public:
*/
static IAXFrame* parse(const unsigned char* buf, unsigned int len, IAXEngine* engine = 0, const SocketAddr* addr = 0);
/**
* Build a miniframe buffer
* @param dest Destination buffer
* @param sCallNo Source call number
* @param tStamp Frame timestamp
* @param data Data
* @param len Data length
*/
static void buildMiniFrame(DataBlock& dest, u_int16_t sCallNo, u_int32_t tStamp,
void* data, unsigned int len);
/**
* Build a video meta frame buffer
* @param dest Destination buffer
* @param sCallNo Source call number
* @param tStamp Frame timestamp
* @param mark Frame mark
* @param data Data
* @param len Data length
*/
static void buildVideoMetaFrame(DataBlock& dest, u_int16_t sCallNo, u_int32_t tStamp,
bool mark, void* data, unsigned int len);
/**
* Pack a subclass value according to IAX protocol
* @param value Value to pack
@ -801,6 +961,7 @@ private:
Type m_type; // Frame type
u_int16_t m_sCallNo; // Source call number
u_int32_t m_tStamp; // Frame timestamp
bool m_mark; // Mark flag
};
/**
@ -843,11 +1004,12 @@ public:
* @param retrans Retransmission flag
* @param buf IE buffer
* @param len IE buffer length
* @param mark Mark flag
*/
IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo,
u_int32_t tStamp, bool retrans,
const unsigned char* buf, unsigned int len);
const unsigned char* buf, unsigned int len, bool mark = false);
/**
* Constructor. Constructs an outgoing full frame
@ -860,11 +1022,12 @@ public:
* @param tStamp Frame timestamp
* @param buf IE buffer
* @param len IE buffer length
* @param mark Mark flag
*/
IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo,
u_int32_t tStamp,
const unsigned char* buf = 0, unsigned int len = 0);
const unsigned char* buf = 0, unsigned int len = 0, bool mark = false);
/**
* Constructor. Constructs an outgoing full frame
@ -877,10 +1040,11 @@ public:
* @param tStamp Frame timestamp
* @param ieList List of frame IEs
* @param maxlen Max frame data length
* @param mark Mark flag
*/
IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo,
u_int32_t tStamp, IAXIEList* ieList, u_int16_t maxlen);
u_int32_t tStamp, IAXIEList* ieList, u_int16_t maxlen, bool mark = false);
/**
* Destructor
@ -1004,11 +1168,12 @@ public:
* @param retransCount Retransmission counter
* @param retransInterval Time interval to the next retransmission
* @param ackOnly Acknoledge only flag. If true, the frame only expects an ACK
* @param mark Mark flag
*/
inline IAXFrameOut(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo, u_int32_t tStamp, const unsigned char* buf, unsigned int len,
u_int16_t retransCount, u_int32_t retransInterval, bool ackOnly)
: IAXFullFrame(type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,buf,len),
u_int16_t retransCount, u_int32_t retransInterval, bool ackOnly, bool mark = false)
: IAXFullFrame(type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,buf,len,mark),
m_ack(false), m_ackOnly(ackOnly), m_retransCount(retransCount), m_retransTimeInterval(retransInterval),
m_nextTransTime(Time::msecNow() + m_retransTimeInterval)
{}
@ -1027,12 +1192,13 @@ public:
* @param retransCount Retransmission counter
* @param retransInterval Time interval to the next retransmission
* @param ackOnly Acknoledge only flag. If true, the frame only expects an ACK
* @param mark Mark flag
*/
inline IAXFrameOut(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo, u_int32_t tStamp,
IAXIEList* ieList, u_int16_t maxlen,
u_int16_t retransCount, u_int32_t retransInterval, bool ackOnly)
: IAXFullFrame(type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,ieList,maxlen),
u_int16_t retransCount, u_int32_t retransInterval, bool ackOnly, bool mark = false)
: IAXFullFrame(type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,ieList,maxlen,mark),
m_ack(false), m_ackOnly(ackOnly), m_retransCount(retransCount), m_retransTimeInterval(retransInterval),
m_nextTransTime(Time::msecNow() + m_retransTimeInterval)
{}
@ -1155,7 +1321,7 @@ public:
* @param tStamp Frame timestamp
* @return The result of the write operation
*/
bool send(u_int32_t tStamp);
bool send(u_int32_t tStamp = Time::msecNow());
private:
u_int8_t* m_data; // Data buffer
@ -1165,6 +1331,43 @@ private:
SocketAddr m_addr; // Remote peer address
};
/**
* This class holds data used by transaction to sync media.
* The mutex is not reentrant
* @short IAX2 transaction media data
*/
class YIAX_API IAXMediaData : public Mutex
{
friend class IAXTransaction;
public:
/**
* Constructor
*/
inline IAXMediaData()
: Mutex(true,"IAXTransaction::InMedia"),
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)
{}
/**
* Print statistics
* @param buf Destination buffer
*/
void print(String& buf);
protected:
u_int32_t m_lastOut; // Last transmitted mini timestamp
u_int32_t m_lastIn; // Last received timestamp
unsigned int m_sent; // Packets sent
unsigned int m_sentBytes; // Bytes sent
unsigned int m_recv; // Packets received
unsigned int m_recvBytes; // Bytes received
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
};
/**
* This class holds all the data needded for the management of an IAX2 transaction
* which might be a call leg, a register/unregister or a poke one
@ -1345,25 +1548,48 @@ public:
{ return m_challenge; }
/**
* Retrieve the media format used during initialization
* @return The initial media format
* Retrieve the media of a given type
* @param type Media type to retrieve
* @return IAXFormat pointer or 0 for invalid type
*/
inline u_int32_t format()
{ return m_format; }
IAXFormat* getFormat(int type);
/**
* 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);
/**
* Retrieve the media format used during initialization
* @param type Media type to retrieve
* @return The initial media format for the given type
*/
inline u_int32_t format(int type) {
IAXFormat* fmt = getFormat(type);
return fmt ? fmt->format() : 0;
}
/**
* Retrieve the incoming media format
* @return The incoming media format
* @param type Media type to retrieve
* @return The incoming media format for the given type
*/
inline u_int32_t formatIn()
{ return m_formatIn; }
inline u_int32_t formatIn(int type) {
IAXFormat* fmt = getFormat(type);
return fmt ? fmt->in() : 0;
}
/**
* Retrieve the outgoing media format
* @return The outgoing media format
* @param type Media type to retrieve
* @return The outgoing media format for the given type
*/
inline u_int32_t formatOut() const
{ return m_formatOut; }
inline u_int32_t formatOut(int type) {
IAXFormat* fmt = getFormat(type);
return fmt ? fmt->out() : 0;
}
/**
* Retrieve the media capability of this transaction
@ -1395,21 +1621,27 @@ public:
IAXTransaction* processFrame(IAXFrame* frame);
/**
* Process received mini frame data
* Process received media data
* @param data Received data
* @param tStamp Mini frame timestamp
* @param voice True if received mini frame inside a Voice full frame
* @param type Media type
* @param full True if received in a full frame
* @param mark Mark flag
* @return 0
*/
IAXTransaction* processMedia(DataBlock& data, u_int32_t tStamp, bool voice = false);
IAXTransaction* processMedia(DataBlock& data, u_int32_t tStamp,
int type = IAXFormat::Audio, bool full = false, bool mark = false);
/**
* Send media data to remote peer. Update the outgoing media format if changed
* @param data Data to send
* @param format Data format
* @return 'this' if successful or 0
* @param type Media type
* @param mark Mark flag
* @return The number of bytes sent
*/
IAXTransaction* sendMedia(const DataBlock& data, u_int32_t format);
unsigned int sendMedia(const DataBlock& data, u_int32_t format,
int type = IAXFormat::Audio, bool mark = false);
/**
* Get an IAX event from the queue
@ -1547,8 +1779,11 @@ public:
/**
* Print transaction data on stdin
* @param printStats True to print media statistics
* @param printFrames True to print in/out pending frames
* @param location Additional location info to be shown in debug
*/
void print();
void print(bool printStats = false, bool printFrames = false, const char* location = "status");
/**
* Standard message sent if unsupported/unknown/none authentication methosd was received
@ -1595,6 +1830,11 @@ protected:
IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno, const SocketAddr& addr, IAXIEList& ieList,
void* data = 0);
/**
* Cleanup
*/
virtual void destroyed();
/**
* Init data members from an IE list
* @param ieList IE list to init from
@ -1651,9 +1891,10 @@ protected:
* @param len Frame IE list length
* @param tStamp Frame timestamp. If 0 the transaction timestamp will be used
* @param ackOnly Frame's acknoledge only flag
* @param mark Frame mark flag
*/
void postFrame(IAXFrame::Type type, u_int32_t subclass, void* data = 0, u_int16_t len = 0, u_int32_t tStamp = 0,
bool ackOnly = false);
bool ackOnly = false, bool mark = false);
/**
* Constructs an IAXFrameOut frame, send it to remote peer and put it in the transmission list
@ -1883,11 +2124,12 @@ protected:
IAXEvent* getEventTerminating(u_int64_t time);
/**
* Process received Voice frames
* @param frame Received voice frame
* Process received media full frames
* @param frame Received frame
* @param type Media type
* @return 0
*/
IAXTransaction* processVoiceFrame(const IAXFullFrame* frame);
IAXTransaction* processMediaFrame(const IAXFullFrame* frame, int type);
/**
* Send all frames from outgoing queue with outbound sequence number starting with seqNo.
@ -1945,10 +2187,9 @@ private:
IAXEngine* m_engine; // Engine that owns this transaction
void* m_userdata; // Arbitrary user data
u_int32_t m_lastFullFrameOut; // Last transmitted full frame timestamp
u_int16_t m_lastMiniFrameOut; // Last transmitted mini frame timestamp
u_int32_t m_lastMiniFrameIn; // Last received mini frame timestamp
IAXMediaData m_dataAudio;
IAXMediaData m_dataVideo;
u_int16_t m_lastAck; // Last ack'd received frame's oseqno
Mutex m_mutexInMedia; // Keep received media thread safe
IAXEvent* m_pendingEvent; // Pointer to a pending event or 0
IAXEvent* m_currentEvent; // Pointer to last generated event or 0
// Outgoing frames management
@ -1975,9 +2216,8 @@ private:
String m_challenge; // Challenge
String m_authdata; // Auth data received with auth reply
u_int32_t m_expire; // Registration expiring time
u_int32_t m_format; // Media format used for initial negotiation
u_int32_t m_formatIn; // Incoming media format
u_int32_t m_formatOut; // Outgoing media format
IAXFormat m_format; // Audio format
IAXFormat m_formatVideo; // Video format
u_int32_t m_capability; // Media capability of this transaction
bool m_callToken; // Call token supported/expected
// Meta trunking
@ -2184,8 +2424,11 @@ public:
* @param transaction IAXTransaction that owns the call leg
* @param data Media data
* @param tStamp Media timestamp
* @param type Media type
* @param mark Mark flag
*/
virtual void processMedia(IAXTransaction* transaction, DataBlock& data, u_int32_t tStamp)
virtual void processMedia(IAXTransaction* transaction, DataBlock& data, u_int32_t tStamp,
int type, bool mark)
{}
/**
@ -2239,10 +2482,11 @@ public:
/**
* Get the default media format
* @param audio True to retrieve default audio format, false for video format
* @return The default media format
*/
inline u_int32_t format() const
{ return m_format; }
inline u_int32_t format(bool audio = true) const
{ return audio ? m_format : m_formatVideo; }
/**
* Get the media capability of this engine
@ -2269,9 +2513,11 @@ public:
* @param len Data length
* @param addr Socket to write to
* @param frame Optional frame to be printed
* @param sent Pointer to variable to be filled with the number of bytes sent
* @return True on success
*/
bool writeSocket(const void* buf, int len, const SocketAddr& addr, IAXFullFrame* frame = 0);
bool writeSocket(const void* buf, int len, const SocketAddr& addr, IAXFullFrame* frame = 0,
unsigned int* sent = 0);
/**
* Write a full frame to socket
@ -2308,12 +2554,13 @@ public:
void keepAlive(SocketAddr& addr);
/**
* Process a new format received with a Voice frame
* Process a new format received with a full frame
* @param trans Transaction that received the new format
* @param type Media type
* @param format The received format
* @return True if accepted
*/
virtual bool voiceFormatChanged(IAXTransaction* trans, u_int32_t format)
virtual bool mediaFormatChanged(IAXTransaction* trans, int type, u_int32_t format)
{ return false; }
/**
@ -2326,12 +2573,15 @@ public:
virtual bool checkCallToken(const SocketAddr& addr, IAXFullFrame& frame);
/**
* Process the initial received format and capability. If accepted on exit will set the transaction format and capability
* Process the initial received format and capability.
* If accepted on exit will set the transaction format and capability
* @param trans Transaction that received the new format
* @param caps Optional codecs to set in transaction before processing
* @param type Media type
* @return True if accepted
*/
bool acceptFormatAndCapability(IAXTransaction* trans, unsigned int* caps = 0);
bool acceptFormatAndCapability(IAXTransaction* trans, unsigned int* caps = 0,
int type = IAXFormat::Audio);
/**
* Default event handler. event MUST NOT be deleted
@ -2351,6 +2601,17 @@ public:
*/
void removeTrunkFrame(IAXMetaTrunkFrame* metaFrame);
/**
* 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
*/
@ -2363,6 +2624,18 @@ public:
inline Socket& socket()
{ return m_socket; }
/**
* Send engine formats
* @param caps Capabilities
* @param fmtAudio Default audio format
* @param fmtVideo Default video format
*/
inline void setFormats(u_int32_t caps, u_int32_t fmtAudio, u_int32_t fmtVideo) {
m_format = fmtAudio;
m_formatVideo = fmtVideo;
m_capability = caps;
}
/**
* Get the MD5 data from a challenge and a password
* @param md5data Destination String
@ -2468,6 +2741,7 @@ private:
bool m_printMsg; // Print frame to output
// Media
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
// Trunking
Mutex m_mutexTrunk; // Mutex for trunk operations

File diff suppressed because it is too large Load Diff