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:
parent
22a7a05cc9
commit
04b7b695a1
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue