📄 sdp.c
字号:
/* $Id: sdp.c 1266 2007-05-11 15:14:34Z bennylp $ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program 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.
*
* 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. 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 <pjmedia/sdp.h>
#include <pjmedia/errno.h>
#include <pjlib-util/scanner.h>
#include <pj/array.h>
#include <pj/except.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/string.h>
#include <pj/pool.h>
#include <pj/assert.h>
#include <pj/ctype.h>
enum {
SKIP_WS = 0,
SYNTAX_ERROR = 1,
};
#define TOKEN "-.!%*_=`'~"
//#define TOKEN "'`-./:?\"#$&*;=@[]^_`{|}+~!"
#define NTP_OFFSET ((pj_uint32_t)2208988800)
#define THIS_FILE "sdp.c"
typedef struct parse_context
{
pj_status_t last_error;
} parse_context;
/*
* Prototypes for line parser.
*/
static void parse_version(pj_scanner *scanner, parse_context *ctx);
static void parse_origin(pj_scanner *scanner, pjmedia_sdp_session *ses,
parse_context *ctx);
static void parse_time(pj_scanner *scanner, pjmedia_sdp_session *ses,
parse_context *ctx);
static void parse_generic_line(pj_scanner *scanner, pj_str_t *str,
parse_context *ctx);
static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn,
parse_context *ctx);
static pjmedia_sdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner,
parse_context *ctx);
static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
parse_context *ctx);
static void on_scanner_error(pj_scanner *scanner);
/*
* Scanner character specification.
*/
static int is_initialized;
static pj_cis_buf_t cis_buf;
static pj_cis_t cs_digit, cs_token;
static void init_sdp_parser(void)
{
if (is_initialized != 0)
return;
pj_enter_critical_section();
if (is_initialized != 0) {
pj_leave_critical_section();
return;
}
pj_cis_buf_init(&cis_buf);
pj_cis_init(&cis_buf, &cs_token);
pj_cis_add_alpha(&cs_token);
pj_cis_add_num(&cs_token);
pj_cis_add_str(&cs_token, TOKEN);
pj_cis_init(&cis_buf, &cs_digit);
pj_cis_add_num(&cs_digit);
is_initialized = 1;
pj_leave_critical_section();
}
PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create( pj_pool_t *pool,
const char *name,
const pj_str_t *value)
{
pjmedia_sdp_attr *attr;
PJ_ASSERT_RETURN(pool && name, NULL);
attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
pj_strdup2(pool, &attr->name, name);
if (value)
pj_strdup_with_null(pool, &attr->value, value);
else {
attr->value.ptr = NULL;
attr->value.slen = 0;
}
return attr;
}
PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_clone(pj_pool_t *pool,
const pjmedia_sdp_attr *rhs)
{
pjmedia_sdp_attr *attr;
PJ_ASSERT_RETURN(pool && rhs, NULL);
attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
pj_strdup(pool, &attr->name, &rhs->name);
pj_strdup_with_null(pool, &attr->value, &rhs->value);
return attr;
}
PJ_DEF(pjmedia_sdp_attr*)
pjmedia_sdp_attr_find (unsigned count,
pjmedia_sdp_attr *const attr_array[],
const pj_str_t *name,
const pj_str_t *c_fmt)
{
unsigned i;
unsigned c_pt = 0xFFFF;
if (c_fmt)
c_pt = pj_strtoul(c_fmt);
for (i=0; i<count; ++i) {
if (pj_strcmp(&attr_array[i]->name, name) == 0) {
const pjmedia_sdp_attr *a = attr_array[i];
if (c_fmt) {
unsigned pt = (unsigned) pj_strtoul2(&a->value, NULL, 10);
if (pt == c_pt) {
return (pjmedia_sdp_attr*)a;
}
} else
return (pjmedia_sdp_attr*)a;
}
}
return NULL;
}
PJ_DEF(pjmedia_sdp_attr*)
pjmedia_sdp_attr_find2(unsigned count,
pjmedia_sdp_attr *const attr_array[],
const char *c_name,
const pj_str_t *c_fmt)
{
pj_str_t name;
name.ptr = (char*)c_name;
name.slen = pj_ansi_strlen(c_name);
return pjmedia_sdp_attr_find(count, attr_array, &name, c_fmt);
}
PJ_DEF(pj_status_t) pjmedia_sdp_attr_add(unsigned *count,
pjmedia_sdp_attr *attr_array[],
pjmedia_sdp_attr *attr)
{
PJ_ASSERT_RETURN(count && attr_array && attr, PJ_EINVAL);
PJ_ASSERT_RETURN(*count < PJMEDIA_MAX_SDP_ATTR, PJ_ETOOMANY);
attr_array[*count] = attr;
(*count)++;
return PJ_SUCCESS;
}
PJ_DEF(unsigned) pjmedia_sdp_attr_remove_all(unsigned *count,
pjmedia_sdp_attr *attr_array[],
const char *name)
{
unsigned i, removed = 0;
pj_str_t attr_name;
PJ_ASSERT_RETURN(count && attr_array && name, PJ_EINVAL);
attr_name.ptr = (char*)name;
attr_name.slen = pj_ansi_strlen(name);
for (i=0; i<*count; ) {
if (pj_strcmp(&attr_array[i]->name, &attr_name)==0) {
pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*),
*count, i);
--(*count);
++removed;
} else {
++i;
}
}
return removed;
}
PJ_DEF(pj_status_t) pjmedia_sdp_attr_remove( unsigned *count,
pjmedia_sdp_attr *attr_array[],
pjmedia_sdp_attr *attr )
{
unsigned i, removed=0;
PJ_ASSERT_RETURN(count && attr_array && attr, PJ_EINVAL);
for (i=0; i<*count; ) {
if (attr_array[i] == attr) {
pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*),
*count, i);
--(*count);
++removed;
} else {
++i;
}
}
return removed ? PJ_SUCCESS : PJ_ENOTFOUND;
}
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtpmap( const pjmedia_sdp_attr *attr,
pjmedia_sdp_rtpmap *rtpmap)
{
pj_scanner scanner;
pj_str_t token;
pj_status_t status = -1;
char term = 0;
PJ_USE_EXCEPTION;
PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtpmap")==0, PJ_EINVALIDOP);
PJ_ASSERT_RETURN(attr->value.slen != 0, PJMEDIA_SDP_EINATTR);
/* Check if input is null terminated, and null terminate if
* necessary. Unfortunately this may crash the application if
* attribute was allocated from a read-only memory location.
* But this shouldn't happen as attribute's value normally is
* null terminated.
*/
if (attr->value.ptr[attr->value.slen] != 0 &&
attr->value.ptr[attr->value.slen] != '\r')
{
pj_assert(!"Shouldn't happen");
term = attr->value.ptr[attr->value.slen];
attr->value.ptr[attr->value.slen] = '\0';
}
pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);
/* rtpmap sample:
* a=rtpmap:98 L16/16000/2.
*/
/* Init */
rtpmap->pt.slen = rtpmap->param.slen = rtpmap->enc_name.slen = 0;
rtpmap->clock_rate = 0;
/* Parse */
PJ_TRY {
/* Get payload type. */
pj_scan_get(&scanner, &cs_token, &rtpmap->pt);
/* Get encoding name. */
pj_scan_get(&scanner, &cs_token, &rtpmap->enc_name);
/* Expecting '/' after encoding name. */
if (pj_scan_get_char(&scanner) != '/') {
status = PJMEDIA_SDP_EINRTPMAP;
goto on_return;
}
/* Get the clock rate. */
pj_scan_get(&scanner, &cs_digit, &token);
rtpmap->clock_rate = pj_strtoul(&token);
/* Expecting either '/' or EOF */
if (*scanner.curptr == '/') {
pj_scan_get_char(&scanner);
rtpmap->param.ptr = scanner.curptr;
rtpmap->param.slen = scanner.end - scanner.curptr;
} else {
rtpmap->param.slen = 0;
}
status = PJ_SUCCESS;
}
PJ_CATCH_ANY {
status = PJMEDIA_SDP_EINRTPMAP;
}
PJ_END;
on_return:
pj_scan_fini(&scanner);
if (term) {
attr->value.ptr[attr->value.slen] = term;
}
return status;
}
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_fmtp( const pjmedia_sdp_attr *attr,
pjmedia_sdp_fmtp *fmtp)
{
const char *p = attr->value.ptr;
const char *end = attr->value.ptr + attr->value.slen;
pj_str_t token;
PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "fmtp")==0, PJ_EINVALIDOP);
/* fmtp BNF:
* a=fmtp:<format> <format specific parameter>
*/
/* Get format. */
token.ptr = (char*)p;
while (pj_isdigit(*p) && p!=end)
++p;
token.slen = p - token.ptr;
if (token.slen == 0)
return PJMEDIA_SDP_EINFMTP;
fmtp->fmt = token;
/* Expecting space after format. */
if (*p != ' ') return PJMEDIA_SDP_EINFMTP;
/* Get space. */
++p;
/* Set the remaining string as fmtp format parameter. */
fmtp->fmt_param.ptr = (char*)p;
fmtp->fmt_param.slen = end - p;
return PJ_SUCCESS;
}
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtcp(const pjmedia_sdp_attr *attr,
pjmedia_sdp_rtcp_attr *rtcp)
{
pj_scanner scanner;
pj_str_t token;
pj_status_t status = -1;
PJ_USE_EXCEPTION;
PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtcp")==0, PJ_EINVALIDOP);
/* fmtp BNF:
* a=rtcp:<port> [nettype addrtype address]
*/
pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);
/* Init */
rtcp->net_type.slen = rtcp->addr_type.slen = rtcp->addr.slen = 0;
/* Parse */
PJ_TRY {
/* Get the port */
pj_scan_get(&scanner, &cs_token, &token);
rtcp->port = pj_strtoul(&token);
/* Have address? */
if (!pj_scan_is_eof(&scanner)) {
/* Get network type */
pj_scan_get(&scanner, &cs_token, &rtcp->net_type);
/* Get address type */
pj_scan_get(&scanner, &cs_token, &rtcp->addr_type);
/* Get the address */
pj_scan_get(&scanner, &cs_token, &rtcp->addr);
}
status = PJ_SUCCESS;
}
PJ_CATCH_ANY {
status = PJMEDIA_SDP_EINRTCP;
}
PJ_END;
pj_scan_fini(&scanner);
return status;
}
PJ_DEF(pj_status_t) pjmedia_sdp_attr_to_rtpmap(pj_pool_t *pool,
const pjmedia_sdp_attr *attr,
pjmedia_sdp_rtpmap **p_rtpmap)
{
PJ_ASSERT_RETURN(pool && attr && p_rtpmap, PJ_EINVAL);
*p_rtpmap = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_rtpmap);
PJ_ASSERT_RETURN(*p_rtpmap, PJ_ENOMEM);
return pjmedia_sdp_attr_get_rtpmap(attr, *p_rtpmap);
}
PJ_DEF(pj_status_t) pjmedia_sdp_rtpmap_to_attr(pj_pool_t *pool,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -