MT#55283 support dict/list for rtpp_flags

Change-Id: Ic9cd39ce771eb5767bb970af5aa438e692a61586
rfuchs/fixes
Richard Fuchs 8 months ago
parent 4f54910f91
commit 6751ff7f84

@ -64,17 +64,188 @@ static bool str_key_val_prefix(const str * p, const char * q,
return true;
}
static bool dummy_is_list(parser_arg a) {
return false;
static inline bool skip_char(str *s, char c) {
if (s->len == 0 || s->s[0] != c)
return false;
str_shift(s, 1);
return true;
}
static inline void skip_chars(str *s, char c) {
while (skip_char(s, c));
}
static int rtpp_is_dict_list(str *a) {
str list = *a;
if (!skip_char(&list, '['))
return 0;
// check contents
if (list.len == 0)
return 0; // unexpected end of string
if (list.s[0] == '[')
return 1; // contains sub-list, must be a list
// inspect first element for 'key='
str key, val;
if (!get_key_val(&key, &val, &list))
return 0; // nothing to read
if (val.len)
return 2; // is a dict
return 1; // is a list
}
static bool rtpp_is_list(rtpp_pos *a) {
return rtpp_is_dict_list(&a->cur) == 1;
}
static str *dummy_get_str(parser_arg a, str *b) {
*b = *a.str;
static bool rtpp_is_dict(rtpp_pos *a) {
return rtpp_is_dict_list(&a->cur) == 2;
}
static str *rtpp_get_str(rtpp_pos *a, str *b) {
if (rtpp_is_dict_list(&a->cur) != 0)
return NULL;
if (a->cur.len == 0)
return NULL;
*b = a->cur;
return b;
}
static long long rtpp_get_int_str(rtpp_pos *a, long long def) {
str s;
if (!rtpp_get_str(a, &s))
return def;
return str_to_i(&s, def);
}
static bool rtpp_dict_list_end_rewind(rtpp_pos *pos) {
// check for dict/list end, which is only valid if it doesn't also start one
if (pos->cur.len == 0 || pos->cur.s[0] == '[' || pos->cur.s[pos->cur.len - 1] != ']')
return false;
pos->cur.len--;
// remove any extra closing bracket, and return them to the remainder for
// the upper level function to parse
while (pos->cur.len && pos->cur.s[pos->cur.len - 1] == ']') {
pos->cur.len--;
pos->remainder.s--;
pos->remainder.len++;
// we might be on a space or something - go to the actual bracket, which must
// be there somewhere
while (pos->remainder.s[0] != ']') {
pos->remainder.s--;
pos->remainder.len++;
}
}
return true;
}
static bool rtpp_dict_list_closing(rtpp_pos *pos) {
if (pos->cur.s[0] != ']')
return false;
str_shift(&pos->cur, 1);
// anything left in the string, return it to the remainder
pos->remainder.len += pos->remainder.s - pos->cur.s;
pos->remainder.s = pos->cur.s;
return true;
}
static void rtpp_list_iter(const ng_parser_t *parser, rtpp_pos *pos,
void (*str_callback)(str *key, unsigned int, helper_arg),
void (*item_callback)(const ng_parser_t *, parser_arg, helper_arg), helper_arg arg)
{
// list opener
if (!skip_char(&pos->cur, '['))
return;
unsigned int idx = 0;
while (true) {
skip_chars(&pos->cur, ' ');
if (!pos->cur.len)
goto next; // empty token?
// list closing?
if (rtpp_dict_list_closing(pos))
break;
// does it start another list or dict?
if (pos->cur.s[0] == '[') {
if (item_callback)
item_callback(parser, pos, arg);
goto next;
}
// guess it's a string token
// does it end the list?
bool end = rtpp_dict_list_end_rewind(pos);
if (pos->cur.len == 0)
break; // nothing left
if (str_callback)
str_callback(&pos->cur, idx++, arg);
if (end)
break;
goto next;
next:
// find next token in remainder, put in `cur`
if (!str_token_sep(&pos->cur, &pos->remainder, ' '))
break;
}
}
static bool rtpp_dict_iter(const ng_parser_t *parser, rtpp_pos *pos,
void (*callback)(const ng_parser_t *, str *, parser_arg, helper_arg),
helper_arg arg)
{
// list opener
if (!skip_char(&pos->cur, '['))
return false;
while (true) {
skip_chars(&pos->cur, ' ');
if (!pos->cur.len)
goto next; // empty token?
// dict closing?
if (rtpp_dict_list_closing(pos))
break;
str key;
if (!str_token(&key, &pos->cur, '=')) {
ilog(LOG_ERR, "Entry in dictionary without equals sign ('" STR_FORMAT "'), aborting",
STR_FMT(&pos->cur));
break;
}
// guess it's a string token
// does it end the dict?
bool end = rtpp_dict_list_end_rewind(pos);
if (pos->cur.len == 0)
break; // nothing left
callback(parser, &key, pos, arg);
if (end)
break;
goto next;
next:
// find next token in remainder, put in `cur`
if (!str_token_sep(&pos->cur, &pos->remainder, ' '))
break;
}
return true;
}
static bool rtpp_is_int(rtpp_pos *pos) {
return false;
}
const ng_parser_t dummy_parser = {
.is_list = dummy_is_list,
.get_str = dummy_get_str,
.is_list = rtpp_is_list,
.is_dict = rtpp_is_dict,
.is_int = rtpp_is_int,
.list_iter = rtpp_list_iter,
.dict_iter = rtpp_dict_iter,
.get_str = rtpp_get_str,
.get_int_str = rtpp_get_int_str,
};
static bool parse_codec_to_dict(str * key, str * val, const char *cmp1, const char *cmp2,
@ -158,8 +329,7 @@ void parse_rtpp_flags(const str * rtpp_flags, sdp_ng_flags *out)
while (remainder.len)
{
/* skip spaces */
while (remainder.len && remainder.s[0] == ' ')
str_shift(&remainder, 1);
skip_chars(&remainder, ' ');
/* set key and val */
if (!get_key_val(&key, &val, &remainder))
@ -229,7 +399,7 @@ void parse_rtpp_flags(const str * rtpp_flags, sdp_ng_flags *out)
if (!val.s && str_eq(&key, "RTP/SAVPF"))
transport = 0x103;
/* direction */
else if (str_eq(&key, "direction"))
else if (str_eq(&key, "direction") && rtpp_is_dict_list(&val) == 0)
rtpp_direction_flag(out, &direction_flag, &val);
else
goto generic;
@ -258,8 +428,11 @@ generic:
if (!val.len)
call_ng_flags_flags(&key, 0, out);
/* generic flags with value, but no particular processing */
else
call_ng_main_flags(&dummy_parser, &key, &val, out);
else {
rtpp_pos pos = { .cur = val, .remainder = remainder };
call_ng_main_flags(&dummy_parser, &key, &pos, out);
remainder = pos.remainder;
}
next:;
}

@ -121,6 +121,10 @@ When the flags are passed to rtpengine, they are formated as following:
{ "rtpp_flags": "replace-origin via-branch=auto-next strict-source label=callee OSRTP-accept transport-protocol=RTP/AVP address-family=IP4" }
Lists and dictionaries are supported in this format using square brackets `[ ]`, for example:
{ "rtpp_flags": "via-branch=auto-next OSRTP=[accept] codec=[transcode=[PCMA PCMU] accept=[AMR-WB AMR] strip=[EVS]]" }
Regardless whether the flags parsing is done by the module or daemon,
a functional behavior remains the same and has no difference in terms of SDP processing.

@ -43,10 +43,15 @@ typedef struct ng_command_ctx ng_command_ctx_t;
typedef struct bencode_item bencode_item_t;
typedef struct {
str cur;
str remainder;
} rtpp_pos;
typedef union {
bencode_item_t *benc;
JsonNode *json;
str *str;
rtpp_pos *rtpp;
void *gen;
} parser_arg __attribute__ ((__transparent_union__));

@ -199,6 +199,92 @@ SDP
new_call;
offer('rtpp-flags: codec-accept obj first',
{ 'rtpp-flags' => 'codec=[accept=[PCMU]] replace-origin strict-source label=caller OSRTP-accept address-family=IP4 transport-protocol=RTP/AVP' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.101.40
s=tester
t=0 0
m=audio 3000 RTP/AVP 0 8
c=IN IP4 198.51.100.1
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0 8
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('rtpp-flags: codec-accept obj first',
{ 'rtpp-flags' => 'replace-origin strict-source label=callee OSRTP-accept address-family=IP4 transport-protocol=RTP/AVP' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.101.40
s=tester
t=0 0
m=audio 3000 RTP/AVP 8
c=IN IP4 198.51.100.1
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('rtpp-flags: codec-accept obj last',
{ 'rtpp-flags' => 'replace-origin strict-source label=caller OSRTP-accept address-family=IP4 transport-protocol=RTP/AVP codec=[accept=[PCMU]] ' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.101.40
s=tester
t=0 0
m=audio 3000 RTP/AVP 0 8
c=IN IP4 198.51.100.1
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0 8
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('rtpp-flags: codec-accept obj last',
{ 'rtpp-flags' => 'replace-origin strict-source label=callee OSRTP-accept address-family=IP4 transport-protocol=RTP/AVP' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.101.40
s=tester
t=0 0
m=audio 3000 RTP/AVP 8
c=IN IP4 198.51.100.1
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('remove rtcp-mux support',
{ 'rtpp-flags' => 'rtcp-mux-demux replace-origin strict-source label=caller OSRTP-accept address-family=IP4 transport-protocol=RTP/AVP' }, <<SDP);
v=0
@ -223,6 +309,30 @@ SDP
new_call;
offer('remove rtcp-mux support obj',
{ 'rtpp-flags' => 'rtcp-mux=[demux] replace=[origin] strict-source label=caller OSRTP-accept address-family=IP4 transport-protocol=RTP/AVP' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
c=IN IP4 198.51.100.1
m=audio 4024 RTP/AVP 0
a=rtcp-mux
a=inactive
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
c=IN IP4 203.0.113.1
m=audio PORT RTP/AVP 0
a=rtpmap:0 PCMU/8000
a=inactive
a=rtcp:PORT
SDP
new_call;
offer('SDES only allowed crypto suites, but not offered',
{ 'rtpp-flags' => 'SDES-only-AES_CM_128_HMAC_SHA1_80 ICE=remove DTLS=off replace-origin strict-source label=caller address-family=IP4 transport-protocol=RTP/SAVP' }, <<SDP);
v=0
@ -346,6 +456,48 @@ SDP
new_call;
offer('direction obj', { 'rtpp-flags' => 'direction=[foo bar]' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.8
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('direction obj', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/AVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.7
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('int/ext', { 'rtpp-flags' => 'internal external' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -386,5 +538,109 @@ a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('sdp-attr delete', { 'rtpp-flags' => 'SDP-attr=[audio=[remove=[test]]]' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=test
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('sdp-attr combination', { 'rtpp-flags' => 'replace=[origin] SDP-attr=[audio=[remove=[test] add=[foo bar]] global=[remove=[quux]]] rtcp-mux=[offer]' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
a=quux
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=test
a=quux
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=quux
a=sendrecv
a=rtcp:PORT
a=rtcp-mux
a=foo
a=bar
SDP
new_call;
offer('int-str', { 'rtpp-flags' => 'digit=3 frequency=344 frequencies=[123]' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('malformed syntax', { 'rtpp-flags' => 'replace=[ origin ] SDP-attr= [audio=[ remove = [ test ] add = [foo bar]] global=[remove=[quux]]] rtcp-mux=[offer]' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
a=quux
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=test
a=quux
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
a=quux
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=test
a=quux
a=sendrecv
a=rtcp:PORT
a=rtcp-mux
SDP
#done_testing;NGCP::Rtpengine::AutoTest::terminate('f00');exit;
done_testing();

Loading…
Cancel
Save