// Put the list of net media in a parameter list void SDPSession::putMedia(NamedList& msg, ObjList* mList, bool putPort) { if (!mList) return; bool audio = false; bool other = false; for (mList = mList->skipNull(); mList; mList = mList->skipNext()) { SDPMedia* m = static_cast<SDPMedia*>(mList->get()); m->putMedia(msg,putPort); if (m->isAudio()) audio = true; else other = true; } if (other && !audio) msg.setParam("media",String::boolText(false)); }
// Set new media list. Return true if changed bool SDPSession::setMedia(ObjList* media) { if (media == m_rtpMedia) return false; DDebug(m_parser,DebugAll,"SDPSession::setMedia(%p) [%p]",media,this); ObjList* tmp = m_rtpMedia; m_rtpMedia = media; bool chg = m_rtpMedia != 0; if (tmp) { chg = false; for (ObjList* o = tmp->skipNull(); o; o = o->skipNext()) { SDPMedia* m = static_cast<SDPMedia*>(o->get()); if (media && m->sameAs(static_cast<SDPMedia*>((*media)[*m]),m_parser->ignorePort())) continue; chg = true; mediaChanged(*m); } TelEngine::destruct(tmp); } return chg; }
// Repeatedly calls dispatchRtp() for each media in the list // Update it on success. Remove it on failure bool SDPSession::dispatchRtp(const char* addr, bool start, RefObject* context) { if (!m_rtpMedia) return false; DDebug(m_parser,DebugAll,"SDPSession::dispatchRtp(%s,%u,%p) [%p]", addr,start,context,this); bool ok = false; ObjList* o = m_rtpMedia->skipNull(); while (o) { SDPMedia* m = static_cast<SDPMedia*>(o->get()); if (dispatchRtp(m,addr,start,true,context)) { ok = true; o = o->skipNext(); } else { Debug(m_parser,DebugMild, "Removing failed SDP media '%s' format '%s' from offer [%p]", m->c_str(),m->format().safe(),this); o->remove(); o = o->skipNull(); } } return ok; }
// Creates a SDP body from transport address and list of media descriptors // Use own list if given media list is 0 MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList) { DDebug(m_parser,DebugAll,"SDPSession::createSDP('%s',%p) [%p]",addr,mediaList,this); if (!mediaList) mediaList = m_rtpMedia; // if we got no media descriptors we simply create no SDP if (!mediaList) return 0; if (m_sdpSession) ++m_sdpVersion; else m_sdpVersion = m_sdpSession = Time::secNow(); // no address means on hold or muted String origin; origin << "yate " << m_sdpSession << " " << m_sdpVersion; origin << " IN IP4 " << (addr ? addr : m_host.safe()); String conn; conn << "IN IP4 " << (addr ? addr : "0.0.0.0"); MimeSdpBody* sdp = new MimeSdpBody; sdp->addLine("v","0"); sdp->addLine("o",origin); sdp->addLine("s",m_parser->m_sessionName); sdp->addLine("c",conn); sdp->addLine("t","0 0"); Lock lock(m_parser); bool defcodecs = m_parser->m_codecs.getBoolValue("default",true); for (ObjList* ml = mediaList->skipNull(); ml; ml = ml->skipNext()) { SDPMedia* m = static_cast<SDPMedia*>(ml->get()); String mline(m->fmtList()); ObjList* l = mline.split(',',false); mline = *m; mline << " " << (m->localPort() ? m->localPort().c_str() : "0") << " " << m->transport(); ObjList* map = m->mappings().split(',',false); ObjList rtpmap; String frm; int ptime = 0; ObjList* f = l; for (; f; f = f->next()) { String* s = static_cast<String*>(f->get()); if (s) { int mode = 0; if (*s == "ilbc20") ptime = mode = 20; else if (*s == "ilbc30") ptime = mode = 30; else if (*s == "g729b") continue; int payload = s->toInteger(SDPParser::s_payloads,-1); int defcode = payload; String tmp = *s; tmp << "="; for (ObjList* pl = map; pl; pl = pl->next()) { String* mapping = static_cast<String*>(pl->get()); if (!mapping) continue; if (mapping->startsWith(tmp)) { payload = -1; tmp = *mapping; tmp >> "=" >> payload; XDebug(m_parser,DebugAll,"RTP mapped payload %d for '%s' [%p]", payload,s->c_str(),this); break; } } if (payload >= 0) { if (defcode < 0) defcode = payload; const char* map = lookup(defcode,SDPParser::s_rtpmap); if (map && m_parser->m_codecs.getBoolValue(*s,defcodecs && DataTranslator::canConvert(*s))) { frm << " " << payload; String* temp = new String("rtpmap:"); *temp << payload << " " << map; rtpmap.append(temp); if (mode) { temp = new String("fmtp:"); *temp << payload << " mode=" << mode; rtpmap.append(temp); } if (*s == "g729") { temp = new String("fmtp:"); *temp << payload << " annexb=" << ((0 != l->find("g729b")) ? "yes" : "no"); rtpmap.append(temp); } else if (*s == "amr") { temp = new String("fmtp:"); *temp << payload << " octet-align=0"; rtpmap.append(temp); } else if (*s == "amr-o") { temp = new String("fmtp:"); *temp << payload << " octet-align=1"; rtpmap.append(temp); } if(s->length()) { String key("fmtp-"); key << *s; for(unsigned int i = 0; i < m->length(); ++i) { const NamedString *ns = m->getParam(i); if(ns && ns->name() == key) { temp = new String("fmtp:"); *temp << payload << " " << *ns; rtpmap.append(temp); } } } } } } } TelEngine::destruct(l); TelEngine::destruct(map); if ((m_rfc2833 >= 0) && frm && m->isAudio()) { int rfc2833 = m->rfc2833().toInteger(m_rfc2833); if (rfc2833 < 96 || rfc2833 > 127) rfc2833 = 101; // claim to support telephone events frm << " " << rfc2833; String* s = new String; *s << "rtpmap:" << rfc2833 << " telephone-event/8000"; rtpmap.append(s); } if (frm.null()) { if (m->isAudio() || !m->fmtList()) { Debug(m_parser,DebugMild,"No formats for '%s', excluding from SDP [%p]", m->c_str(),this); continue; } Debug(m_parser,DebugInfo,"Assuming formats '%s' for media '%s' [%p]", m->fmtList(),m->c_str(),this); frm << " " << m->fmtList(); // brutal but effective for (char* p = const_cast<char*>(frm.c_str()); *p; p++) { if (*p == ',') *p = ' '; } } if (ptime) { String* temp = new String("ptime:"); *temp << ptime; rtpmap.append(temp); } sdp->addLine("m",mline + frm); bool enc = false; if (m->isModified()) { unsigned int n = m->length(); for (unsigned int i = 0; i < n; i++) { const NamedString* param = m->getParam(i); if (param) { String tmp = param->name(); if (tmp.startsWith("fmtp-")) continue; if (*param) tmp << ":" << *param; sdp->addLine("a",tmp); enc = enc || (param->name() == "encryption"); } } } for (f = rtpmap.skipNull(); f; f = f->skipNext()) { String* s = static_cast<String*>(f->get()); if (s) sdp->addLine("a",*s); } if (addr && m->localCrypto()) { sdp->addLine("a","crypto:" + m->localCrypto()); if (!enc) sdp->addLine("a","encryption:optional"); } }
// Update from parameters. Build a default SDP if no media is found in params bool SDPSession::updateSDP(const NamedList& params) { DDebug(m_parser,DebugAll,"SDPSession::updateSdp('%s') [%p]",params.c_str(),this); bool defaults = true; const char* sdpPrefix = params.getValue("osdp-prefix","osdp"); ObjList* lst = 0; unsigned int n = params.length(); String defFormats; m_parser->getAudioFormats(defFormats); for (unsigned int i = 0; i < n; i++) { const NamedString* p = params.getParam(i); if (!p) continue; // search for rtp_port or rtp_port_MEDIANAME parameters String tmp(p->name()); if (!tmp.startSkip("media",false)) continue; if (tmp && (tmp[0] != '_')) continue; // since we found at least one media declaration disable defaults defaults = false; // now tmp holds the suffix for the media, null for audio bool audio = tmp.null(); // check if media is supported, default only for audio if (!p->toBoolean(audio)) continue; String fmts = params.getValue("formats" + tmp); if (audio && fmts.null()) fmts = defFormats; if (fmts.null()) continue; String trans = params.getValue("transport" + tmp,"RTP/AVP"); String crypto; if (m_secure) crypto = params.getValue("crypto" + tmp); if (audio) tmp = "audio"; else tmp >> "_"; SDPMedia* rtp = 0; // try to take the media descriptor from the old list if (m_rtpMedia) { ObjList* om = m_rtpMedia->find(tmp); if (om) rtp = static_cast<SDPMedia*>(om->remove(false)); } bool append = false; if (rtp) rtp->update(fmts); else { rtp = new SDPMedia(tmp,trans,fmts); append = true; } rtp->crypto(crypto,false); if (sdpPrefix) { for (unsigned int j = 0; j < n; j++) { const NamedString* param = params.getParam(j); if (!param) continue; tmp = param->name(); if (tmp.startSkip(sdpPrefix + rtp->suffix() + "_",false) && (tmp.find('_') < 0)) rtp->parameter(tmp,*param,append); } } if (!lst) lst = new ObjList; lst->append(rtp); } if (defaults && !lst) { lst = new ObjList; lst->append(new SDPMedia("audio","RTP/AVP",params.getValue("formats",defFormats))); } return setMedia(lst); }
// Creates a SDP body from transport address and list of media descriptors // Use own list if given media list is 0 MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList) { DDebug(m_enabler,DebugAll,"SDPSession::createSDP('%s',%p) [%p]",addr,mediaList,m_ptr); if (!mediaList) mediaList = m_rtpMedia; // if we got no media descriptors we simply create no SDP if (!mediaList) return 0; if (m_sdpSession) ++m_sdpVersion; else m_sdpVersion = m_sdpSession = Time::secNow(); // override the address with the externally advertised if needed if (addr && m_rtpNatAddr) addr = m_rtpNatAddr; if (!m_originAddr) m_originAddr = addr ? addr : m_host.safe(); // no address means on hold or muted String origin; origin << "yate " << m_sdpSession << " " << m_sdpVersion; origin << " "; int f = addIP(origin,m_originAddr); String conn; addIP(conn,addr,f); MimeSdpBody* sdp = new MimeSdpBody; sdp->addLine("v","0"); sdp->addLine("o",origin); sdp->addLine("s",m_parser->m_sessionName); sdp->addLine("c",conn); sdp->addLine("t","0 0"); Lock lock(m_parser); bool defcodecs = m_parser->m_codecs.getBoolValue("default",true); for (ObjList* ml = mediaList->skipNull(); ml; ml = ml->skipNext()) { SDPMedia* m = static_cast<SDPMedia*>(ml->get()); int rfc2833 = 0; if ((m_rfc2833 >= 0) && m->isAudio()) { if (!m_rtpForward) { rfc2833 = m->rfc2833().toInteger(m_rfc2833); if (rfc2833 < 96 || rfc2833 > 127) rfc2833 = 101; } else if (m->rfc2833().toBoolean(true)) { rfc2833 = m->rfc2833().toInteger(); if (rfc2833 < 96 || rfc2833 > 127) rfc2833 = 0; } } String mline(m->fmtList()); ObjList* l = mline.split(',',false); mline = *m; mline << " " << (m->localPort() ? m->localPort().c_str() : "0") << " " << m->transport(); ObjList* map = m->mappings().split(',',false); ObjList rtpmap; ObjList* dest = &rtpmap; String frm; int ptime = 0; ObjList* f = l; for (; f; f = f->next()) { const String* s = static_cast<const String*>(f->get()); if (s) { int mode = 0; if (*s == "g729b") continue; int payload = s->toInteger(SDPParser::s_payloads,-1); int defcode = payload; String tmp = *s; tmp << "="; bool found = false; for (ObjList* pl = map; pl; pl = pl->next()) { const String* mapping = static_cast<const String*>(pl->get()); if (!mapping) continue; if (mapping->startsWith(tmp)) { payload = -1; tmp = *mapping; tmp >> "=" >> payload; found = true; XDebug(m_enabler,DebugAll,"RTP mapped payload %d for '%s' [%p]", payload,s->c_str(),m_ptr); break; } String tmp2 = *mapping; int pload; tmp2 >> "=" >> pload; if (payload == pload) { XDebug(m_enabler,DebugAll,"RTP conflict for payload %d, allocating new [%p]", payload,m_ptr); payload = -1; u_int32_t bmap = 0; for (ObjList* sl = map; sl; sl = sl->next()) { mapping = static_cast<const String*>(sl->get()); if (!mapping) continue; tmp2 = *mapping; pload = 0; tmp2 >> "=" >> pload; if (pload >= 96 && pload < 127) bmap |= 1 << (pload - 96); } // allocate free and non-standard is possible for (pload = 96; pload < 127; pload++) { if (pload == rfc2833) continue; if (lookup(pload,SDPParser::s_rtpmap)) continue; if ((bmap & (1 << (pload - 96))) == 0) { payload = pload; break; } } if (payload >= 0) break; // none free, allocate from "standard" ones too for (pload = 96; pload < 127; pload++) { if (pload == rfc2833) continue; if ((bmap & (1 << (pload - 96))) == 0) { payload = pload; break; } } break; } } if (payload >= 0) { if (!found) { tmp = *s; tmp << "=" << payload; map->append(new String(tmp)); } if (defcode < 0) defcode = payload; const char* map = lookup(defcode,SDPParser::s_rtpmap); if (map && m_parser->m_codecs.getBoolValue(*s,defcodecs && DataTranslator::canConvert(*s))) { if (*s == "ilbc20") ptime = mode = 20; else if (*s == "ilbc30") ptime = mode = 30; frm << " " << payload; String* temp = new String("rtpmap:"); *temp << payload << " " << map; dest = dest->append(temp); if (mode) { temp = new String("fmtp:"); *temp << payload << " mode=" << mode; dest = dest->append(temp); } if (*s == "g729") { temp = new String("fmtp:"); *temp << payload << " annexb=" << ((0 != l->find("g729b")) ? "yes" : "no"); dest = dest->append(temp); } else if (*s == "amr") { temp = new String("fmtp:"); *temp << payload << " octet-align=0"; dest = dest->append(temp); } else if (*s == "amr-o") { temp = new String("fmtp:"); *temp << payload << " octet-align=1"; dest = dest->append(temp); } } } } }
bool SDPDescription::add_media(const SDPMedia& media) { if (!media.add_to_sdp_description(sdp_description_, index_, ip_addr_)) return false; index_++; return true; }