/** * media.cpp * This file is part of the YATE Project http://YATE.null.ro * * SDP media handling * * Yet Another Telephony Engine - a fully featured software PBX and IVR * Copyright (C) 2004-2014 Null Team * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include namespace TelEngine { /* * SDPMedia */ SDPMedia::SDPMedia(const char* media, const char* transport, const char* formats, int rport, int lport) : NamedList(media), m_audio(true), m_video(false), m_modified(false), m_securable(true), m_localChanged(false), m_transport(transport), m_formats(formats), m_rfc2833(String::boolText(false)) { DDebug(DebugAll,"SDPMedia::SDPMedia('%s','%s','%s',%d,%d) [%p]", media,transport,formats,rport,lport,this); if (String::operator!=(YSTRING("audio"))) { m_audio = false; m_video = String::operator==(YSTRING("video")); m_suffix << "_" << media; } int q = m_formats.find(','); m_format = m_formats.substr(0,q); if (rport >= 0) m_rPort = rport; if (lport >= 0) m_lPort = lport; } SDPMedia::~SDPMedia() { DDebug(DebugAll,"SDPMedia::~SDPMedia() '%s' [%p]",c_str(),this); } const char* SDPMedia::fmtList() const { if (m_formats) return m_formats.c_str(); if (m_format) return m_format.c_str(); // unspecified audio assumed to support G711 if (m_audio) return "alaw,mulaw"; return 0; } // Compare this media with another one bool SDPMedia::sameAs(const SDPMedia* other, bool ignorePort, bool checkStarted) const { if (!other) return false; const SDPMedia& m = *other; if (m.transport() != m_transport) return false; if (m.remotePort() != m_rPort) return false; checkStarted = checkStarted && isStarted(); if (!checkStarted) { return (m.formats() == m_formats) && ((ignorePort && m.remotePort() && m_rPort) || (m.remotePort() == m_rPort)); } // Check format ObjList* lst = m.formats().split(',',false); bool found = (0 != lst->find(m_format)); TelEngine::destruct(lst); if (!found) { XDebug(DebugAll,"SDPMedia::sameAs(%p) format='%s' other_formats='%s': not found [%p]", other,m_format.c_str(),m.formats().c_str(),this); return false; } // Check payload format int pLoad = SDPMedia::payloadMapping(mappings(),m_format); int oPLoad = SDPMedia::payloadMapping(m.mappings(),m_format); if (pLoad == -1 || oPLoad == -1 || pLoad != oPLoad) { XDebug(DebugAll, "SDPMedia::sameAs(%p) format='%s' pload=%d (%s) other_pload=%d (%s): not matched [%p]", other,m_format.c_str(),pLoad,mappings().c_str(),oPLoad,m.mappings().c_str(),this); return false; } // Check RFC 2833 if (m_rfc2833 != m.m_rfc2833) { XDebug(DebugAll, "SDPMedia::sameAs(%p) rfc2833=%s other_rfc2833=%s: not matched [%p]", other,m_rfc2833.c_str(),m.m_rfc2833.c_str(),this); return false; } // TODO: Check crypto return true; } // Update members with data taken from a SDP, return true if something changed bool SDPMedia::update(const char* formats, int rport, int lport, bool force) { DDebug(DebugAll,"SDPMedia::update('%s',%d,%d,%s) [%p]", formats,rport,lport,String::boolText(force),this); bool chg = false; String tmp(formats); if (tmp && (m_formats != tmp)) { if (tmp.find(',') < 0) { // single format received, check if acceptable if (m_formats && !force && m_formats.find(tmp) < 0) { Debug(DebugNote,"Not changing to '%s' from '%s' [%p]", formats,m_formats.c_str(),this); tmp.clear(); } } else if (m_formats && !force) { // from received list keep only already offered formats ObjList* l1 = tmp.split(',',false); ObjList* l2 = m_formats.split(',',false); for (ObjList* fmt = l1->skipNull(); fmt; ) { if (l2->find(fmt->get()->toString())) fmt = fmt->skipNext(); else { fmt->remove(); fmt = fmt->skipNull(); } } tmp.clear(); tmp.append(l1,","); TelEngine::destruct(l1); TelEngine::destruct(l2); if (tmp.null()) Debug(DebugNote,"Not changing formats '%s' [%p]",m_formats.c_str(),this); } if (tmp && (m_formats != tmp)) { chg = true; m_formats = tmp; int q = m_formats.find(','); m_format = m_formats.substr(0,q); Debug(DebugInfo,"Choosing offered '%s' format '%s' [%p]", c_str(),m_format.c_str(),this); } } if (rport >= 0) { tmp = rport; if (m_rPort != tmp) { chg = true; m_rPort = tmp; } } if (lport >= 0) { tmp = lport; if (m_lPort != tmp) { m_localChanged = true; chg = true; m_lPort = tmp; } } return chg; } // Update members from a dispatched "chan.rtp" message void SDPMedia::update(const NamedList& msg, bool pickFormat) { DDebug(DebugAll,"SDPMedia::update('%s',%s) [%p]", msg.c_str(),String::boolText(pickFormat),this); m_id = msg.getValue("rtpid",m_id); m_lPort = msg.getValue("localport",m_lPort); if (pickFormat) { const char* format = msg.getValue("format"); if (format) { m_format = format; if ((m_formats != m_format) && (msg.getIntValue("remoteport") > 0)) { Debug(DebugNote,"Choosing started '%s' format '%s' [%p]", c_str(),format,this); m_formats = m_format; } } } } // Add or replace a parameter by name and value, set the modified flag void SDPMedia::parameter(const char* name, const char* value, bool append) { if (!name) return; m_modified = true; if (append) addParam(name,value); else setParam(name,value); } // Add or replace a parameter, set the modified flag void SDPMedia::parameter(NamedString* param, bool append) { if (!param) return; m_modified = true; if (append) addParam(param); else setParam(param); } void SDPMedia::crypto(const char* desc, bool remote) { String& sdes = remote ? m_rCrypto : m_lCrypto; if (sdes != desc) { sdes = desc; m_modified = true; } if (remote && !desc) m_securable = false; } // Put the list of net media in a parameter list void SDPMedia::putMedia(NamedList& msg, bool putPort) { msg.addParam("media" + suffix(),"yes"); msg.addParam("formats" + suffix(),formats()); msg.addParam("transport" + suffix(),transport()); if (mappings()) msg.addParam("rtp_mapping" + suffix(),mappings()); if (isAudio()) msg.addParam("rtp_rfc2833",rfc2833()); if (putPort) msg.addParam("rtp_port" + suffix(),remotePort()); if (remoteCrypto()) msg.addParam("crypto" + suffix(),remoteCrypto()); // must handle encryption differently const char* enc = getValue("encryption"); if (enc) msg.addParam("encryption" + suffix(),enc); clearParam("encryption"); unsigned int n = length(); for (unsigned int i = 0; i < n; i++) { const NamedString* param = getParam(i); if (param) msg.addParam("sdp" + suffix() + "_" + param->name(),*param); } } // Copy RTP related data from old media void SDPMedia::keepRtp(const SDPMedia& other) { m_formats = other.m_formats; m_format = other.m_format; m_rfc2833 = other.m_rfc2833; m_id = other.m_id; m_rPort = other.m_rPort; m_lPort = other.m_lPort; crypto(other.m_rCrypto,true); crypto(other.m_lCrypto,false); } int SDPMedia::payloadMapping(const String& mappings, const String& fmt) { if (!(mappings && fmt)) return -2; String tmp = fmt; tmp << "="; ObjList* lst = mappings.split(',',false); int payload = -2; for (ObjList* pl = lst; pl; pl = pl->next()) { String* mapping = static_cast(pl->get()); if (!mapping) continue; if (mapping->startsWith(tmp)) { payload = mapping->substr(tmp.length()).toInteger(-1); break; } } TelEngine::destruct(lst); return payload; } }; // namespace TelEngine /* vi: set ts=8 sw=4 sts=4 noet: */