Fixed media checking for incoming contents. Fixed ilbc codec negotiation. Added speex to codecs list.
git-svn-id: http://voip.null.ro/svn/yate@4240 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
8c8952cace
commit
677666e486
|
@ -130,6 +130,9 @@ alaw=true
|
|||
; gsm: bool: European GSM 06.10 (GSM/8000)
|
||||
gsm=default
|
||||
|
||||
; ilbc: bool: Internet Low Bandwidth Codec (iLBC/8000)
|
||||
;ilbc=default
|
||||
|
||||
; lpc10: bool: Linear Prediction Codec (LPC/8000)
|
||||
lpc10=default
|
||||
|
||||
|
@ -147,3 +150,16 @@ g728=default
|
|||
|
||||
; g729: bool: ITU G.729 all variations (G729/8000)
|
||||
g729=default
|
||||
|
||||
|
||||
[hacks]
|
||||
; This section holds the dirty stuff required to work with some broken
|
||||
; or old implementations
|
||||
|
||||
; ilbc_forced: string: Format to offer as iLBC, can be: ilbc20 or ilbc30
|
||||
; Defaults to ilbc30
|
||||
;ilbc_forced=ilbc30
|
||||
|
||||
; ilbc_default: string: Format to use for iLBC when packetization is unknown
|
||||
; Defaults to ilbc30
|
||||
;ilbc_default=ilbc30
|
||||
|
|
|
@ -287,6 +287,8 @@ private:
|
|||
void overrideJingleVersion(const NamedList& list, bool caps);
|
||||
// Copy chan/session parameters to a destination list
|
||||
void copySessionParams(NamedList& list, bool redirect = true);
|
||||
// Check media for a received content
|
||||
bool checkMedia(const JGEvent& event, JGSessionContent& c);
|
||||
|
||||
Mutex m_mutex; // Lock transport and session
|
||||
State m_state; // Connection state
|
||||
|
@ -298,8 +300,8 @@ private:
|
|||
JabberID m_remote; // Remote user's JID
|
||||
ObjList m_audioContents; // The list of negotiated audio contents
|
||||
JGSessionContent* m_audioContent; // The current audio content
|
||||
JGRtpMediaList m_audioFormats; // Audio formats used by this channel
|
||||
String m_callerPrompt; // Text to be sent to called before calling it
|
||||
String m_formats; // Formats received in call.execute
|
||||
String m_subject; // Connection subject
|
||||
String m_line; // Connection line
|
||||
String m_localip; // Local address
|
||||
|
@ -526,6 +528,7 @@ static String s_capsNode = "http://yate.null.ro/yate/jingle/caps"; // node for e
|
|||
static bool s_serverMode = true; // Server/client mode
|
||||
static YJGEngine* s_jingle = 0;
|
||||
static YJGDriver plugin; // The driver
|
||||
static bool s_ilbcDefault30 = true; // Default ilbc format when ptime is unknown (30 or 20)
|
||||
|
||||
// Message handlers installed by the module
|
||||
static const TokenDict s_msgHandler[] = {
|
||||
|
@ -608,20 +611,6 @@ static inline void jingleAddParam(NamedList& list, const char* param, const char
|
|||
copy->append(param,",");
|
||||
}
|
||||
|
||||
// Add formats to a list of jingle payloads
|
||||
static void setMedia(JGRtpMediaList& dest, const String& formats,
|
||||
const JGRtpMediaList& src)
|
||||
{
|
||||
ObjList* f = formats.split(',');
|
||||
for (ObjList* o = f->skipNull(); o; o = o->skipNext()) {
|
||||
String* format = static_cast<String*>(o->get());
|
||||
JGRtpMedia* a = src.findSynonym(*format);
|
||||
if (a)
|
||||
dest.append(new JGRtpMedia(*a));
|
||||
}
|
||||
TelEngine::destruct(f);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* YJGEngine
|
||||
|
@ -738,6 +727,7 @@ YJGConnection::YJGConnection(Message& msg, const char* caller, const char* calle
|
|||
m_state(Pending), m_session(0), m_rtpStarted(false), m_acceptRelay(s_acceptRelay),
|
||||
m_sessVersion(s_sessVersion),
|
||||
m_local(caller), m_remote(called), m_audioContent(0),
|
||||
m_audioFormats(JGRtpMediaList::Audio),
|
||||
m_callerPrompt(msg.getValue("callerprompt")),
|
||||
m_localip(localip),
|
||||
m_offerRawTransport(true), m_offerIceTransport(true),
|
||||
|
@ -771,9 +761,12 @@ YJGConnection::YJGConnection(Message& msg, const char* caller, const char* calle
|
|||
}
|
||||
// Get formats. Check if this is a file transfer session
|
||||
if (null(file)) {
|
||||
m_formats = msg.getValue("formats");
|
||||
if (!m_formats)
|
||||
s_usedCodecs.createList(m_formats,true);
|
||||
String audio = msg["formats"];
|
||||
plugin.lock();
|
||||
if (!(audio || s_usedCodecs.createList(audio,true)))
|
||||
audio = "alaw,mulaw";
|
||||
m_audioFormats.setMedia(s_usedCodecs,audio);
|
||||
plugin.unlock();
|
||||
}
|
||||
else {
|
||||
m_ftStatus = FTIdle;
|
||||
|
@ -835,6 +828,7 @@ YJGConnection::YJGConnection(JGEvent* event)
|
|||
m_sessVersion(event->session()->version()),
|
||||
m_local(event->session()->local()), m_remote(event->session()->remote()),
|
||||
m_audioContent(0),
|
||||
m_audioFormats(JGRtpMediaList::Audio),
|
||||
m_offerRawTransport(true), m_offerIceTransport(true),
|
||||
m_redirectCount(0), m_dtmfMeth(s_dtmfMeth),
|
||||
m_secure(s_useCrypto), m_secureRequired(s_cryptoMandatory),
|
||||
|
@ -843,6 +837,9 @@ YJGConnection::YJGConnection(JGEvent* event)
|
|||
m_connSocksServer(false)
|
||||
{
|
||||
m_line = m_session->line();
|
||||
plugin.lock();
|
||||
m_audioFormats.setMedia(s_usedCodecs);
|
||||
plugin.unlock();
|
||||
// Update local ip in non server mode
|
||||
if (!s_serverMode && m_line) {
|
||||
Message* m = plugin.checkAccount(m_line);
|
||||
|
@ -1001,6 +998,11 @@ bool YJGConnection::route()
|
|||
m->addParam("file_time",String(time));
|
||||
}
|
||||
}
|
||||
else {
|
||||
String formats;
|
||||
m_audioFormats.createList(formats,true);
|
||||
m->addParam("formats",formats,false);
|
||||
}
|
||||
m_mutex.unlock();
|
||||
return startRouter(m);
|
||||
}
|
||||
|
@ -2078,15 +2080,6 @@ void YJGConnection::addContent(bool local, JGSessionContent* c)
|
|||
else
|
||||
c->m_rtpLocalCandidates.generateOldIceAuth();
|
||||
}
|
||||
// Fill synonym for received media
|
||||
if (!local) {
|
||||
for (ObjList* o = c->m_rtpMedia.skipNull(); o; o = o->skipNext()) {
|
||||
JGRtpMedia* m = static_cast<JGRtpMedia*>(o->get());
|
||||
JGRtpMedia* tmp = s_knownCodecs.findMedia(m->toString());
|
||||
if (tmp)
|
||||
m->m_synonym = tmp->m_synonym;
|
||||
}
|
||||
}
|
||||
Debug(this,DebugAll,"Added content='%s' type=%s initiator=%s [%p]",
|
||||
c->toString().c_str(),c->m_rtpLocalCandidates.typeName(),
|
||||
String::boolText(c->creator() == JGSessionContent::CreatorInitiator),this);
|
||||
|
@ -2131,12 +2124,8 @@ void YJGConnection::removeCurrentAudioContent(bool removeReq)
|
|||
if (m_audioContent->isEarlyMedia())
|
||||
c->setEarlyMedia();
|
||||
// Copy media
|
||||
c->m_rtpMedia.m_media = m_audioContent->m_rtpMedia.m_media;
|
||||
c->m_rtpMedia.m_cryptoRequired = m_audioContent->m_rtpMedia.m_cryptoRequired;
|
||||
for (ObjList* o = m_audioContent->m_rtpMedia.skipNull(); o; o = o->skipNext()) {
|
||||
JGRtpMedia* m = static_cast<JGRtpMedia*>(o->get());
|
||||
c->m_rtpMedia.append(new JGRtpMedia(*m));
|
||||
}
|
||||
c->m_rtpMedia.setMedia(m_audioContent->m_rtpMedia);
|
||||
// Append
|
||||
addContent(true,c);
|
||||
if (m_session)
|
||||
|
@ -2234,8 +2223,11 @@ bool YJGConnection::startRtp()
|
|||
m.addParam("media","audio");
|
||||
m.addParam("getsession","true");
|
||||
ObjList* obj = m_audioContent->m_rtpMedia.skipNull();
|
||||
if (obj)
|
||||
m.addParam("format",(static_cast<JGRtpMedia*>(obj->get()))->m_synonym);
|
||||
if (obj) {
|
||||
JGRtpMedia* media = static_cast<JGRtpMedia*>(obj->get());
|
||||
m.addParam("payload",media->m_id);
|
||||
m.addParam("format",media->m_synonym);
|
||||
}
|
||||
m.addParam("localip",rtpLocal->m_address);
|
||||
m.addParam("localport",rtpLocal->m_port);
|
||||
m.addParam("remoteip",rtpRemote->m_address);
|
||||
|
@ -2411,34 +2403,7 @@ bool YJGConnection::processContentAdd(const JGEvent& event, ObjList& ok, ObjList
|
|||
}
|
||||
|
||||
// Check media
|
||||
// Fill a string with our capabilities for debug purposes
|
||||
String remoteCaps;
|
||||
if (debugAt(DebugInfo))
|
||||
c->m_rtpMedia.createList(remoteCaps,false);
|
||||
// Check received media against the used codecs list
|
||||
// Compare 'id' and 'name'
|
||||
ListIterator iter(c->m_rtpMedia);
|
||||
for (GenObject* go; (go = iter.get());) {
|
||||
JGRtpMedia* recv = static_cast<JGRtpMedia*>(go);
|
||||
ObjList* used = s_usedCodecs.skipNull();
|
||||
for (; used; used = used->skipNext()) {
|
||||
JGRtpMedia* local = static_cast<JGRtpMedia*>(used->get());
|
||||
if (local->m_id == recv->m_id && local->m_name == recv->m_name)
|
||||
break;
|
||||
}
|
||||
if (!used)
|
||||
c->m_rtpMedia.remove(recv,true);
|
||||
}
|
||||
// Check if both parties have common media
|
||||
if (!c->m_rtpMedia.skipNull()) {
|
||||
if (debugAt(DebugInfo)) {
|
||||
String localCaps;
|
||||
s_usedCodecs.createList(localCaps,false);
|
||||
Debug(this,DebugNote,
|
||||
"Event(%s) no common media for content='%s' local='%s' remote='%s' [%p]",
|
||||
event.actionName(),c->toString().c_str(),localCaps.c_str(),
|
||||
remoteCaps.c_str(),this);
|
||||
}
|
||||
if (!checkMedia(event,*c)) {
|
||||
remove.append(c)->setDelete(false);
|
||||
continue;
|
||||
}
|
||||
|
@ -2514,7 +2479,7 @@ JGSessionContent* YJGConnection::buildAudioContent(JGRtpCandidates::Type type,
|
|||
if (m_secure && m_secureRequired)
|
||||
c->m_rtpMedia.m_cryptoRequired = true;
|
||||
if (useFormats)
|
||||
setMedia(c->m_rtpMedia,m_formats,s_usedCodecs);
|
||||
c->m_rtpMedia.setMedia(m_audioFormats);
|
||||
|
||||
c->m_rtpLocalCandidates.m_type = c->m_rtpRemoteCandidates.m_type = type;
|
||||
|
||||
|
@ -2685,7 +2650,9 @@ void YJGConnection::setEarlyMediaOut(Message& msg)
|
|||
if (!c) {
|
||||
c = buildAudioContent(JGRtpCandidates::RtpRawUdp,
|
||||
JGSessionContent::SendResponder,false,false);
|
||||
setMedia(c->m_rtpMedia,formats,s_usedCodecs);
|
||||
plugin.lock();
|
||||
c->m_rtpMedia.setMedia(s_usedCodecs,formats);
|
||||
plugin.unlock();
|
||||
c->setEarlyMedia();
|
||||
addContent(true,c);
|
||||
}
|
||||
|
@ -3037,8 +3004,11 @@ void YJGConnection::copySessionParams(NamedList& list, bool redirect)
|
|||
jingleAddParam(list,"redirectcount",String(m_redirectCount),copy);
|
||||
list.addParam("diverter",m_remote,false);
|
||||
}
|
||||
if (m_ftStatus == FTNone)
|
||||
jingleAddParam(list,"formats",m_formats,copy,false);
|
||||
if (m_ftStatus == FTNone) {
|
||||
String formats;
|
||||
m_audioFormats.createList(formats,true);
|
||||
jingleAddParam(list,"formats",formats,copy,false);
|
||||
}
|
||||
else
|
||||
jingleAddParam(list,"format","data",copy);
|
||||
// Jingle session params
|
||||
|
@ -3075,6 +3045,125 @@ void YJGConnection::copySessionParams(NamedList& list, bool redirect)
|
|||
jingleAddParam(list,"file_time",String(t),copy);
|
||||
}
|
||||
|
||||
// Check media in a received content
|
||||
bool YJGConnection::checkMedia(const JGEvent& event, JGSessionContent& c)
|
||||
{
|
||||
JGRtpMediaList& codecs = m_audioFormats;
|
||||
// Fill a string with our capabilities for debug purposes
|
||||
String remoteCaps;
|
||||
if (debugAt(DebugInfo))
|
||||
c.m_rtpMedia.createList(remoteCaps,false);
|
||||
ListIterator iter(c.m_rtpMedia);
|
||||
for (GenObject* go = 0; (go = iter.get());) {
|
||||
JGRtpMedia* recv = static_cast<JGRtpMedia*>(go);
|
||||
XDebug(this,DebugAll,"Checking received media %s/%s/%s/%s/%s/%s/%s [%p]",
|
||||
recv->m_id.c_str(),recv->m_name.c_str(),recv->m_clockrate.c_str(),
|
||||
recv->m_channels.c_str(),recv->m_pTime.c_str(),
|
||||
recv->m_maxPTime.c_str(),recv->m_bitRate.c_str(),this);
|
||||
const char* reason = 0;
|
||||
int level = DebugNote;
|
||||
// Use a while() to break to the end
|
||||
while (true) {
|
||||
// RTP payload id must be [0..127]
|
||||
int payloadId = recv->m_id.toInteger(-1);
|
||||
if (payloadId < 0 || payloadId > 127) {
|
||||
reason = "Invalid id";
|
||||
break;
|
||||
}
|
||||
// XEP 0167: Channels is an unsigned byte, defaults to 1
|
||||
// We support only 1 channel for now
|
||||
if (recv->m_channels.toInteger(1) != 1) {
|
||||
reason = "Invalid number of channels";
|
||||
break;
|
||||
}
|
||||
JGRtpMedia* found = 0;
|
||||
// 0..95: static payloads: match by id
|
||||
// > 95: dynamic payloads: match by name
|
||||
if (payloadId < 96)
|
||||
found = codecs.findMedia(recv->m_id);
|
||||
else if (recv->m_name) {
|
||||
// Remove tel event from offer
|
||||
if ((recv->m_name &= "telephone-event") ||
|
||||
(recv->m_name &= "tone") ||
|
||||
(recv->m_name &= "audio/telephone-event")) {
|
||||
XDebug(this,DebugAll,"Removing tel event '%s' [%p]",
|
||||
recv->m_name.c_str(),this);
|
||||
c.m_rtpMedia.remove(recv);
|
||||
break;
|
||||
}
|
||||
for (ObjList* o = codecs.skipNull(); o; o = o->skipNext(), found = 0) {
|
||||
found = static_cast<JGRtpMedia*>(o->get());
|
||||
if (found->m_name |= recv->m_name)
|
||||
continue;
|
||||
if (recv->m_clockrate && recv->m_clockrate != found->m_clockrate)
|
||||
continue;
|
||||
// Fix ilbc
|
||||
if (recv->m_name &= "ilbc") {
|
||||
// RFC 3952 specifies
|
||||
// 30ms ptime = 13.33 kbit/s: check 13000
|
||||
// 20ms ptime = 15.2 kbit/s: check 15000
|
||||
if (!recv->m_pTime && recv->m_bitRate) {
|
||||
int val = recv->m_bitRate.toInteger() / 1000;
|
||||
if (val == 13)
|
||||
recv->m_pTime = "30";
|
||||
else if (val == 15)
|
||||
recv->m_pTime = "20";
|
||||
}
|
||||
if (!recv->m_pTime)
|
||||
recv->m_pTime = (s_ilbcDefault30 ? "30" : "20");
|
||||
if (recv->m_pTime != found->m_pTime)
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// XEP 0167: name is mandatory for dynamic payloads
|
||||
reason = "Missing name for dynamic payload";
|
||||
break;
|
||||
}
|
||||
if (found) {
|
||||
XDebug(this,DebugAll,"Setting synonym=%s to received %s from %s/%s [%p]",
|
||||
found->m_synonym.c_str(),recv->m_name.c_str(),
|
||||
found->m_id.c_str(),found->m_name.c_str(),this);
|
||||
recv->m_synonym = found->m_synonym;
|
||||
}
|
||||
else {
|
||||
reason = "Codec disabled/unknown";
|
||||
level = DebugAll;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!reason)
|
||||
continue;
|
||||
Debug(this,level,
|
||||
"Event(%s) removing payload id=%s %s/%s/%s/%s from content='%s': %s [%p]",
|
||||
event.actionName(),recv->m_id.c_str(),recv->m_name.c_str(),
|
||||
recv->m_clockrate.c_str(),recv->m_channels.c_str(),recv->m_pTime.c_str(),
|
||||
c.toString().c_str(),reason,this);
|
||||
c.m_rtpMedia.remove(recv);
|
||||
}
|
||||
// Check if both parties have common media
|
||||
if (c.m_rtpMedia.skipNull()) {
|
||||
#ifdef DEBUG
|
||||
String formats;
|
||||
c.m_rtpMedia.createList(formats,true);
|
||||
Debug(this,DebugAll,"Set formats '%s' in content '%s' [%p]",
|
||||
formats.c_str(),c.toString().c_str(),this);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
if (debugAt(DebugInfo)) {
|
||||
String localCaps;
|
||||
codecs.createList(localCaps,false);
|
||||
Debug(this,DebugNote,
|
||||
"Event(%s) no common media for content='%s' local='%s' remote='%s' [%p]",
|
||||
event.actionName(),c.toString().c_str(),localCaps.c_str(),
|
||||
remoteCaps.c_str(),this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transfer thread (route and execute)
|
||||
|
@ -3261,8 +3350,11 @@ void YJGDriver::initialize()
|
|||
s_knownCodecs.add("32", "MPV", "90000", "mpv");
|
||||
s_knownCodecs.add("34", "H263", "90000", "h263");
|
||||
s_knownCodecs.add("98", "iLBC", "8000", "ilbc");
|
||||
s_knownCodecs.add("98", "iLBC", "8000", "ilbc20");
|
||||
s_knownCodecs.add("98", "iLBC", "8000", "ilbc30");
|
||||
s_knownCodecs.add("98", "iLBC", "8000", "ilbc20", 0, "20");
|
||||
s_knownCodecs.add("98", "iLBC", "8000", "ilbc30", 0, "30");
|
||||
s_knownCodecs.add("102","speex", "8000", "speex");
|
||||
s_knownCodecs.add("103","speex", "16000", "speex/16000");
|
||||
s_knownCodecs.add("104","speex", "32000", "speex/32000");
|
||||
|
||||
s_jingle = new YJGEngine;
|
||||
s_jingle->debugChain(this);
|
||||
|
@ -3369,10 +3461,24 @@ void YJGDriver::initialize()
|
|||
bool defcodecs = s_cfg.getBoolValue("codecs","default",true);
|
||||
for (ObjList* o = s_knownCodecs.skipNull(); o; o = o->skipNext()) {
|
||||
JGRtpMedia* crt = static_cast<JGRtpMedia*>(o->get());
|
||||
if (crt->m_name &= "ilbc")
|
||||
continue;
|
||||
bool enable = defcodecs && DataTranslator::canConvert(crt->m_synonym);
|
||||
if (s_cfg.getBoolValue("codecs",crt->m_synonym,enable))
|
||||
s_usedCodecs.append(new JGRtpMedia(*crt));
|
||||
}
|
||||
// Special care for ilbc
|
||||
bool ilbc = s_cfg.getBoolValue("codecs","ilbc",defcodecs);
|
||||
if (ilbc) {
|
||||
String tmp = s_cfg.getValue("hacks","ilbc_forced","ilbc30");
|
||||
if (tmp != "ilbc20" && tmp != "ilbc30")
|
||||
tmp = "ilbc30";
|
||||
JGRtpMedia* s = s_knownCodecs.findSynonym(tmp);
|
||||
if (s && DataTranslator::canConvert(s->m_synonym))
|
||||
s_usedCodecs.append(new JGRtpMedia(*s));
|
||||
tmp = s_cfg.getValue("hacks","ilbc_default","ilbc30");
|
||||
s_ilbcDefault30 = (tmp != "ilbc20");
|
||||
}
|
||||
|
||||
TelEngine::destruct(m_ftProxy);
|
||||
const char* ftJid = sect->getValue("socks_proxy_jid");
|
||||
|
@ -3388,14 +3494,14 @@ void YJGDriver::initialize()
|
|||
}
|
||||
|
||||
int dbg = DebugInfo;
|
||||
if (!m_localAddress)
|
||||
if (!m_localAddress && s_serverMode)
|
||||
dbg = DebugNote;
|
||||
if (!s_usedCodecs.count())
|
||||
dbg = DebugWarn;
|
||||
|
||||
if (debugAt(dbg)) {
|
||||
String s;
|
||||
s << " localip=" << m_localAddress ? m_localAddress.c_str() : "MISSING";
|
||||
s << " localip=" << (m_localAddress ? m_localAddress.c_str() : "MISSING");
|
||||
s << " jingle_version=" << JGSession::lookupVersion(s_sessVersion);
|
||||
s << " singletone=" << String::boolText(s_singleTone);
|
||||
s << " pending_timeout=" << s_pendingTimeout;
|
||||
|
|
Loading…
Reference in New Issue