📄 sdp.c
字号:
ctx->last_error = PJMEDIA_SDP_EINSDP;
/* check equal sign */
if (*(scanner->curptr+1) != '=') {
on_scanner_error(scanner);
return;
}
/* x= */
pj_scan_advance_n(scanner, 2, SKIP_WS);
/* get anything until newline (including whitespaces). */
pj_scan_get_until_ch(scanner, '\r', str);
/* newline. */
pj_scan_get_newline(scanner);
}
static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn,
parse_context *ctx)
{
ctx->last_error = PJMEDIA_SDP_EINCONN;
/* c= */
pj_scan_advance_n(scanner, 2, SKIP_WS);
/* network-type */
pj_scan_get_until_ch(scanner, ' ', &conn->net_type);
pj_scan_get_char(scanner);
/* addr-type */
pj_scan_get_until_ch(scanner, ' ', &conn->addr_type);
pj_scan_get_char(scanner);
/* address. */
pj_scan_get_until_chr(scanner, " \t\r", &conn->addr);
/* We've got what we're looking for, skip anything until newline */
pj_scan_skip_line(scanner);
}
static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
parse_context *ctx)
{
pj_str_t str;
ctx->last_error = PJMEDIA_SDP_EINMEDIA;
/* check the equal sign */
if (*(scanner->curptr+1) != '=') {
on_scanner_error(scanner);
return;
}
/* m= */
pj_scan_advance_n(scanner, 2, SKIP_WS);
/* type */
pj_scan_get_until_ch(scanner, ' ', &med->desc.media);
pj_scan_get_char(scanner);
/* port */
pj_scan_get(scanner, &cs_token, &str);
med->desc.port = (unsigned short)pj_strtoul(&str);
if (*scanner->curptr == '/') {
/* port count */
pj_scan_get_char(scanner);
pj_scan_get(scanner, &cs_token, &str);
med->desc.port_count = pj_strtoul(&str);
} else {
med->desc.port_count = 0;
}
if (pj_scan_get_char(scanner) != ' ') {
PJ_THROW(SYNTAX_ERROR);
}
/* transport */
pj_scan_get_until_ch(scanner, ' ', &med->desc.transport);
/* format list */
med->desc.fmt_count = 0;
while (*scanner->curptr == ' ') {
pj_scan_get_char(scanner);
/* Check again for the end of the line */
if ((*scanner->curptr == '\r') || (*scanner->curptr == '\n'))
break;
pj_scan_get(scanner, &cs_token, &med->desc.fmt[med->desc.fmt_count++]);
}
/* We've got what we're looking for, skip anything until newline */
pj_scan_skip_line(scanner);
}
static void on_scanner_error(pj_scanner *scanner)
{
PJ_UNUSED_ARG(scanner);
PJ_THROW(SYNTAX_ERROR);
}
static pjmedia_sdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner,
parse_context *ctx)
{
pjmedia_sdp_attr *attr;
ctx->last_error = PJMEDIA_SDP_EINATTR;
attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
/* check equal sign */
if (*(scanner->curptr+1) != '=') {
on_scanner_error(scanner);
return NULL;
}
/* skip a= */
pj_scan_advance_n(scanner, 2, SKIP_WS);
/* get attr name. */
pj_scan_get(scanner, &cs_token, &attr->name);
if (*scanner->curptr != '\r' && *scanner->curptr != '\n') {
/* skip ':' if present. */
if (*scanner->curptr == ':')
pj_scan_get_char(scanner);
/* get value */
if (*scanner->curptr != '\r') {
pj_scan_get_until_ch(scanner, '\r', &attr->value);
} else {
attr->value.ptr = NULL;
attr->value.slen = 0;
}
} else {
attr->value.ptr = NULL;
attr->value.slen = 0;
}
/* We've got what we're looking for, skip anything until newline */
pj_scan_skip_line(scanner);
return attr;
}
/*
* Parse SDP message.
*/
PJ_DEF(pj_status_t) pjmedia_sdp_parse( pj_pool_t *pool,
char *buf, pj_size_t len,
pjmedia_sdp_session **p_sdp)
{
pj_scanner scanner;
pjmedia_sdp_session *session;
pjmedia_sdp_media *media = NULL;
pjmedia_sdp_attr *attr;
pjmedia_sdp_conn *conn;
pj_str_t dummy;
int cur_name = 254;
parse_context ctx;
PJ_USE_EXCEPTION;
ctx.last_error = PJ_SUCCESS;
init_sdp_parser();
pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
session = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
PJ_ASSERT_RETURN(session != NULL, PJ_ENOMEM);
PJ_TRY {
while (!pj_scan_is_eof(&scanner)) {
cur_name = *scanner.curptr;
switch (cur_name) {
case 'a':
attr = parse_attr(pool, &scanner, &ctx);
if (attr) {
if (media) {
media->attr[media->attr_count++] = attr;
} else {
session->attr[session->attr_count++] = attr;
}
}
break;
case 'o':
parse_origin(&scanner, session, &ctx);
break;
case 's':
parse_generic_line(&scanner, &session->name, &ctx);
break;
case 'c':
conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
parse_connection_info(&scanner, conn, &ctx);
if (media) {
media->conn = conn;
} else {
session->conn = conn;
}
break;
case 't':
parse_time(&scanner, session, &ctx);
break;
case 'm':
media = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
parse_media(&scanner, media, &ctx);
session->media[ session->media_count++ ] = media;
break;
case 'v':
parse_version(&scanner, &ctx);
break;
case 13:
/* Allow empty newline at the end of the message */
pj_scan_get_char(&scanner);
/* Continue below */
case 10:
pj_scan_get_char(&scanner);
if (!pj_scan_is_eof(&scanner)) {
on_scanner_error(&scanner);
}
break;
default:
if (cur_name >= 'a' && cur_name <= 'z')
parse_generic_line(&scanner, &dummy, &ctx);
else {
ctx.last_error = PJMEDIA_SDP_EINSDP;
on_scanner_error(&scanner);
}
break;
}
}
ctx.last_error = PJ_SUCCESS;
}
PJ_CATCH_ANY {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(ctx.last_error, errmsg, sizeof(errmsg));
PJ_LOG(4, (THIS_FILE, "Error parsing SDP in line %d col %d: %s",
scanner.line, pj_scan_get_col(&scanner),
errmsg));
session = NULL;
pj_assert(ctx.last_error != PJ_SUCCESS);
}
PJ_END;
pj_scan_fini(&scanner);
*p_sdp = session;
return ctx.last_error;
}
/*
* Print SDP description.
*/
PJ_DEF(int) pjmedia_sdp_print( const pjmedia_sdp_session *desc,
char *buf, pj_size_t size)
{
return print_session(desc, buf, size);
}
/*
* Clone session
*/
PJ_DEF(pjmedia_sdp_session*)
pjmedia_sdp_session_clone( pj_pool_t *pool,
const pjmedia_sdp_session *rhs)
{
pjmedia_sdp_session *sess;
unsigned i;
PJ_ASSERT_RETURN(pool && rhs, NULL);
sess = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
PJ_ASSERT_RETURN(sess != NULL, NULL);
/* Clone origin line. */
pj_strdup(pool, &sess->origin.user, &rhs->origin.user);
sess->origin.id = rhs->origin.id;
sess->origin.version = rhs->origin.version;
pj_strdup(pool, &sess->origin.net_type, &rhs->origin.net_type);
pj_strdup(pool, &sess->origin.addr_type, &rhs->origin.addr_type);
pj_strdup(pool, &sess->origin.addr, &rhs->origin.addr);
/* Clone subject line. */
pj_strdup(pool, &sess->name, &rhs->name);
/* Clone connection line */
if (rhs->conn) {
sess->conn = pjmedia_sdp_conn_clone(pool, rhs->conn);
PJ_ASSERT_RETURN(sess->conn != NULL, NULL);
}
/* Clone time line. */
sess->time.start = rhs->time.start;
sess->time.stop = rhs->time.stop;
/* Duplicate session attributes. */
sess->attr_count = rhs->attr_count;
for (i=0; i<rhs->attr_count; ++i) {
sess->attr[i] = pjmedia_sdp_attr_clone(pool, rhs->attr[i]);
}
/* Duplicate media descriptors. */
sess->media_count = rhs->media_count;
for (i=0; i<rhs->media_count; ++i) {
sess->media[i] = pjmedia_sdp_media_clone(pool, rhs->media[i]);
}
return sess;
}
#define CHECK(exp,ret) do { \
/*pj_assert(exp);*/ \
if (!(exp)) \
return ret; \
} while (0)
/* Validate SDP connetion info. */
static pj_status_t validate_sdp_conn(const pjmedia_sdp_conn *c)
{
CHECK( c, PJ_EINVAL);
CHECK( pj_strcmp2(&c->net_type, "IN")==0, PJMEDIA_SDP_EINCONN);
CHECK( pj_strcmp2(&c->addr_type, "IP4")==0 ||
pj_strcmp2(&c->addr_type, "IP6")==0,
PJMEDIA_SDP_EINCONN);
CHECK( c->addr.slen != 0, PJMEDIA_SDP_EINCONN);
return PJ_SUCCESS;
}
/* Validate SDP session descriptor. */
PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp)
{
unsigned i;
const pj_str_t STR_RTPMAP = { "rtpmap", 6 };
CHECK( sdp != NULL, PJ_EINVAL);
/* Validate origin line. */
CHECK( sdp->origin.user.slen != 0, PJMEDIA_SDP_EINORIGIN);
CHECK( pj_strcmp2(&sdp->origin.net_type, "IN")==0,
PJMEDIA_SDP_EINORIGIN);
CHECK( pj_strcmp2(&sdp->origin.addr_type, "IP4")==0 ||
pj_strcmp2(&sdp->origin.addr_type, "IP6")==0,
PJMEDIA_SDP_EINORIGIN);
CHECK( sdp->origin.addr.slen != 0, PJMEDIA_SDP_EINORIGIN);
/* Validate subject line. */
CHECK( sdp->name.slen != 0, PJMEDIA_SDP_EINNAME);
/* Ignore start and stop time. */
/* If session level connection info is present, validate it. */
if (sdp->conn) {
pj_status_t status = validate_sdp_conn(sdp->conn);
if (status != PJ_SUCCESS)
return status;
}
/* Validate each media. */
for (i=0; i<sdp->media_count; ++i) {
const pjmedia_sdp_media *m = sdp->media[i];
unsigned j;
/* Validate the m= line. */
CHECK( m->desc.media.slen != 0, PJMEDIA_SDP_EINMEDIA);
CHECK( m->desc.transport.slen != 0, PJMEDIA_SDP_EINMEDIA);
CHECK( m->desc.fmt_count != 0 || m->desc.port==0, PJMEDIA_SDP_ENOFMT);
/* If media level connection info is present, validate it. */
if (m->conn) {
pj_status_t status = validate_sdp_conn(m->conn);
if (status != PJ_SUCCESS)
return status;
}
/* If media doesn't have connection info, then connection info
* must be present in the session.
*/
if (m->conn == NULL) {
if (sdp->conn == NULL)
return PJMEDIA_SDP_EMISSINGCONN;
}
/* Verify payload type. */
for (j=0; j<m->desc.fmt_count; ++j) {
/* Arrgh noo!! Payload type can be non-numeric!!
* RTC based programs sends "null" for instant messaging!
*/
if (pj_isdigit(*m->desc.fmt[j].ptr)) {
unsigned pt = pj_strtoul(&m->desc.fmt[j]);
/* Payload type is between 0 and 127.
*/
CHECK( pt <= 127, PJMEDIA_SDP_EINPT);
/* If port is not zero, then for each dynamic payload type, an
* rtpmap attribute must be specified.
*/
if (m->desc.port != 0 && pt >= 96) {
const pjmedia_sdp_attr *a;
a = pjmedia_sdp_media_find_attr(m, &STR_RTPMAP,
&m->desc.fmt[j]);
CHECK( a != NULL, PJMEDIA_SDP_EMISSINGRTPMAP);
}
}
}
}
/* Looks good. */
return PJ_SUCCESS;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -