Browse Source

MT#56128 SDP manipulations: add support for 'substitute' command.

It substitutes a specified `a=` line taken from the concerned
media attributes list. If such line has been not found,
the attributes list remains untouched.

It subsitutes one attribute at a time, so one attribute into
another attribute.

Change-Id: Ie0a48ba46a1b196fbe33b09dedc40e4498640e34
pull/1640/head
Donat Zenichev 3 years ago
parent
commit
4b79d20577
5 changed files with 335 additions and 13 deletions
  1. +68
    -3
      daemon/call_interfaces.c
  2. +90
    -2
      daemon/sdp.c
  3. +33
    -8
      docs/ng_control_protocol.md
  4. +10
    -0
      include/sdp.h
  5. +134
    -0
      t/auto-daemon-tests.pl

+ 68
- 3
daemon/call_interfaces.c View File

@ -644,12 +644,57 @@ INLINE void ng_sdp_attr_manipulations(struct sdp_manipulations_common ** sm_ptr,
if (!bencode_get_str(it_c, &command_type))
continue;
GQueue * q_ptr = NULL;
switch (__csh_lookup(&command_type)) {
/* CMD_ADD commands */
case CSH_LOOKUP("add"):;
GQueue * q_ptr = NULL;
/* CMD_ADD / CMD_SUBST commands */
case CSH_LOOKUP("substitute"):;
struct sdp_substitute_attr * subst_command = NULL;
switch (media) {
case MT_UNKNOWN:
q_ptr = &(*sm_ptr)->subst_commands_glob;
break;
case MT_AUDIO:
q_ptr = &(*sm_ptr)->subst_commands_audio;
break;
case MT_VIDEO:
q_ptr = &(*sm_ptr)->subst_commands_video;
break;
default:
ilog(LOG_WARN, "SDP manipulations: unspported SDP section targeted.");
continue;
}
for (bencode_item_t *it_v = command_value->child; it_v; it_v = it_v->sibling)
{
str from;
str to;
if (!bencode_get_str(it_v->child, &from))
continue;
if (!bencode_get_str(it_v->child->sibling, &to))
continue;
str * s_copy_from = str_dup_escape(&from);
str * s_copy_to = str_dup_escape(&to);
if (!s_copy_from->len || !s_copy_to->len) {
free(s_copy_from);
free(s_copy_to);
continue;
}
subst_command = g_slice_alloc0(sizeof(*subst_command));
subst_command->value_a = s_copy_from;
subst_command->value_b = s_copy_to;
g_queue_push_tail(q_ptr, subst_command);
}
break;
case CSH_LOOKUP("add"):;
switch (media) {
case MT_UNKNOWN:
q_ptr = &(*sm_ptr)->add_commands_glob;
@ -1775,7 +1820,20 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu
call_ng_dict_iter(out, input, call_ng_main_flags);
}
static void free_subst_attr(void *p) {
struct sdp_substitute_attr *attr = p;
if (attr->value_a)
free(attr->value_a);
if (attr->value_b)
free(attr->value_b);
g_slice_free1(sizeof(*attr), attr);
}
static void ng_sdp_attr_manipulations_free(struct sdp_manipulations_common * sdp_manipulations) {
GQueue *cpq_subst_glob = &sdp_manipulations->subst_commands_glob;
GQueue *cpq_subst_audio = &sdp_manipulations->subst_commands_audio;
GQueue *cpq_subst_video = &sdp_manipulations->subst_commands_video;
if (sdp_manipulations->rem_commands_glob)
g_hash_table_destroy(sdp_manipulations->rem_commands_glob);
if (sdp_manipulations->rem_commands_audio)
@ -1787,6 +1845,13 @@ static void ng_sdp_attr_manipulations_free(struct sdp_manipulations_common * sdp
g_queue_clear_full(&sdp_manipulations->add_commands_audio, free);
g_queue_clear_full(&sdp_manipulations->add_commands_video, free);
if (cpq_subst_glob && cpq_subst_glob->head)
g_queue_clear_full(cpq_subst_glob, free_subst_attr);
if (cpq_subst_audio && cpq_subst_audio->head)
g_queue_clear_full(cpq_subst_audio, free_subst_attr);
if (cpq_subst_video && cpq_subst_video->head)
g_queue_clear_full(cpq_subst_video, free_subst_attr);
g_slice_free1(sizeof(*sdp_manipulations), sdp_manipulations);
}


+ 90
- 2
daemon/sdp.c View File

@ -291,12 +291,21 @@ static void attr_free(void *p);
static void attr_insert(struct sdp_attributes *attrs, struct sdp_attribute *attr);
INLINE void chopper_append_c(struct sdp_chopper *c, const char *s);
/**
* Helper, which compares a substitute command for SDP manipulations.
*/
static int sdp_manipulate_subst_cmp(gconstpointer a, gconstpointer b) {
const struct sdp_substitute_attr * subst = a;
const struct sdp_substitute_attr * subst_lookup = b;
return str_cmp_str(subst->value_a, subst_lookup->value_a);
}
/**
* Checks whether a given type of SDP manipulation exists for a given session level.
*/
static int sdp_manipulate_check(enum command_type command_type,
struct sdp_manipulations_common * sdp_manipulations,
enum media_type media_type, str *attr_name) {
enum media_type media_type, str * attr_name) {
/* for now we only support session lvl, audio and media streams */
if (media_type == MT_OTHER)
{
@ -309,9 +318,33 @@ static int sdp_manipulate_check(enum command_type command_type,
if (!sdp_manipulations)
return 0;
GQueue * q_ptr = NULL;
switch (command_type) {
case CMD_SUBST:;
q_ptr = NULL;
if (!attr_name)
break;
struct sdp_substitute_attr fictitious = {attr_name, NULL};
switch (media_type) {
case MT_AUDIO:
q_ptr = &sdp_manipulations->subst_commands_audio;
break;
case MT_VIDEO:
q_ptr = &sdp_manipulations->subst_commands_video;
break;
default: /* MT_UNKNOWN */
q_ptr = &sdp_manipulations->subst_commands_glob;
}
if (q_ptr && q_ptr->head &&
g_queue_find_custom(q_ptr, &fictitious, sdp_manipulate_subst_cmp))
return 1;
break;
case CMD_ADD:;
GQueue * q_ptr = NULL;
q_ptr = NULL;
switch (media_type) {
case MT_AUDIO:
q_ptr = &sdp_manipulations->add_commands_audio;
@ -379,6 +412,37 @@ static void sdp_manipulations_add(struct sdp_chopper *chop,
}
}
/**
* Substitute values for a requested session level (global, audio, video)
*/
static void sdp_manipulations_subst(struct sdp_chopper *chop,
struct sdp_manipulations_common * sdp_manipulations,
enum media_type media_type, str * attr_name) {
GQueue * q_ptr = NULL;
switch (media_type) {
case MT_AUDIO:
q_ptr = &sdp_manipulations->subst_commands_audio;
break;
case MT_VIDEO:
q_ptr = &sdp_manipulations->subst_commands_video;
break;
default: /* MT_UNKNOWN */
q_ptr = &sdp_manipulations->subst_commands_glob;
}
for (GList *l = q_ptr->head; l; l = l->next)
{
struct sdp_substitute_attr * attr_value = l->data;
if (!str_cmp_str(attr_name, attr_value->value_a)) {
chopper_append_c(chop, "a=");
chopper_append_c(chop, attr_value->value_b->s);
chopper_append_c(chop, "\r\n");
}
}
}
static void append_attr_to_gstring(GString *s, char * name, const str * value,
struct sdp_ng_flags *flags, enum media_type media_type);
static void append_attr_char_to_gstring(GString *s, char * name, const char * value,
@ -2273,6 +2337,10 @@ static int process_session_attributes(struct sdp_chopper *chop, struct sdp_attri
if (sdp_manipulate_check(CMD_REM, sdp_manipulations, MT_UNKNOWN, &attr->line_value))
goto strip;
/* if attr is supposed to be substituted don't add to the chop->output, but add another value */
if (sdp_manipulate_check(CMD_SUBST, sdp_manipulations, MT_UNKNOWN, &attr->line_value))
goto strip_with_subst;
switch (attr->attr) {
case ATTR_ICE:
case ATTR_ICE_UFRAG:
@ -2332,6 +2400,14 @@ strip:
return -1;
if (skip_over(chop, &attr->full_line))
return -1;
continue;
strip_with_subst:
if (copy_up_to(chop, &attr->full_line))
return -1;
if (skip_over(chop, &attr->full_line))
return -1;
sdp_manipulations_subst(chop, sdp_manipulations, MT_UNKNOWN, &attr->line_value);
}
return 0;
@ -2356,6 +2432,10 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media *
if (sdp_manipulate_check(CMD_REM, sdp_manipulations, sdp->media_type_id, &attr->line_value))
goto strip;
/* if attr is supposed to be substituted don't add to the chop->output, but add another value */
if (sdp_manipulate_check(CMD_SUBST, sdp_manipulations, sdp->media_type_id, &attr->line_value))
goto strip_with_subst;
switch (attr->attr) {
case ATTR_ICE:
case ATTR_ICE_UFRAG:
@ -2444,6 +2524,14 @@ strip:
return -1;
if (skip_over(chop, &attr->full_line))
return -1;
continue;
strip_with_subst:
if (copy_up_to(chop, &attr->full_line))
return -1;
if (skip_over(chop, &attr->full_line))
return -1;
sdp_manipulations_subst(chop, sdp_manipulations, sdp->media_type_id, &attr->line_value);
}
return 0;


+ 33
- 8
docs/ng_control_protocol.md View File

@ -1256,14 +1256,19 @@ Description:
Can take multiple values (so multiple attributes can removed per one command).
- `substitute`
Substitutes a specified `a=` line taken from the concerned media attributes list.
If such line has been not found, the attributes list remains untouched.
Substitutes one attribute at a time, so one attribute into another attribute.
Read more about that below in the `<value>` section.
* `<value>`
The `value` doesn't take the `a=` lvalue part, only what must go after an equal sign.
For `remove` and `substitute`, the value of the command has a wildcard matching
using a prefix. So that, all values caught by the prefix are affected.
No wild-cards and regex expressions accepted. Only a prefix or whole value are allowed.
No wild-cards and regex expressions accepted. Only a whole value is allowed.
One should remember that some attributes are allowed to be present multiple times,
as for example `a=ssrc:`. Therefore the RTPEngine does not expect specified `a=` lines
@ -1277,10 +1282,16 @@ Description:
regardless of their content. On the other hand, one might want to remove all attributes
corresponding to one SSRC only — so a removal of all `a=ssrc:123456`, for example.
Thus, in case of intention to remove multiple attribute lines related to a specific
scope of session parameters, one should specify a prefix as a value,
which would catch all of them. And vice-versa, in case of intention to remove quite
a specific attribute, one should consider tight uniqueness of the value (so full value).
Important remark regarding `substitute` command.
It takes only two values at a time, in other words substitutes one attribute per command:
- the first `value`, that matches the value to be substituted; and
- the second `value`, that is to be placed.
Therefore, the only allowed syntax for it is (per command):
"substitute": ["from-this-attribute", "to-that-attribute"]
All other possible usages will be ignored and only first two values will be taken.
However, multiple `substitute` commands can be given per time, see examples below.
Examples:
@ -1318,6 +1329,20 @@ Examples:
}
}
* Substitute two attributes of the global session and one for audio media section (pay attention, `substitute` is using lists, not dictionaries):
"sdp-attr" :
{
"none" :
{
"substitute": [[ "sendrecv" , "sendonly" ], [ "ptime:20" , "ptime:40" ]]
},
"audio" :
{
"substitute": [["fmtp:101 0-15" , "fmtp:126 0-16" ]]
},
}
An example of a complete `offer` request dictionary could be (SDP body abbreviated):
{ "command": "offer", "call-id": "cfBXzDSZqhYNcXM", "from-tag": "mS9rSAn0Cr",


+ 10
- 0
include/sdp.h View File

@ -13,6 +13,12 @@ enum command_type {
CMD_SUBST,
};
/* */
struct sdp_substitute_attr {
str * value_a; /* from */
str * value_b; /* to */
};
/* A structure for SDP arbitary manipulations on all levels of SDP:
* session (global), media (audio/video). Works only on `a=` lines.
*/
@ -24,6 +30,10 @@ struct sdp_manipulations_common {
GHashTable * rem_commands_glob;
GHashTable * rem_commands_audio;
GHashTable * rem_commands_video;
GQueue subst_commands_glob;
GQueue subst_commands_audio;
GQueue subst_commands_video;
};
struct ice_candidate;


+ 134
- 0
t/auto-daemon-tests.pl View File

@ -16052,6 +16052,140 @@ a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('SDP attr manipulations - substitute a= line for media audio', { ICE => 'remove', DTLS => 'off', SDES => [ 'nonew' ], 'sdp-attr' => { audio => { substitute => [['test1', 'test2']] } }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/SAVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=test1
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=test2
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('SDP attr manipulations - substitute a= line for media audio', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/SAVP 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/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('SDP attr manipulations - substitute a= line for a session level', { ICE => 'remove', DTLS => 'off', SDES => [ 'nonew' ], 'sdp-attr' => { none => { substitute => [['test1', 'test2']] } }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
a=test1
m=audio 2000 RTP/SAVP 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
a=test2
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('SDP attr manipulations - substitute a= line for a session level', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/SAVP 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/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('SDP attr manipulations - substitute two a= lines for media audio', { ICE => 'remove', DTLS => 'off', SDES => [ 'nonew' ], 'sdp-attr' => { audio => [ substitute => [['test1', 'test2'] , ['test5', 'test6']] ] }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/SAVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=test1
a=test5
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/SAVP 0
c=IN IP4 203.0.113.1
a=test2
a=test6
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('SDP attr manipulations - substitute two a= lines for media audio', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/SAVP 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/SAVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
# codec masking gh#664
new_call;


Loading…
Cancel
Save