1
/* $Id: sdp.c 4367 2013-02-21 20:49:19Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjmedia/sdp.h>
21
#include <pjmedia/errno.h>
22
#include <pjlib-util/scanner.h>
24
#include <pj/except.h>
27
#include <pj/string.h>
29
#include <pj/assert.h>
37
// New token definition from RFC 4566 (SDP)
38
#define TOKEN "!#$%&'*+-.^_`{|}~"
39
//#define TOKEN "-.!%*_=`'~"
40
//#define TOKEN "'`-./:?\"#$&*;=@[]^_`{|}+~!"
41
#define NTP_OFFSET ((pj_uint32_t)2208988800)
42
#define THIS_FILE "sdp.c"
44
typedef struct parse_context
46
pj_status_t last_error;
51
* Prototypes for line parser.
53
static void parse_version(pj_scanner *scanner, parse_context *ctx);
54
static void parse_origin(pj_scanner *scanner, pjmedia_sdp_session *ses,
56
static void parse_time(pj_scanner *scanner, pjmedia_sdp_session *ses,
58
static void parse_generic_line(pj_scanner *scanner, pj_str_t *str,
60
static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn,
62
static void parse_bandwidth_info(pj_scanner *scanner, pjmedia_sdp_bandw *bandw,
64
static pjmedia_sdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner,
66
static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
68
static void on_scanner_error(pj_scanner *scanner);
71
* Scanner character specification.
73
static int is_initialized;
74
static pj_cis_buf_t cis_buf;
75
static pj_cis_t cs_digit, cs_token;
77
static void init_sdp_parser(void)
79
if (is_initialized != 0)
82
pj_enter_critical_section();
84
if (is_initialized != 0) {
85
pj_leave_critical_section();
89
pj_cis_buf_init(&cis_buf);
91
pj_cis_init(&cis_buf, &cs_token);
92
pj_cis_add_alpha(&cs_token);
93
pj_cis_add_num(&cs_token);
94
pj_cis_add_str(&cs_token, TOKEN);
96
pj_cis_init(&cis_buf, &cs_digit);
97
pj_cis_add_num(&cs_digit);
100
pj_leave_critical_section();
103
PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create( pj_pool_t *pool,
105
const pj_str_t *value)
107
pjmedia_sdp_attr *attr;
109
PJ_ASSERT_RETURN(pool && name, NULL);
111
attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
112
pj_strdup2(pool, &attr->name, name);
115
pj_strdup_with_null(pool, &attr->value, value);
117
attr->value.ptr = NULL;
118
attr->value.slen = 0;
124
PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_clone(pj_pool_t *pool,
125
const pjmedia_sdp_attr *rhs)
127
pjmedia_sdp_attr *attr;
129
PJ_ASSERT_RETURN(pool && rhs, NULL);
131
attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
133
pj_strdup(pool, &attr->name, &rhs->name);
134
pj_strdup_with_null(pool, &attr->value, &rhs->value);
139
PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_find (unsigned count,
140
pjmedia_sdp_attr *const attr_array[],
141
const pj_str_t *name,
142
const pj_str_t *c_fmt)
145
unsigned c_pt = 0xFFFF;
148
c_pt = pj_strtoul(c_fmt);
150
for (i=0; i<count; ++i) {
151
if (pj_strcmp(&attr_array[i]->name, name) == 0) {
152
const pjmedia_sdp_attr *a = attr_array[i];
154
unsigned pt = (unsigned) pj_strtoul2(&a->value, NULL, 10);
156
return (pjmedia_sdp_attr*)a;
159
return (pjmedia_sdp_attr*)a;
165
PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_find2(unsigned count,
166
pjmedia_sdp_attr *const attr_array[],
168
const pj_str_t *c_fmt)
172
name.ptr = (char*)c_name;
173
name.slen = pj_ansi_strlen(c_name);
175
return pjmedia_sdp_attr_find(count, attr_array, &name, c_fmt);
180
PJ_DEF(pj_status_t) pjmedia_sdp_attr_add(unsigned *count,
181
pjmedia_sdp_attr *attr_array[],
182
pjmedia_sdp_attr *attr)
184
PJ_ASSERT_RETURN(count && attr_array && attr, PJ_EINVAL);
185
PJ_ASSERT_RETURN(*count < PJMEDIA_MAX_SDP_ATTR, PJ_ETOOMANY);
187
attr_array[*count] = attr;
194
PJ_DEF(unsigned) pjmedia_sdp_attr_remove_all(unsigned *count,
195
pjmedia_sdp_attr *attr_array[],
198
unsigned i, removed = 0;
201
PJ_ASSERT_RETURN(count && attr_array && name, PJ_EINVAL);
203
attr_name.ptr = (char*)name;
204
attr_name.slen = pj_ansi_strlen(name);
206
for (i=0; i<*count; ) {
207
if (pj_strcmp(&attr_array[i]->name, &attr_name)==0) {
208
pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*),
221
PJ_DEF(pj_status_t) pjmedia_sdp_attr_remove( unsigned *count,
222
pjmedia_sdp_attr *attr_array[],
223
pjmedia_sdp_attr *attr )
225
unsigned i, removed=0;
227
PJ_ASSERT_RETURN(count && attr_array && attr, PJ_EINVAL);
229
for (i=0; i<*count; ) {
230
if (attr_array[i] == attr) {
231
pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*),
240
return removed ? PJ_SUCCESS : PJ_ENOTFOUND;
244
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtpmap( const pjmedia_sdp_attr *attr,
245
pjmedia_sdp_rtpmap *rtpmap)
249
pj_status_t status = -1;
253
PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtpmap")==0, PJ_EINVALIDOP);
255
PJ_ASSERT_RETURN(attr->value.slen != 0, PJMEDIA_SDP_EINATTR);
259
/* Check if input is null terminated, and null terminate if
260
* necessary. Unfortunately this may crash the application if
261
* attribute was allocated from a read-only memory location.
262
* But this shouldn't happen as attribute's value normally is
265
if (attr->value.ptr[attr->value.slen] != 0 &&
266
attr->value.ptr[attr->value.slen] != '\r' &&
267
attr->value.ptr[attr->value.slen] != '\n')
269
pj_assert(!"Shouldn't happen");
270
term = attr->value.ptr[attr->value.slen];
271
attr->value.ptr[attr->value.slen] = '\0';
274
pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
275
PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);
278
* a=rtpmap:98 L16/16000/2.
282
rtpmap->pt.slen = rtpmap->param.slen = rtpmap->enc_name.slen = 0;
283
rtpmap->clock_rate = 0;
288
/* Get payload type. */
289
pj_scan_get(&scanner, &cs_token, &rtpmap->pt);
292
/* Get encoding name. */
293
pj_scan_get(&scanner, &cs_token, &rtpmap->enc_name);
295
/* Expecting '/' after encoding name. */
296
if (pj_scan_get_char(&scanner) != '/') {
297
status = PJMEDIA_SDP_EINRTPMAP;
302
/* Get the clock rate. */
303
pj_scan_get(&scanner, &cs_digit, &token);
304
rtpmap->clock_rate = pj_strtoul(&token);
306
/* Expecting either '/' or EOF */
307
if (*scanner.curptr == '/') {
308
pj_scan_get_char(&scanner);
309
rtpmap->param.ptr = scanner.curptr;
310
rtpmap->param.slen = scanner.end - scanner.curptr;
312
rtpmap->param.slen = 0;
318
status = PJMEDIA_SDP_EINRTPMAP;
324
pj_scan_fini(&scanner);
326
attr->value.ptr[attr->value.slen] = term;
331
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_fmtp( const pjmedia_sdp_attr *attr,
332
pjmedia_sdp_fmtp *fmtp)
334
const char *p = attr->value.ptr;
335
const char *end = attr->value.ptr + attr->value.slen;
338
PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "fmtp")==0, PJ_EINVALIDOP);
341
* a=fmtp:<format> <format specific parameter>
345
token.ptr = (char*)p;
346
while (pj_isdigit(*p) && p!=end)
348
token.slen = p - token.ptr;
350
return PJMEDIA_SDP_EINFMTP;
354
/* Expecting space after format. */
355
if (*p != ' ') return PJMEDIA_SDP_EINFMTP;
360
/* Set the remaining string as fmtp format parameter. */
361
fmtp->fmt_param.ptr = (char*)p;
362
fmtp->fmt_param.slen = end - p;
368
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtcp(const pjmedia_sdp_attr *attr,
369
pjmedia_sdp_rtcp_attr *rtcp)
373
pj_status_t status = -1;
376
PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtcp")==0, PJ_EINVALIDOP);
381
* a=rtcp:<port> [nettype addrtype address]
384
pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
385
PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);
388
rtcp->net_type.slen = rtcp->addr_type.slen = rtcp->addr.slen = 0;
394
pj_scan_get(&scanner, &cs_token, &token);
395
rtcp->port = pj_strtoul(&token);
398
if (!pj_scan_is_eof(&scanner)) {
400
/* Get network type */
401
pj_scan_get(&scanner, &cs_token, &rtcp->net_type);
403
/* Get address type */
404
pj_scan_get(&scanner, &cs_token, &rtcp->addr_type);
406
/* Get the address */
407
pj_scan_get(&scanner, &cs_token, &rtcp->addr);
415
status = PJMEDIA_SDP_EINRTCP;
419
pj_scan_fini(&scanner);
424
PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_rtcp(pj_pool_t *pool,
425
const pj_sockaddr *a)
428
ATTR_LEN = PJ_INET6_ADDRSTRLEN+16
430
pjmedia_sdp_attr *attr;
432
attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
433
attr->name = pj_str("rtcp");
434
attr->value.ptr = (char*) pj_pool_alloc(pool, ATTR_LEN);
435
if (a->addr.sa_family == pj_AF_INET()) {
437
pj_ansi_snprintf(attr->value.ptr, ATTR_LEN,
439
pj_ntohs(a->ipv4.sin_port),
440
pj_inet_ntoa(a->ipv4.sin_addr));
441
} else if (a->addr.sa_family == pj_AF_INET6()) {
442
char tmp_addr[PJ_INET6_ADDRSTRLEN];
444
pj_ansi_snprintf(attr->value.ptr, ATTR_LEN,
446
pj_sockaddr_get_port(a),
447
pj_sockaddr_print(a, tmp_addr,
448
sizeof(tmp_addr), 0));
451
pj_assert(!"Unsupported address family");
459
PJ_DEF(pj_status_t) pjmedia_sdp_attr_to_rtpmap(pj_pool_t *pool,
460
const pjmedia_sdp_attr *attr,
461
pjmedia_sdp_rtpmap **p_rtpmap)
463
PJ_ASSERT_RETURN(pool && attr && p_rtpmap, PJ_EINVAL);
465
*p_rtpmap = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_rtpmap);
466
PJ_ASSERT_RETURN(*p_rtpmap, PJ_ENOMEM);
468
return pjmedia_sdp_attr_get_rtpmap(attr, *p_rtpmap);
472
PJ_DEF(pj_status_t) pjmedia_sdp_rtpmap_to_attr(pj_pool_t *pool,
473
const pjmedia_sdp_rtpmap *rtpmap,
474
pjmedia_sdp_attr **p_attr)
476
pjmedia_sdp_attr *attr;
480
/* Check arguments. */
481
PJ_ASSERT_RETURN(pool && rtpmap && p_attr, PJ_EINVAL);
483
/* Check that mandatory attributes are specified. */
484
PJ_ASSERT_RETURN(rtpmap->enc_name.slen && rtpmap->clock_rate,
485
PJMEDIA_SDP_EINRTPMAP);
488
attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
489
PJ_ASSERT_RETURN(attr != NULL, PJ_ENOMEM);
491
attr->name.ptr = "rtpmap";
494
/* Format: ":pt enc_name/clock_rate[/param]" */
495
len = pj_ansi_snprintf(tempbuf, sizeof(tempbuf),
496
"%.*s %.*s/%u%s%.*s",
497
(int)rtpmap->pt.slen,
499
(int)rtpmap->enc_name.slen,
500
rtpmap->enc_name.ptr,
502
(rtpmap->param.slen ? "/" : ""),
503
(int)rtpmap->param.slen,
506
if (len < 1 || len > (int)sizeof(tempbuf))
507
return PJMEDIA_SDP_ERTPMAPTOOLONG;
509
attr->value.slen = len;
510
attr->value.ptr = (char*) pj_pool_alloc(pool, attr->value.slen+1);
511
pj_memcpy(attr->value.ptr, tempbuf, attr->value.slen+1);
518
static int print_connection_info( pjmedia_sdp_conn *c, char *buf, int len)
522
printed = pj_ansi_snprintf(buf, len, "c=%.*s %.*s %.*s\r\n",
523
(int)c->net_type.slen,
525
(int)c->addr_type.slen,
529
if (printed < 1 || printed > len)
536
PJ_DEF(pjmedia_sdp_conn*) pjmedia_sdp_conn_clone (pj_pool_t *pool,
537
const pjmedia_sdp_conn *rhs)
539
pjmedia_sdp_conn *c = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_conn);
542
if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL;
543
if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL;
544
if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL;
549
PJ_DEF(pjmedia_sdp_bandw*)
550
pjmedia_sdp_bandw_clone (pj_pool_t *pool,
551
const pjmedia_sdp_bandw *rhs)
553
pjmedia_sdp_bandw *b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
556
if (!pj_strdup (pool, &b->modifier, &rhs->modifier)) return NULL;
557
b->value = rhs->value;
562
static pj_ssize_t print_bandw(const pjmedia_sdp_bandw *bandw,
563
char *buf, pj_size_t len)
567
if ((int)len < bandw->modifier.slen + 10 + 5)
572
pj_memcpy(p, bandw->modifier.ptr, bandw->modifier.slen);
573
p += bandw->modifier.slen;
575
p += pj_utoa(bandw->value, p);
582
static pj_ssize_t print_attr(const pjmedia_sdp_attr *attr,
583
char *buf, pj_size_t len)
587
if ((int)len < attr->name.slen + attr->value.slen + 10)
592
pj_memcpy(p, attr->name.ptr, attr->name.slen);
593
p += attr->name.slen;
596
if (attr->value.slen) {
598
pj_memcpy(p, attr->value.ptr, attr->value.slen);
599
p += attr->value.slen;
607
static int print_media_desc( pjmedia_sdp_media *m, char *buf, int len)
614
/* check length for the "m=" line. */
615
if (len < m->desc.media.slen+m->desc.transport.slen+12+24) {
620
pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen);
621
p += m->desc.media.slen;
623
printed = pj_utoa(m->desc.port, p);
625
if (m->desc.port_count > 1) {
627
printed = pj_utoa(m->desc.port_count, p);
631
pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen);
632
p += m->desc.transport.slen;
633
for (i=0; i<m->desc.fmt_count; ++i) {
635
pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen);
636
p += m->desc.fmt[i].slen;
641
/* print connection info, if present. */
643
printed = print_connection_info(m->conn, p, end-p);
650
/* print optional bandwidth info. */
651
for (i=0; i<m->bandw_count; ++i) {
652
printed = print_bandw(m->bandw[i], p, end-p);
659
/* print attributes. */
660
for (i=0; i<m->attr_count; ++i) {
661
printed = print_attr(m->attr[i], p, end-p);
671
PJ_DEF(pjmedia_sdp_media*) pjmedia_sdp_media_clone(
673
const pjmedia_sdp_media *rhs)
676
pjmedia_sdp_media *m = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_media);
677
PJ_ASSERT_RETURN(m != NULL, NULL);
679
pj_strdup (pool, &m->desc.media, &rhs->desc.media);
680
m->desc.port = rhs->desc.port;
681
m->desc.port_count = rhs->desc.port_count;
682
pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
683
m->desc.fmt_count = rhs->desc.fmt_count;
684
for (i=0; i<rhs->desc.fmt_count; ++i)
685
pj_strdup(pool, &m->desc.fmt[i], &rhs->desc.fmt[i]);
688
m->conn = pjmedia_sdp_conn_clone (pool, rhs->conn);
689
PJ_ASSERT_RETURN(m->conn != NULL, NULL);
694
m->bandw_count = rhs->bandw_count;
695
for (i=0; i < rhs->bandw_count; ++i) {
696
m->bandw[i] = pjmedia_sdp_bandw_clone (pool, rhs->bandw[i]);
697
PJ_ASSERT_RETURN(m->bandw[i] != NULL, NULL);
700
m->attr_count = rhs->attr_count;
701
for (i=0; i < rhs->attr_count; ++i) {
702
m->attr[i] = pjmedia_sdp_attr_clone (pool, rhs->attr[i]);
703
PJ_ASSERT_RETURN(m->attr[i] != NULL, NULL);
709
PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_media_find_attr(
710
const pjmedia_sdp_media *m,
711
const pj_str_t *name, const pj_str_t *fmt)
713
PJ_ASSERT_RETURN(m && name, NULL);
714
return pjmedia_sdp_attr_find(m->attr_count, m->attr, name, fmt);
719
PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_media_find_attr2(
720
const pjmedia_sdp_media *m,
721
const char *name, const pj_str_t *fmt)
723
PJ_ASSERT_RETURN(m && name, NULL);
724
return pjmedia_sdp_attr_find2(m->attr_count, m->attr, name, fmt);
728
PJ_DEF(pj_status_t) pjmedia_sdp_media_add_attr( pjmedia_sdp_media *m,
729
pjmedia_sdp_attr *attr)
731
return pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
734
PJ_DEF(pj_status_t) pjmedia_sdp_session_add_attr(pjmedia_sdp_session *s,
735
pjmedia_sdp_attr *attr)
737
return pjmedia_sdp_attr_add(&s->attr_count, s->attr, attr);
740
PJ_DEF(unsigned) pjmedia_sdp_media_remove_all_attr(pjmedia_sdp_media *m,
743
return pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, name);
746
PJ_DEF(pj_status_t) pjmedia_sdp_media_remove_attr(pjmedia_sdp_media *m,
747
pjmedia_sdp_attr *attr)
749
return pjmedia_sdp_attr_remove(&m->attr_count, m->attr, attr);
752
static int print_session(const pjmedia_sdp_session *ses,
753
char *buf, pj_ssize_t len)
760
/* Check length for v= and o= lines. */
762
2+ses->origin.user.slen+18+
763
ses->origin.net_type.slen+ses->origin.addr.slen + 2)
768
/* SDP version (v= line) */
769
pj_memcpy(p, "v=0\r\n", 5);
772
/* Owner (o=) line. */
775
pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
776
p += ses->origin.user.slen;
778
printed = pj_utoa(ses->origin.id, p);
781
printed = pj_utoa(ses->origin.version, p);
784
pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
785
p += ses->origin.net_type.slen;
787
pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
788
p += ses->origin.addr_type.slen;
790
pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
791
p += ses->origin.addr.slen;
795
/* Session name (s=) line. */
796
if ((end-p) < 8+ses->name.slen) {
801
pj_memcpy(p, ses->name.ptr, ses->name.slen);
806
/* Connection line (c=) if exist. */
808
printed = print_connection_info(ses->conn, p, end-p);
815
/* print optional bandwidth info. */
816
for (i=0; i<ses->bandw_count; ++i) {
817
printed = print_bandw(ses->bandw[i], p, end-p);
830
printed = pj_utoa(ses->time.start, p);
833
printed = pj_utoa(ses->time.stop, p);
838
/* Print all attribute (a=) lines. */
839
for (i=0; i<ses->attr_count; ++i) {
840
printed = print_attr(ses->attr[i], p, end-p);
847
/* Print media (m=) lines. */
848
for (i=0; i<ses->media_count; ++i) {
849
printed = print_media_desc(ses->media[i], p, end-p);
859
/******************************************************************************
863
static void parse_version(pj_scanner *scanner, parse_context *ctx)
865
ctx->last_error = PJMEDIA_SDP_EINVER;
867
/* check equal sign */
868
if (*(scanner->curptr+1) != '=') {
869
on_scanner_error(scanner);
873
/* check version is 0 */
874
if (*(scanner->curptr+2) != '0') {
875
on_scanner_error(scanner);
879
/* We've got what we're looking for, skip anything until newline */
880
pj_scan_skip_line(scanner);
883
static void parse_origin(pj_scanner *scanner, pjmedia_sdp_session *ses,
888
ctx->last_error = PJMEDIA_SDP_EINORIGIN;
890
/* check equal sign */
891
if (*(scanner->curptr+1) != '=') {
892
on_scanner_error(scanner);
897
pj_scan_advance_n(scanner, 2, SKIP_WS);
900
pj_scan_get_until_ch(scanner, ' ', &ses->origin.user);
901
pj_scan_get_char(scanner);
904
pj_scan_get_until_ch(scanner, ' ', &str);
905
ses->origin.id = pj_strtoul(&str);
906
pj_scan_get_char(scanner);
909
pj_scan_get_until_ch(scanner, ' ', &str);
910
ses->origin.version = pj_strtoul(&str);
911
pj_scan_get_char(scanner);
914
pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type);
915
pj_scan_get_char(scanner);
918
pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type);
919
pj_scan_get_char(scanner);
922
pj_scan_get_until_chr(scanner, " \t\r\n", &ses->origin.addr);
924
/* We've got what we're looking for, skip anything until newline */
925
pj_scan_skip_line(scanner);
929
static void parse_time(pj_scanner *scanner, pjmedia_sdp_session *ses,
934
ctx->last_error = PJMEDIA_SDP_EINTIME;
936
/* check equal sign */
937
if (*(scanner->curptr+1) != '=') {
938
on_scanner_error(scanner);
943
pj_scan_advance_n(scanner, 2, SKIP_WS);
946
pj_scan_get_until_ch(scanner, ' ', &str);
947
ses->time.start = pj_strtoul(&str);
949
pj_scan_get_char(scanner);
952
pj_scan_get_until_chr(scanner, " \t\r\n", &str);
953
ses->time.stop = pj_strtoul(&str);
955
/* We've got what we're looking for, skip anything until newline */
956
pj_scan_skip_line(scanner);
959
static void parse_generic_line(pj_scanner *scanner, pj_str_t *str,
962
ctx->last_error = PJMEDIA_SDP_EINSDP;
964
/* check equal sign */
965
if (*(scanner->curptr+1) != '=') {
966
on_scanner_error(scanner);
971
pj_scan_advance_n(scanner, 2, SKIP_WS);
973
/* get anything until newline (including whitespaces). */
974
pj_scan_get_until_chr(scanner, "\r\n", str);
977
pj_scan_get_newline(scanner);
980
static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn,
983
ctx->last_error = PJMEDIA_SDP_EINCONN;
986
pj_scan_advance_n(scanner, 2, SKIP_WS);
989
pj_scan_get_until_ch(scanner, ' ', &conn->net_type);
990
pj_scan_get_char(scanner);
993
pj_scan_get_until_ch(scanner, ' ', &conn->addr_type);
994
pj_scan_get_char(scanner);
997
pj_scan_get_until_chr(scanner, "/ \t\r\n", &conn->addr);
998
PJ_TODO(PARSE_SDP_CONN_ADDRESS_SUBFIELDS);
1000
/* We've got what we're looking for, skip anything until newline */
1001
pj_scan_skip_line(scanner);
1004
static void parse_bandwidth_info(pj_scanner *scanner, pjmedia_sdp_bandw *bandw,
1009
ctx->last_error = PJMEDIA_SDP_EINBANDW;
1012
pj_scan_advance_n(scanner, 2, SKIP_WS);
1015
pj_scan_get_until_ch(scanner, ':', &bandw->modifier);
1016
pj_scan_get_char(scanner);
1019
pj_scan_get_until_chr(scanner, " \t\r\n", &str);
1020
bandw->value = pj_strtoul(&str);
1022
/* We've got what we're looking for, skip anything until newline */
1023
pj_scan_skip_line(scanner);
1026
static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
1031
ctx->last_error = PJMEDIA_SDP_EINMEDIA;
1033
/* check the equal sign */
1034
if (*(scanner->curptr+1) != '=') {
1035
on_scanner_error(scanner);
1040
pj_scan_advance_n(scanner, 2, SKIP_WS);
1043
pj_scan_get_until_ch(scanner, ' ', &med->desc.media);
1044
pj_scan_get_char(scanner);
1047
pj_scan_get(scanner, &cs_token, &str);
1048
med->desc.port = (unsigned short)pj_strtoul(&str);
1049
if (*scanner->curptr == '/') {
1051
pj_scan_get_char(scanner);
1052
pj_scan_get(scanner, &cs_token, &str);
1053
med->desc.port_count = pj_strtoul(&str);
1056
med->desc.port_count = 0;
1059
if (pj_scan_get_char(scanner) != ' ') {
1060
PJ_THROW(SYNTAX_ERROR);
1064
pj_scan_get_until_chr(scanner, " \t\r\n", &med->desc.transport);
1067
med->desc.fmt_count = 0;
1068
while (*scanner->curptr == ' ') {
1071
pj_scan_get_char(scanner);
1073
/* Check again for the end of the line */
1074
if ((*scanner->curptr == '\r') || (*scanner->curptr == '\n'))
1077
pj_scan_get(scanner, &cs_token, &fmt);
1078
if (med->desc.fmt_count < PJMEDIA_MAX_SDP_FMT)
1079
med->desc.fmt[med->desc.fmt_count++] = fmt;
1081
PJ_PERROR(2,(THIS_FILE, PJ_ETOOMANY,
1082
"Error adding SDP media format %.*s, "
1083
"format is ignored",
1084
(int)fmt.slen, fmt.ptr));
1087
/* We've got what we're looking for, skip anything until newline */
1088
pj_scan_skip_line(scanner);
1091
static void on_scanner_error(pj_scanner *scanner)
1093
PJ_UNUSED_ARG(scanner);
1095
PJ_THROW(SYNTAX_ERROR);
1098
static pjmedia_sdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner,
1101
pjmedia_sdp_attr *attr;
1103
ctx->last_error = PJMEDIA_SDP_EINATTR;
1105
attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
1107
/* check equal sign */
1108
if (*(scanner->curptr+1) != '=') {
1109
on_scanner_error(scanner);
1114
pj_scan_advance_n(scanner, 2, SKIP_WS);
1116
/* get attr name. */
1117
pj_scan_get(scanner, &cs_token, &attr->name);
1119
if (*scanner->curptr && *scanner->curptr != '\r' &&
1120
*scanner->curptr != '\n')
1122
/* skip ':' if present. */
1123
if (*scanner->curptr == ':')
1124
pj_scan_get_char(scanner);
1127
if (*scanner->curptr != '\r' && *scanner->curptr != '\n') {
1128
pj_scan_get_until_chr(scanner, "\r\n", &attr->value);
1130
attr->value.ptr = NULL;
1131
attr->value.slen = 0;
1135
attr->value.ptr = NULL;
1136
attr->value.slen = 0;
1139
/* We've got what we're looking for, skip anything until newline */
1140
pj_scan_skip_line(scanner);
1147
* Apply direction attribute in session to all media.
1149
static void apply_media_direction(pjmedia_sdp_session *sdp)
1151
pjmedia_sdp_attr *dir_attr = NULL;
1154
const pj_str_t inactive = { "inactive", 8 };
1155
const pj_str_t sendonly = { "sendonly", 8 };
1156
const pj_str_t recvonly = { "recvonly", 8 };
1157
const pj_str_t sendrecv = { "sendrecv", 8 };
1159
/* Find direction attribute in session, don't need to find default
1160
* direction "sendrecv".
1162
for (i = 0; i < sdp->attr_count && !dir_attr; ++i) {
1163
if (!pj_strcmp(&sdp->attr[i]->name, &sendonly) ||
1164
!pj_strcmp(&sdp->attr[i]->name, &recvonly) ||
1165
!pj_strcmp(&sdp->attr[i]->name, &inactive))
1167
dir_attr = sdp->attr[i];
1171
/* Found the direction attribute */
1173
/* Remove the direction attribute in session */
1174
pjmedia_sdp_attr_remove(&sdp->attr_count, sdp->attr, dir_attr);
1176
/* Apply the direction attribute to all media, but not overriding it
1177
* if media already has direction attribute.
1179
for (i = 0; i < sdp->media_count; ++i) {
1180
pjmedia_sdp_media *m;
1183
/* Find direction attribute in this media */
1185
for (j = 0; j < m->attr_count; ++j) {
1186
if (!pj_strcmp(&m->attr[j]->name, &sendrecv) ||
1187
!pj_strcmp(&m->attr[j]->name, &sendonly) ||
1188
!pj_strcmp(&m->attr[j]->name, &recvonly) ||
1189
!pj_strcmp(&m->attr[j]->name, &inactive))
1195
/* Not found, apply direction attribute from session */
1196
if (j == m->attr_count)
1197
pjmedia_sdp_media_add_attr(m, dir_attr);
1204
* Parse SDP message.
1206
PJ_DEF(pj_status_t) pjmedia_sdp_parse( pj_pool_t *pool,
1207
char *buf, pj_size_t len,
1208
pjmedia_sdp_session **p_sdp)
1211
pjmedia_sdp_session *session;
1212
pjmedia_sdp_media *media = NULL;
1213
pjmedia_sdp_attr *attr;
1214
pjmedia_sdp_conn *conn;
1215
pjmedia_sdp_bandw *bandw;
1221
ctx.last_error = PJ_SUCCESS;
1225
pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
1226
session = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
1227
PJ_ASSERT_RETURN(session != NULL, PJ_ENOMEM);
1229
/* Ignore leading newlines */
1230
while (*scanner.curptr=='\r' || *scanner.curptr=='\n')
1231
pj_scan_get_char(&scanner);
1234
while (!pj_scan_is_eof(&scanner)) {
1235
cur_name = *scanner.curptr;
1238
attr = parse_attr(pool, &scanner, &ctx);
1241
pjmedia_sdp_media_add_attr(media, attr);
1243
pjmedia_sdp_session_add_attr(session, attr);
1248
parse_origin(&scanner, session, &ctx);
1251
parse_generic_line(&scanner, &session->name, &ctx);
1254
conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
1255
parse_connection_info(&scanner, conn, &ctx);
1259
session->conn = conn;
1263
parse_time(&scanner, session, &ctx);
1266
media = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
1267
parse_media(&scanner, media, &ctx);
1268
if (session->media_count < PJMEDIA_MAX_SDP_MEDIA)
1269
session->media[ session->media_count++ ] = media;
1271
PJ_PERROR(2,(THIS_FILE, PJ_ETOOMANY,
1272
"Error adding media, media is ignored"));
1275
parse_version(&scanner, &ctx);
1279
pj_scan_get_char(&scanner);
1280
/* Allow empty newlines at the end of the message */
1281
while (!pj_scan_is_eof(&scanner)) {
1282
if (*scanner.curptr != 13 && *scanner.curptr != 10) {
1283
ctx.last_error = PJMEDIA_SDP_EINSDP;
1284
on_scanner_error(&scanner);
1286
pj_scan_get_char(&scanner);
1290
bandw = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_bandw);
1291
parse_bandwidth_info(&scanner, bandw, &ctx);
1293
media->bandw[media->bandw_count++] = bandw;
1295
session->bandw[session->bandw_count++] = bandw;
1299
if (cur_name >= 'a' && cur_name <= 'z')
1300
parse_generic_line(&scanner, &dummy, &ctx);
1302
ctx.last_error = PJMEDIA_SDP_EINSDP;
1303
on_scanner_error(&scanner);
1309
ctx.last_error = PJ_SUCCESS;
1314
char errmsg[PJ_ERR_MSG_SIZE];
1315
pj_strerror(ctx.last_error, errmsg, sizeof(errmsg));
1317
PJ_LOG(4, (THIS_FILE, "Error parsing SDP in line %d col %d: %s",
1318
scanner.line, pj_scan_get_col(&scanner),
1323
pj_assert(ctx.last_error != PJ_SUCCESS);
1327
pj_scan_fini(&scanner);
1330
apply_media_direction(session);
1333
return ctx.last_error;
1337
* Print SDP description.
1339
PJ_DEF(int) pjmedia_sdp_print( const pjmedia_sdp_session *desc,
1340
char *buf, pj_size_t size)
1342
return print_session(desc, buf, size);
1349
PJ_DEF(pjmedia_sdp_session*) pjmedia_sdp_session_clone( pj_pool_t *pool,
1350
const pjmedia_sdp_session *rhs)
1352
pjmedia_sdp_session *sess;
1355
PJ_ASSERT_RETURN(pool && rhs, NULL);
1357
sess = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
1358
PJ_ASSERT_RETURN(sess != NULL, NULL);
1360
/* Clone origin line. */
1361
pj_strdup(pool, &sess->origin.user, &rhs->origin.user);
1362
sess->origin.id = rhs->origin.id;
1363
sess->origin.version = rhs->origin.version;
1364
pj_strdup(pool, &sess->origin.net_type, &rhs->origin.net_type);
1365
pj_strdup(pool, &sess->origin.addr_type, &rhs->origin.addr_type);
1366
pj_strdup(pool, &sess->origin.addr, &rhs->origin.addr);
1368
/* Clone subject line. */
1369
pj_strdup(pool, &sess->name, &rhs->name);
1371
/* Clone connection line */
1373
sess->conn = pjmedia_sdp_conn_clone(pool, rhs->conn);
1374
PJ_ASSERT_RETURN(sess->conn != NULL, NULL);
1377
/* Duplicate bandwidth info */
1378
sess->bandw_count = rhs->bandw_count;
1379
for (i=0; i<rhs->bandw_count; ++i) {
1380
sess->bandw[i] = pjmedia_sdp_bandw_clone(pool, rhs->bandw[i]);
1383
/* Clone time line. */
1384
sess->time.start = rhs->time.start;
1385
sess->time.stop = rhs->time.stop;
1387
/* Duplicate session attributes. */
1388
sess->attr_count = rhs->attr_count;
1389
for (i=0; i<rhs->attr_count; ++i) {
1390
sess->attr[i] = pjmedia_sdp_attr_clone(pool, rhs->attr[i]);
1393
/* Duplicate media descriptors. */
1394
sess->media_count = rhs->media_count;
1395
for (i=0; i<rhs->media_count; ++i) {
1396
sess->media[i] = pjmedia_sdp_media_clone(pool, rhs->media[i]);
1403
#define CHECK(exp,ret) do { \
1404
/*pj_assert(exp);*/ \
1409
/* Validate SDP connetion info. */
1410
static pj_status_t validate_sdp_conn(const pjmedia_sdp_conn *c)
1412
CHECK( c, PJ_EINVAL);
1413
CHECK( pj_strcmp2(&c->net_type, "IN")==0, PJMEDIA_SDP_EINCONN);
1414
CHECK( pj_strcmp2(&c->addr_type, "IP4")==0 ||
1415
pj_strcmp2(&c->addr_type, "IP6")==0,
1416
PJMEDIA_SDP_EINCONN);
1417
CHECK( c->addr.slen != 0, PJMEDIA_SDP_EINCONN);
1423
/* Validate SDP session descriptor. */
1424
PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp)
1426
return pjmedia_sdp_validate2(sdp, PJ_TRUE);
1430
/* Validate SDP session descriptor. */
1431
PJ_DEF(pj_status_t) pjmedia_sdp_validate2(const pjmedia_sdp_session *sdp,
1435
const pj_str_t STR_RTPMAP = { "rtpmap", 6 };
1437
CHECK( sdp != NULL, PJ_EINVAL);
1439
/* Validate origin line. */
1440
CHECK( sdp->origin.user.slen != 0, PJMEDIA_SDP_EINORIGIN);
1441
CHECK( pj_strcmp2(&sdp->origin.net_type, "IN")==0,
1442
PJMEDIA_SDP_EINORIGIN);
1443
CHECK( pj_strcmp2(&sdp->origin.addr_type, "IP4")==0 ||
1444
pj_strcmp2(&sdp->origin.addr_type, "IP6")==0,
1445
PJMEDIA_SDP_EINORIGIN);
1446
CHECK( sdp->origin.addr.slen != 0, PJMEDIA_SDP_EINORIGIN);
1448
/* Validate subject line. */
1449
CHECK( sdp->name.slen != 0, PJMEDIA_SDP_EINNAME);
1451
/* Ignore start and stop time. */
1453
/* If session level connection info is present, validate it. */
1455
pj_status_t status = validate_sdp_conn(sdp->conn);
1456
if (status != PJ_SUCCESS)
1460
/* Validate each media. */
1461
for (i=0; i<sdp->media_count; ++i) {
1462
const pjmedia_sdp_media *m = sdp->media[i];
1465
/* Validate the m= line. */
1466
CHECK( m->desc.media.slen != 0, PJMEDIA_SDP_EINMEDIA);
1467
CHECK( m->desc.transport.slen != 0, PJMEDIA_SDP_EINMEDIA);
1468
CHECK( m->desc.fmt_count != 0 || m->desc.port==0, PJMEDIA_SDP_ENOFMT);
1470
/* If media level connection info is present, validate it. */
1472
pj_status_t status = validate_sdp_conn(m->conn);
1473
if (status != PJ_SUCCESS)
1477
/* If media doesn't have connection info, then connection info
1478
* must be present in the session.
1480
if (m->conn == NULL) {
1481
if (sdp->conn == NULL)
1482
if (strict || m->desc.port != 0)
1483
return PJMEDIA_SDP_EMISSINGCONN;
1486
/* Verify payload type. */
1487
for (j=0; j<m->desc.fmt_count; ++j) {
1489
/* Arrgh noo!! Payload type can be non-numeric!!
1490
* RTC based programs sends "null" for instant messaging!
1492
if (pj_isdigit(*m->desc.fmt[j].ptr)) {
1493
unsigned pt = pj_strtoul(&m->desc.fmt[j]);
1495
/* Payload type is between 0 and 127.
1497
CHECK( pt <= 127, PJMEDIA_SDP_EINPT);
1499
/* If port is not zero, then for each dynamic payload type, an
1500
* rtpmap attribute must be specified.
1502
if (m->desc.port != 0 && pt >= 96) {
1503
const pjmedia_sdp_attr *a;
1505
a = pjmedia_sdp_media_find_attr(m, &STR_RTPMAP,
1507
CHECK( a != NULL, PJMEDIA_SDP_EMISSINGRTPMAP);
1518
PJ_DEF(pj_status_t) pjmedia_sdp_transport_cmp( const pj_str_t *t1,
1521
static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
1522
static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
1524
/* Exactly equal? */
1525
if (pj_stricmp(t1, t2) == 0)
1529
if ((!pj_stricmp(t1, &ID_RTP_AVP) || !pj_stricmp(t1, &ID_RTP_SAVP)) &&
1530
(!pj_stricmp(t2, &ID_RTP_AVP) || !pj_stricmp(t2, &ID_RTP_SAVP)))
1533
return PJMEDIA_SDP_ETPORTNOTEQUAL;
1537
PJ_DEF(pj_status_t) pjmedia_sdp_media_deactivate(pj_pool_t *pool,
1538
pjmedia_sdp_media *m)
1540
PJ_ASSERT_RETURN(m, PJ_EINVAL);
1541
PJ_UNUSED_ARG(pool);
1543
/* Set port to zero */
1546
/* And remove attributes */
1553
PJ_DEF(pjmedia_sdp_media*) pjmedia_sdp_media_clone_deactivate(
1555
const pjmedia_sdp_media *rhs)
1558
pjmedia_sdp_media *m;
1560
PJ_ASSERT_RETURN(pool && rhs, NULL);
1562
m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
1563
pj_memcpy(m, rhs, sizeof(*m));
1565
/* Clone the media line only */
1566
pj_strdup (pool, &m->desc.media, &rhs->desc.media);
1567
pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
1568
for (i=0; i<rhs->desc.fmt_count; ++i)
1569
pj_strdup(pool, &m->desc.fmt[i], &rhs->desc.fmt[i]);
1572
m->conn = pjmedia_sdp_conn_clone (pool, rhs->conn);
1573
PJ_ASSERT_RETURN(m->conn != NULL, NULL);
1576
m->bandw_count = rhs->bandw_count;
1577
for (i=0; i < rhs->bandw_count; ++i) {
1578
m->bandw[i] = pjmedia_sdp_bandw_clone (pool, rhs->bandw[i]);
1579
PJ_ASSERT_RETURN(m->bandw[i] != NULL, NULL);
1582
/* And deactivate it */
1583
pjmedia_sdp_media_deactivate(pool, m);