mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
750 lines
18 KiB
750 lines
18 KiB
/*
|
|
* $Id: AmSdp.cpp,v 1.20.2.4 2005/06/01 12:00:24 rco Exp $
|
|
*
|
|
* Copyright (C) 2002-2003 Fhg Fokus
|
|
*
|
|
* This file is part of sems, a free SIP media server.
|
|
*
|
|
* sems is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* For a license to use the ser software under conditions
|
|
* other than those described here, or to purchase support for this
|
|
* software, please contact iptel.org by e-mail at the following addresses:
|
|
* info@iptel.org
|
|
*
|
|
* sems 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. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <assert.h>
|
|
|
|
#include "AmConfig.h"
|
|
#include "AmSdp.h"
|
|
#include "AmCmd.h"
|
|
#include "AmUtils.h"
|
|
#include "AmPlugIn.h"
|
|
|
|
#include "amci/amci.h"
|
|
#include "log.h"
|
|
|
|
inline char* get_next_line(char* s);
|
|
inline bool parse_string_tok(char*& s, string& res, char sep_char = ' ');
|
|
inline bool parse_type(char*& s, int& v, char** ref, const char* name);
|
|
|
|
static bool parse_sdp_connection(AmSdp* sdp_msg, char*& s, char*&);
|
|
static bool parse_sdp_media(AmSdp* sdp_msg, char*& s, char*&);
|
|
static bool parse_sdp_attribute(AmSdp* sdp_msg, char*& s, char*&);
|
|
|
|
|
|
// inline functions
|
|
|
|
inline string net_t_2_str(int nt)
|
|
{
|
|
switch(nt){
|
|
case NT_IN: return "IN";
|
|
default: return "<unknown network type>";
|
|
}
|
|
}
|
|
|
|
inline string addr_t_2_str(int at)
|
|
{
|
|
switch(at){
|
|
case AT_V4: return "IP4";
|
|
case AT_V6: return "IP6";
|
|
default: return "<unknown address type>";
|
|
}
|
|
}
|
|
|
|
inline string media_t_2_str(int mt)
|
|
{
|
|
switch(mt){
|
|
case MT_AUDIO: return "audio";
|
|
case MT_VIDEO: return "video";
|
|
case MT_APPLICATION: return "application";
|
|
case MT_DATA: return "data";
|
|
default: return "<unknown media type>";
|
|
}
|
|
}
|
|
|
|
inline string transport_p_2_str(int tp)
|
|
{
|
|
switch(tp){
|
|
case TP_RTPAVP: return "RTP/AVP";
|
|
case TP_UDP: return "udp";
|
|
default: return "<unknown transport protocol>";
|
|
}
|
|
}
|
|
|
|
bool SdpPayload::operator == (int r)
|
|
{
|
|
DBG("pl == r: payload_type = %i; r = %i\n",payload_type, r);
|
|
return payload_type == r;
|
|
}
|
|
|
|
|
|
// WARNING:
|
|
//
|
|
// If you call this function from a handler function,
|
|
// do not forget to set 'next_line' to the proper value
|
|
// (else, it could think EOT has been reached !)
|
|
//
|
|
// Generally: call this function if you know what your doing !
|
|
// Else call 'parse_sdp_line'
|
|
//
|
|
inline bool parse_sdp_line_ex( AmSdp* sdp_msg, char*& s, char discr,
|
|
bool optional,
|
|
bool (*parse_func)(AmSdp*,char*&,char*&),
|
|
bool only_one, char*& next_line);
|
|
|
|
// WARNING:
|
|
//
|
|
// Do not touch the handler's third parameter until you known what you are doing.
|
|
//
|
|
inline bool parse_sdp_line( AmSdp* sdp_msg, char*& s, char discr,
|
|
bool optional, bool (*parse_func)(AmSdp*,char*&,char*&),
|
|
bool only_one = true );
|
|
|
|
//
|
|
// class AmSdp: Methods
|
|
//
|
|
|
|
AmSdp::AmSdp()
|
|
{
|
|
}
|
|
|
|
AmSdp::AmSdp(const AmSdp& p_sdp_msg)
|
|
: version(p_sdp_msg.version),
|
|
origin(p_sdp_msg.origin),
|
|
sessionName(p_sdp_msg.sessionName),
|
|
conn(p_sdp_msg.conn),
|
|
media(p_sdp_msg.media),
|
|
telephone_event_pt(NULL),
|
|
remote_active(false)
|
|
{
|
|
memcpy(r_buf,p_sdp_msg.r_buf,BUFFER_SIZE);
|
|
}
|
|
|
|
void AmSdp::setBody(const char* _sdp_msg)
|
|
{
|
|
strcpy(r_buf,_sdp_msg);
|
|
}
|
|
|
|
|
|
int AmSdp::parse()
|
|
{
|
|
char* s = r_buf;
|
|
|
|
media.clear();
|
|
|
|
bool ret =
|
|
parse_sdp_line(this,s,'v',false,NULL) ||
|
|
parse_sdp_line(this,s,'o',false,NULL) ||
|
|
parse_sdp_line(this,s,'s',false,NULL) ||
|
|
parse_sdp_line(this,s,'i',true,NULL) ||
|
|
parse_sdp_line(this,s,'u',true,NULL) ||
|
|
parse_sdp_line(this,s,'e',true,NULL,false) ||
|
|
parse_sdp_line(this,s,'p',true,NULL,false) ||
|
|
parse_sdp_line(this,s,'c',true,parse_sdp_connection) ||
|
|
parse_sdp_line(this,s,'b',true,NULL,false) ||
|
|
parse_sdp_line(this,s,'t',true,NULL,false) ||
|
|
parse_sdp_line(this,s,'k',true,NULL) ||
|
|
parse_sdp_line(this,s,'a',true,NULL,false) ||
|
|
parse_sdp_line(this,s,'m',false,parse_sdp_media,false);
|
|
|
|
if(!ret && conn.address.empty()){
|
|
for(vector<SdpMedia>::iterator it = media.begin();
|
|
!ret && (it != media.end()); ++it)
|
|
ret = it->conn.address.empty();
|
|
|
|
if(ret){
|
|
ERROR("A connection field must be present in every\n");
|
|
ERROR("media description or at the session level.\n");
|
|
}
|
|
}
|
|
|
|
telephone_event_pt = findPayload("telephone-event");
|
|
//DBG("telephone_event_pt = %i\n",telephone_event_pt);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int AmSdp::genResponse(const string& localip, int localport,
|
|
string& out_buf)
|
|
{
|
|
string l_ip = "IP4 " + localip;
|
|
|
|
#ifdef SUPPORT_IPV6
|
|
if(localip.find('.') = string::npos)
|
|
l_ip = "IP6 " + localip;
|
|
#endif
|
|
|
|
out_buf =
|
|
"v=0\r\n"
|
|
"o=username 0 0 IN " + l_ip + "\r\n"
|
|
"s=session\r\n"
|
|
"c=IN " + l_ip + "\r\n"
|
|
"t=0 0\r\n"
|
|
"m=audio " + int2str(localport) + " RTP/AVP";
|
|
|
|
string payloads;
|
|
string options;
|
|
|
|
for(vector<SdpPayload*>::iterator it = sup_pl.begin();
|
|
it != sup_pl.end(); ++it){
|
|
|
|
payloads += " " + int2str((*it)->payload_type);
|
|
|
|
//if ((*it)->payload_type >= 96) // dynamic payload
|
|
options += "a=rtpmap:" + int2str((*it)->payload_type) + " "
|
|
+ (*it)->encoding_name + "/" + int2str((*it)->clock_rate) + "\r\n";
|
|
|
|
if ((*it)->sdp_format_parameters.size()) {
|
|
// return format parameters as sent in the invite
|
|
// (we have initialized our codec with those)
|
|
options += "a=fmtp:" + int2str((*it)->payload_type) + " "
|
|
+ (*it)->sdp_format_parameters + "\r\n";
|
|
}
|
|
}
|
|
|
|
if (hasTelephoneEvent())
|
|
payloads += " " + int2str(telephone_event_pt->payload_type);
|
|
|
|
out_buf += payloads + "\r\n"
|
|
+ options;
|
|
if (hasTelephoneEvent())
|
|
{
|
|
out_buf += "a=rtpmap:" + int2str(telephone_event_pt->payload_type) + " " +
|
|
telephone_event_pt->encoding_name + "/" +
|
|
int2str(telephone_event_pt->clock_rate) + "\r\n"
|
|
"a=fmtp:" + int2str(telephone_event_pt->payload_type) + " 0-15\r\n";
|
|
}
|
|
|
|
if(remote_active /* dir == SdpMedia::DirActive */)
|
|
out_buf += "a=direction:passive\r\n";
|
|
|
|
return 0;
|
|
}
|
|
|
|
int AmSdp::genRequest(const string& localip,int localport, string& out_buf)
|
|
{
|
|
AmPlugIn* plugin = AmPlugIn::instance();
|
|
const map<int,amci_payload_t*>& payloads = plugin->getPayloads();
|
|
|
|
if(payloads.empty()){
|
|
ERROR("no payload plugin loaded.\n");
|
|
return -1;
|
|
}
|
|
|
|
string l_ip = "IP4 " + localip;
|
|
|
|
#ifdef SUPPORT_IPV6
|
|
if(localip.find('.') == string::npos)
|
|
l_ip = "IP6 " + localip;
|
|
#endif
|
|
|
|
out_buf =
|
|
"v=0\r\n"
|
|
"o=username 0 0 IN " + l_ip + "\r\n"
|
|
"s=session\r\n"
|
|
"c=IN " + l_ip + "\r\n"
|
|
"t=0 0\r\n"
|
|
"m=audio " + int2str(localport) + " RTP/AVP ";
|
|
|
|
map<int,amci_payload_t*>::const_iterator it = payloads.begin();
|
|
out_buf += int2str((it++)->first);
|
|
|
|
for(;it != payloads.end();++it)
|
|
out_buf += string(" ") + int2str(it->first);
|
|
|
|
out_buf += "\r\n";
|
|
|
|
for(it = payloads.begin();it != payloads.end();++it) {
|
|
|
|
//if(it->first >= 96) {
|
|
out_buf += "a=rtpmap:" + int2str(it->first)
|
|
+ " " + string(it->second->name)
|
|
+ "/" + int2str(it->second->sample_rate)
|
|
+ "\r\n";
|
|
//}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SdpPayload* AmSdp::getCompatiblePayload(int media_type, string& addr, int& port)
|
|
{
|
|
vector<SdpMedia>::iterator m_it;
|
|
SdpPayload* payload=0;
|
|
|
|
AmPlugIn* pi = AmPlugIn::instance();
|
|
|
|
for( m_it = media.begin(); m_it != media.end(); ++m_it ){
|
|
|
|
if( (media_type != m_it->type) )
|
|
continue;
|
|
|
|
vector<SdpPayload>::iterator it = m_it->payloads.begin();
|
|
for(; it != m_it->payloads.end(); ++it ) {
|
|
|
|
amci_payload_t* a_pl = NULL;
|
|
if(it->payload_type < 96){
|
|
// try static payloads
|
|
a_pl = pi->payload(it->payload_type);
|
|
}
|
|
|
|
if( a_pl ) {
|
|
|
|
payload = &(*it);
|
|
payload->int_pt = a_pl->payload_id;
|
|
payload->encoding_name = a_pl->name;
|
|
payload->clock_rate = a_pl->sample_rate;
|
|
goto end;
|
|
}
|
|
else {
|
|
// Try dynamic payloads
|
|
// and give a chance to broken
|
|
// implementation using a static payload number
|
|
// for dynamic ones.
|
|
|
|
int int_pt = getDynPayload(it->encoding_name,
|
|
it->clock_rate);
|
|
|
|
if(int_pt != -1){
|
|
|
|
payload = &(*it);
|
|
payload->int_pt = int_pt;
|
|
goto end;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
end:
|
|
sup_pl.clear();
|
|
|
|
if(payload){
|
|
|
|
if(m_it->conn.address.empty()){
|
|
DBG("using global address: %s\n",conn.address.c_str());
|
|
addr = conn.address;
|
|
}
|
|
else {
|
|
DBG("using media specific address: %s\n",m_it->conn.address.c_str());
|
|
addr = m_it->conn.address;
|
|
}
|
|
|
|
if(m_it->dir == SdpMedia::DirActive)
|
|
remote_active = true;
|
|
|
|
sup_pl.push_back(payload);
|
|
port = (int)m_it->port;
|
|
}
|
|
|
|
return payload;
|
|
}
|
|
|
|
bool AmSdp::hasTelephoneEvent()
|
|
{
|
|
return telephone_event_pt != NULL;
|
|
}
|
|
|
|
int AmSdp::getDynPayload(const string& name, int rate)
|
|
{
|
|
AmPlugIn* pi = AmPlugIn::instance();
|
|
const map<int, amci_payload_t*>& ref_payloads = pi->getPayloads();
|
|
|
|
for(map<int, amci_payload_t*>::const_iterator pl_it = ref_payloads.begin();
|
|
pl_it != ref_payloads.end(); ++pl_it)
|
|
if( (name == pl_it->second->name)
|
|
&& (rate == pl_it->second->sample_rate) )
|
|
return pl_it->first;
|
|
|
|
return -1;
|
|
}
|
|
|
|
const SdpPayload *AmSdp::findPayload(const string& name)
|
|
{
|
|
vector<SdpMedia>::iterator m_it;
|
|
|
|
for (m_it = media.begin(); m_it != media.end(); ++m_it)
|
|
{
|
|
vector<SdpPayload>::iterator it = m_it->payloads.begin();
|
|
for(; it != m_it->payloads.end(); ++it )
|
|
{
|
|
if (it->encoding_name == name)
|
|
{
|
|
return new SdpPayload(*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
// enum { TP_NONE=0, TP_RTPAVP, TP_UDP };
|
|
char* transport_prot_lookup[] = { "RTP/AVP", "udp", 0 };
|
|
|
|
inline bool parse_transport_prot(char*& s, int& tp)
|
|
{ return parse_type(s,tp,transport_prot_lookup,"transport protocol"); }
|
|
|
|
// enum { MT_NONE=0, MT_AUDIO, MT_VIDEO, MT_APPLICATION, MT_DATA };
|
|
char* media_type_lookup[] = { "audio", "video", "application", "data", 0 };
|
|
#define parse_media_type(s,mt) parse_type(s,mt,media_type_lookup,"media type")
|
|
|
|
//inline bool parse_media_type(char*& s, int& mt)
|
|
//{ return parse_type(s,mt,media_type_lookup,"media type"); }
|
|
|
|
// enum { NT_OTHER=0, NT_IN };
|
|
char* net_type_lookup[] = { "IN", 0 };
|
|
|
|
inline bool parse_net_type(char*& s, int& network)
|
|
{ return parse_type(s,network,net_type_lookup,"net type"); }
|
|
|
|
// enum { AT_NONE=0, AT_V4, AT_V6 };
|
|
char* addr_type_lookup[] = { "IP4", "IP6", 0 };
|
|
|
|
inline bool parse_addr_type(char*& s, int& addr_t)
|
|
{ return parse_type(s,addr_t,addr_type_lookup,"address type"); }
|
|
|
|
|
|
|
|
inline char* get_next_line(char* s)
|
|
{
|
|
char* next_line=s;
|
|
|
|
// search for next line
|
|
while( *next_line != '\0') {
|
|
|
|
if(*next_line == 13){
|
|
*next_line = '\0';
|
|
next_line += 2;
|
|
break;
|
|
}
|
|
else if(*next_line == 10){
|
|
*(next_line++) = '\0';
|
|
break;
|
|
}
|
|
next_line++;
|
|
}
|
|
return next_line;
|
|
}
|
|
|
|
// WARNING:
|
|
//
|
|
// If you call this function from a handler function,
|
|
// do not forget to set 'next_line' to the proper value
|
|
// (else, it could think EOT has been reached !)
|
|
//
|
|
// Generally: call this function if you know what your doing !
|
|
// Else call 'parse_sdp_line'
|
|
//
|
|
inline bool parse_sdp_line_ex( AmSdp* sdp_msg, char*& s, char discr,
|
|
bool optional,
|
|
bool (*parse_func)(AmSdp*,char*&,char*&),
|
|
bool only_one, char*& next_line)
|
|
{
|
|
while(true){
|
|
|
|
if((*s == '\0') && !optional){
|
|
ERROR("parse_sdp_line : unexpected end of text while looking for '%c'\n",discr);
|
|
return true;
|
|
}
|
|
|
|
if(*s == discr) {
|
|
|
|
if( *(++s) != '=' ){
|
|
ERROR("parse_sdp_line : expected '=' but "
|
|
"<%c> found \n",*s);
|
|
return true;
|
|
}
|
|
s++;
|
|
|
|
next_line = get_next_line(s);
|
|
bool ret=false;
|
|
if(parse_func)
|
|
ret = (*parse_func)(sdp_msg,s,next_line);
|
|
|
|
s = next_line;
|
|
if(only_one || (*s != discr))
|
|
return ret;
|
|
|
|
continue;
|
|
}
|
|
else if(!optional){
|
|
ERROR(" parse_sdp_line : parameter '%c=' was "
|
|
"not found\n",discr);
|
|
return true;
|
|
}
|
|
|
|
// token is optional and has not been found.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// WARNING:
|
|
//
|
|
// Do not touch the handler's third parameter until you known what you are doing.
|
|
//
|
|
inline bool parse_sdp_line( AmSdp* sdp_msg, char*& s, char discr,
|
|
bool optional, bool (*parse_func)(AmSdp*,char*&,char*&),
|
|
bool only_one)
|
|
{
|
|
char* next_line=0;
|
|
return parse_sdp_line_ex(sdp_msg,s,discr,optional,parse_func,only_one,next_line);
|
|
}
|
|
|
|
inline bool parse_string_tok(char*& s, string& res, char sep_char)
|
|
{
|
|
for( ;(*s != '\0') && (*s == ' '); s++);
|
|
char* begin = s;
|
|
|
|
while(*s != '\0'){
|
|
|
|
if(*s == sep_char){
|
|
*(s++) = '\0';
|
|
break;
|
|
}
|
|
s++;
|
|
}
|
|
|
|
res = begin;
|
|
return res.empty();
|
|
}
|
|
|
|
inline bool parse_type(char*& s, int& v, char** ref, const char* name)
|
|
{
|
|
string e;
|
|
v=0;
|
|
if(parse_string_tok(s,e)){
|
|
ERROR(" parse_type : while parsing %s\n",name);
|
|
return true;
|
|
}
|
|
|
|
char** cur = ref;
|
|
while(*cur){
|
|
if(e == *cur){
|
|
v = cur-ref+1;
|
|
break;
|
|
}
|
|
cur++;
|
|
}
|
|
if(!*cur){
|
|
ERROR("unkown %s: <%s>\n",name,e.c_str());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
static bool parse_sdp_version(AmSdp* sdp_msg, char*& s, char*&)
|
|
{
|
|
return str2i(s,sdp_msg->version);
|
|
}
|
|
*/
|
|
|
|
inline bool parse_sdp_connection_struct(SdpConnection& c, char*& s)
|
|
{
|
|
return parse_net_type(s,c.network) ||
|
|
parse_addr_type(s,c.addrType) ||
|
|
parse_string_tok(s,c.address);
|
|
}
|
|
|
|
/*
|
|
static bool parse_sdp_origin(AmSdp* sdp_msg, char*& s, char*&)
|
|
{
|
|
SdpOrigin& o = sdp_msg->origin;
|
|
return parse_string_tok(s,o.user) ||
|
|
str2i(s,o.sessId) ||
|
|
str2i(s,o.sessV) ||
|
|
parse_sdp_connection_struct(o.conn,s);
|
|
parse_net_type(s,o.conn.network) ||
|
|
parse_addr_type(s,o.conn.addrType) ||
|
|
parse_string_tok(s,o.conn.address);
|
|
}
|
|
*/
|
|
|
|
static bool parse_sdp_connection(AmSdp* sdp_msg, char*& s, char*&)
|
|
{
|
|
return parse_sdp_connection_struct(sdp_msg->media.empty() ?
|
|
sdp_msg->conn :
|
|
sdp_msg->media.back().conn,
|
|
s);
|
|
}
|
|
|
|
inline bool parse_codec_list(char*& s, vector<SdpPayload>& payloads)
|
|
{
|
|
unsigned int payload;
|
|
|
|
while(*s != '\0'){
|
|
|
|
if(!str2i(s,payload)){
|
|
|
|
payloads.push_back(SdpPayload(payload));
|
|
}
|
|
else {
|
|
ERROR("invalid payload number found in media line\n");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool parse_sdp_media(AmSdp* sdp_msg, char*& s, char*& next_line)
|
|
{
|
|
SdpMedia m;
|
|
m.dir = SdpMedia::DirBoth;
|
|
|
|
char* old_s = s;
|
|
bool ret = parse_media_type(s,m.type) ||
|
|
str2i(s,m.port) ||
|
|
parse_transport_prot(s,m.transport);
|
|
|
|
if(ret){
|
|
ERROR("while parsing 'm=%s' line.\n",old_s);
|
|
return true;
|
|
}
|
|
|
|
if(!ret && (m.transport == TP_RTPAVP))
|
|
ret = ret || parse_codec_list(s,m.payloads);
|
|
|
|
sdp_msg->media.push_back(m);
|
|
|
|
s = next_line;
|
|
DBG("next_line=<%s>\n",next_line);
|
|
ret = ret
|
|
// Media title
|
|
|| parse_sdp_line(sdp_msg,s,'i',true,NULL)
|
|
// connection information - optional if included at session-level
|
|
|| parse_sdp_line(sdp_msg,s,'c',true,parse_sdp_connection)
|
|
// bandwidth information
|
|
|| parse_sdp_line(sdp_msg,s,'b',true,NULL,false)
|
|
// encryption key
|
|
|| parse_sdp_line(sdp_msg,s,'k',true,NULL)
|
|
// zero or more media attribute lines
|
|
|| parse_sdp_line(sdp_msg,s,'a',true,parse_sdp_attribute,false);
|
|
|
|
if(ret){
|
|
ERROR("while parsing media attributes.\n");
|
|
return true;
|
|
}
|
|
|
|
next_line = get_next_line(s);
|
|
DBG("ret=%i; next_line=<%s>\n",ret,next_line);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool parse_sdp_attribute(AmSdp* sdp_msg, char*& s, char*& next_line)
|
|
{
|
|
DBG("parse_sdp_attribute: s=%s\n",s);
|
|
if(sdp_msg->media.empty()){
|
|
ERROR("While parsing media options: no actual media !\n");
|
|
return true;
|
|
}
|
|
|
|
SdpMedia& media = sdp_msg->media.back();
|
|
|
|
char* sep=0;
|
|
for( sep=s; *sep!='\0' && *sep!=':'; sep++ );
|
|
|
|
if( *sep == ':' ){
|
|
|
|
// attribute definition: 'attribute:value'
|
|
string attr_name(s,int(sep-s));
|
|
char* old_s = s;
|
|
s = sep + 1;
|
|
|
|
if(attr_name == "rtpmap"){
|
|
|
|
//fmt: "<payload type> <encoding name>/<clock rate>[/<encoding parameters>]"
|
|
unsigned int payload_type=0,clock_rate=0;
|
|
string encoding_name, params;
|
|
|
|
bool ret = str2i(s,payload_type)
|
|
|| parse_string_tok(s,encoding_name,'/')
|
|
|| str2i(s,clock_rate,'/');
|
|
|
|
if(ret){
|
|
ERROR("while parsing 'a=%s'\n",old_s);
|
|
return true;
|
|
}
|
|
|
|
parse_string_tok(s,params,'\0');
|
|
DBG("sdp attribute: pt=%u; enc=%s; cr=%u\n",
|
|
payload_type,encoding_name.c_str(),clock_rate);
|
|
|
|
vector<SdpPayload>::iterator pl_it;
|
|
|
|
for( pl_it=media.payloads.begin();
|
|
(pl_it != media.payloads.end())
|
|
&& (pl_it->payload_type != int(payload_type));
|
|
++pl_it);
|
|
|
|
if(pl_it != media.payloads.end()){
|
|
*pl_it = SdpPayload( int(payload_type),
|
|
encoding_name,
|
|
int(clock_rate));
|
|
}
|
|
return ret;
|
|
}
|
|
else if (attr_name == "fmtp") {
|
|
// fmt: "<payload type> parameters" (?)
|
|
// z.b. a=fmtp:101 0-15
|
|
unsigned int payload_type=0;
|
|
string params;
|
|
bool ret = str2i(s, payload_type) || parse_string_tok(s, params, '\0');
|
|
|
|
vector<SdpPayload>::iterator pl_it;
|
|
|
|
for( pl_it=media.payloads.begin();
|
|
(pl_it != media.payloads.end())
|
|
&& (pl_it->payload_type != int(payload_type));
|
|
++pl_it);
|
|
|
|
|
|
if(pl_it != media.payloads.end())
|
|
pl_it->sdp_format_parameters = params;
|
|
|
|
return ret;
|
|
}
|
|
else if(attr_name == "direction"){
|
|
if(!strncmp(s,"active",6/*sizeof("active")*/))
|
|
media.dir = SdpMedia::DirActive;
|
|
else if(!strncmp(s,"passive",7/*sizeof("passive")*/))
|
|
media.dir = SdpMedia::DirPassive;
|
|
else if(!strncmp(s,"both",4/*sizeof("both")*/))
|
|
media.dir = SdpMedia::DirBoth;
|
|
else
|
|
DBG("unknown value for a=direction:%s",s);
|
|
}
|
|
else {
|
|
DBG("unknown attribute definition '%s'\n",old_s);
|
|
}
|
|
}
|
|
else {
|
|
// flag: 'flag_name'
|
|
DBG("flag definition is not yet supported (%s)\n",s);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|