TT#147451 add support for multi-ml subscriptions

Change-Id: I1ff9861840bc743068eac2cd18d81990d44acdc9
mika/coverity
Richard Fuchs 4 years ago
parent 77be8a00b0
commit a7e22ba698

@ -658,6 +658,13 @@ The request dictionary must contain at least the following keys:
Optionally included keys are:
* `from-tags`
Contains a list of strings used to selected multiple existing call
participants (e.g. for the `subscribe request` message). An alternative
way to list multiple tags is by putting them into the `flags` list,
each prefixed with `from-tags-`.
* `via-branch`
The SIP `Via` branch as string. Used to additionally refine the matching logic between media streams
@ -2095,24 +2102,30 @@ described above.
---------------------------
This message is used to request subscription (i.e. receiving a copy of the
media) to an existing call participant, which must have been created either
through the offer/answer mechanism, or through the publish mechanism.
media) to one or multiple existing call participants, which must have been
created either through the offer/answer mechanism, or through the publish
mechanism.
A single call participant can be selected in the same way as described under
`block DTMF`. Multiple call participants can be selected either by using the
`all` keyword, in which case all call participants that were created through
the offer/answer mechanism will be selected, or by providing a list of tags
(from-tags) in the `from-tags` list.
The call participant is selected in the same way as described under `block
DTMF` except that one call participant must be selected (i.e. the `all` keyword
cannot be used). This message then creates a new call participant, which
corresponds to the subscription. This new call participant will be identified
by a newly generated unique tag, or by the tag given in the `to-tag` key. If a
label is to be set for the newly created subscription, it can be set through
`set-label`.
This message then creates a new call participant, which corresponds to the
subscription. This new call participant will be identified by a newly generated
unique tag, or by the tag given in the `to-tag` key. If a label is to be set
for the newly created subscription, it can be set through `set-label`.
The reply message will contain a sendonly offer SDP in `sdp` which by default
will mirror the SDP of the call participant being subscribed to. This offer SDP
can be manipulated with the same flags as used in an `offer` message, including
the option to manipulate the codecs. The reply message will also contain the
`from-tag` (corresponding to the call participant being subscribed to) and the
`to-tag` (corresponding to the subscription, either generated or taken from the
received message).
will mirror the SDP of the call participant being subscribed to. If multiple
call participants are subscribed to at the same time, then this SDP will
contain multiple media sections, combined out of the media sections of all
selected call participants. This offer SDP can be manipulated with the same
flags as used in an `offer` message, including the option to manipulate the
codecs. The reply message will also contain the `from-tags` (corresponding to
the call participants being subscribed to) and the `to-tag` (corresponding to
the subscription, either generated or taken from the received message).
If a `subscribe request` is made for an existing `to-tag` then all existing
subscriptions for that `to-tag` are deleted before the new subscriptions are

@ -825,11 +825,18 @@ struct call_media *call_media_new(struct call *call) {
}
static struct call_media *__get_media(struct call_monologue *ml, GList **it, const struct stream_params *sp,
const struct sdp_ng_flags *flags)
const struct sdp_ng_flags *flags, int index)
{
struct call_media *med;
struct call *call;
// is this a repeated call with *it set but for a different ml?
if (*it) {
med = (*it)->data;
if (med->monologue != ml)
*it = NULL;
}
/* iterator points to last seen element, or NULL if uninitialized */
if (!*it)
*it = ml->medias.head;
@ -849,21 +856,25 @@ static struct call_media *__get_media(struct call_monologue *ml, GList **it, con
STR_FMT(&sp->media_id));
}
unsigned int want_index = sp->index;
if (index != -1)
want_index = index;
/* possible incremental update, hunt for correct media struct */
while (*it) {
med = (*it)->data;
if (med->index == sp->index) {
__C_DBG("found existing call_media for stream #%u", sp->index);
if (med->index == want_index) {
__C_DBG("found existing call_media for stream #%u", want_index);
return med;
}
*it = (*it)->next;
}
__C_DBG("allocating new call_media for stream #%u", sp->index);
__C_DBG("allocating new call_media for stream #%u", want_index);
call = ml->call;
med = call_media_new(call);
med->monologue = ml;
med->index = sp->index;
med->index = want_index;
call_str_cpy(ml->call, &med->type, &sp->type);
med->type_id = sp->type_id;
@ -1309,6 +1320,11 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru
la = A->streams.head;
lb = B ? B->streams.head : NULL;
if (B)
__C_DBG("Sink init media %u -> %u", A->index, B->index);
else
__C_DBG("Stream init media %u", A->index);
while (la) {
a = la->data;
b = lb ? lb->data : NULL;
@ -2397,7 +2413,11 @@ static void __update_init_subscribers(struct call_monologue *ml, GQueue *streams
for (GList *l = ml->subscribers.head; l; l = l->next) {
struct call_subscription *cs = l->data;
struct call_monologue *sub_ml = cs->monologue;
sub_medias[num_subs++] = sub_ml->medias.head;
sub_medias[num_subs] = sub_ml->medias.head;
// skip into correct media section for multi-ml subscriptions
for (unsigned int offset = cs->media_offset; offset && sub_medias[num_subs]; offset--)
sub_medias[num_subs] = sub_medias[num_subs]->next;
num_subs++;
}
// keep num_subs as shortcut to ml->subscribers.length
@ -2622,8 +2642,8 @@ int monologue_offer_answer(struct call_monologue *dialogue[2], GQueue *streams,
/* first, check for existence of call_media struct on both sides of
* the dialogue */
media = __get_media(monologue, &ml_media, sp, flags);
other_media = __get_media(other_ml, &other_ml_media, sp, flags);
media = __get_media(monologue, &ml_media, sp, flags, -1);
other_media = __get_media(other_ml, &other_ml_media, sp, flags, -1);
/* OTHER is the side which has sent the message. SDP parameters in
* "sp" are as advertised by OTHER side. The message will be sent to
* THIS side. Parameters sent to THIS side may be overridden by
@ -2742,6 +2762,10 @@ error_intf:
}
void call_subscriptions_clear(GQueue *q) {
g_queue_clear_full(q, call_subscription_free);
}
static void __unsubscribe_one_link(struct call_monologue *which, GList *which_cs_link) {
struct call_subscription *cs = which_cs_link->data;
struct call_subscription *rev_cs = cs->link->data;
@ -2786,7 +2810,9 @@ static void __unsubscribe_from_all(struct call_monologue *ml) {
l = next;
}
}
void __add_subscription(struct call_monologue *which, struct call_monologue *to, bool offer_answer) {
void __add_subscription(struct call_monologue *which, struct call_monologue *to, bool offer_answer,
unsigned int offset)
{
if (g_hash_table_lookup(which->subscriptions_ht, to)) {
ilog(LOG_DEBUG, "Tag '" STR_FORMAT_M "' is already subscribed to '" STR_FORMAT_M "'",
STR_FMT_M(&which->tag),
@ -2800,6 +2826,8 @@ void __add_subscription(struct call_monologue *which, struct call_monologue *to,
struct call_subscription *to_rev_cs = g_slice_alloc0(sizeof(*to_rev_cs));
which_cs->monologue = to;
to_rev_cs->monologue = which;
which_cs->media_offset = offset;
to_rev_cs->media_offset = offset;
// keep offer-answer subscriptions first in the list
if (!offer_answer) {
g_queue_push_tail(&which->subscriptions, which_cs);
@ -2821,8 +2849,8 @@ void __add_subscription(struct call_monologue *which, struct call_monologue *to,
static void __subscribe_offer_answer_both_ways(struct call_monologue *a, struct call_monologue *b) {
__unsubscribe_all_offer_answer_subscribers(a);
__unsubscribe_all_offer_answer_subscribers(b);
__add_subscription(a, b, true);
__add_subscription(b, a, true);
__add_subscription(a, b, true, 0);
__add_subscription(b, a, true, 0);
}
@ -2835,7 +2863,7 @@ int monologue_publish(struct call_monologue *ml, GQueue *streams, struct sdp_ng_
for (GList *l = streams->head; l; l = l->next) {
struct stream_params *sp = l->data;
struct call_media *media = __get_media(ml, &media_iter, sp, flags);
struct call_media *media = __get_media(ml, &media_iter, sp, flags, -1);
__media_init_from_flags(media, NULL, sp, flags);
@ -2889,21 +2917,20 @@ int monologue_publish(struct call_monologue *ml, GQueue *streams, struct sdp_ng_
}
/* called with call->master_lock held in W */
int monologue_subscribe_request(struct call_monologue *src_ml, struct call_monologue *dst_ml,
struct sdp_ng_flags *flags)
static int monologue_subscribe_request1(struct call_monologue *src_ml, struct call_monologue *dst_ml,
struct sdp_ng_flags *flags, GList **src_media_it, GList **dst_media_it, unsigned int *index)
{
__unsubscribe_from_all(dst_ml);
__call_monologue_init_from_flags(dst_ml, flags);
GList *dst_media_it = NULL;
GList *src_media_it = NULL;
unsigned int idx_diff = 0;
for (GList *l = src_ml->last_in_sdp_streams.head; l; l = l->next) {
struct stream_params *sp = l->data;
struct call_media *dst_media = __get_media(dst_ml, &dst_media_it, sp, flags);
struct call_media *src_media = __get_media(src_ml, &src_media_it, sp, flags);
struct call_media *dst_media = __get_media(dst_ml, dst_media_it, sp, flags, (*index)++);
struct call_media *src_media = __get_media(src_ml, src_media_it, sp, flags, -1);
// track media index difference if one ml is subscribed to multiple other mls
if (idx_diff == 0 && dst_media->index > src_media->index)
idx_diff = dst_media->index - src_media->index;
if (__media_init_from_flags(src_media, dst_media, sp, flags) == 1)
continue;
@ -2942,7 +2969,7 @@ int monologue_subscribe_request(struct call_monologue *src_ml, struct call_monol
return -1;
}
__add_subscription(dst_ml, src_ml, false);
__add_subscription(dst_ml, src_ml, false, idx_diff);
__update_init_subscribers(src_ml, NULL, NULL);
__update_init_subscribers(dst_ml, NULL, NULL);
@ -2950,20 +2977,54 @@ int monologue_subscribe_request(struct call_monologue *src_ml, struct call_monol
return 0;
}
/* called with call->master_lock held in W */
int monologue_subscribe_answer(struct call_monologue *dst_ml, struct sdp_ng_flags *flags, GQueue *streams) {
if (!dst_ml->subscriptions.length)
return -1;
struct call_subscription *cs = dst_ml->subscriptions.head->data;
struct call_monologue *src_ml = cs->monologue;
int monologue_subscribe_request(const GQueue *srcs, struct call_monologue *dst_ml,
struct sdp_ng_flags *flags)
{
__unsubscribe_from_all(dst_ml);
__call_monologue_init_from_flags(dst_ml, flags);
GList *dst_media_it = NULL;
GList *src_media_it = NULL;
unsigned int index = 1; // running counter for output/dst medias
for (GList *sl = srcs->head; sl; sl = sl->next) {
struct call_subscription *cs = sl->data;
struct call_monologue *src_ml = cs->monologue;
int ret = monologue_subscribe_request1(src_ml, dst_ml, flags, &src_media_it, &dst_media_it,
&index);
if (ret)
return -1;
}
return 0;
}
/* called with call->master_lock held in W */
int monologue_subscribe_answer(struct call_monologue *dst_ml, struct sdp_ng_flags *flags, GQueue *streams) {
GList *dst_media_it = NULL;
GList *src_media_it = NULL;
GList *src_ml_it = dst_ml->subscriptions.head;
unsigned int index = 1; // running counter for input/src medias
for (GList *l = streams->head; l; l = l->next) {
struct stream_params *sp = l->data;
struct call_media *dst_media = __get_media(dst_ml, &dst_media_it, sp, flags);
struct call_media *src_media = __get_media(src_ml, &src_media_it, sp, flags);
// grab the matching source ml:
// we need to move to the next one when we've reached the last media of
// the current source ml
if (src_media_it && !src_media_it->next) {
src_ml_it = src_ml_it->next;
index = 1; // starts over at 1
}
if (!src_ml_it)
return -1;
struct call_subscription *cs = src_ml_it->data;
struct call_monologue *src_ml = cs->monologue;
struct call_media *dst_media = __get_media(dst_ml, &dst_media_it, sp, flags, -1);
struct call_media *src_media = __get_media(src_ml, &src_media_it, sp, flags, index++);
if (__media_init_from_flags(dst_media, NULL, sp, flags) == 1)
continue;
@ -2995,11 +3056,15 @@ int monologue_subscribe_answer(struct call_monologue *dst_ml, struct sdp_ng_flag
}
__update_init_subscribers(dst_ml, streams, flags);
__update_init_subscribers(src_ml, NULL, NULL);
dialogue_unkernelize(src_ml);
dialogue_unkernelize(dst_ml);
for (GList *l = dst_ml->subscriptions.head; l; l = l->next) {
struct call_subscription *cs = l->data;
struct call_monologue *src_ml = cs->monologue;
__update_init_subscribers(src_ml, NULL, NULL);
dialogue_unkernelize(src_ml);
}
return 0;
}

@ -924,6 +924,9 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) {
break;
default:
// handle values aliases from other dictionaries
if (call_ng_flags_prefix(out, s, "from-tags-", call_ng_flags_esc_str_list,
&out->from_tags))
return;
if (call_ng_flags_prefix(out, s, "SDES-no-", call_ng_flags_str_ht, &out->sdes_no))
return;
if (call_ng_flags_prefix(out, s, "SDES-", ng_sdes_option, NULL))
@ -996,6 +999,7 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu
bencode_get_alt(input, "call-id", "call-ID", &out->call_id);
bencode_dictionary_get_str(input, "from-tag", &out->from_tag);
call_ng_flags_list(out, input, "from-tags", call_ng_flags_esc_str_list, &out->from_tags);
bencode_dictionary_get_str(input, "to-tag", &out->to_tag);
bencode_dictionary_get_str(input, "via-branch", &out->via_branch);
bencode_get_alt(input, "label", "from-label", &out->label);
@ -1240,6 +1244,7 @@ void call_ng_free_flags(struct sdp_ng_flags *flags) {
g_hash_table_destroy(flags->codec_set);
if (flags->sdes_no)
g_hash_table_destroy(flags->sdes_no);
g_queue_clear_full(&flags->from_tags, free);
g_queue_clear_full(&flags->codec_offer, free);
g_queue_clear_full(&flags->codec_transcode, free);
g_queue_clear_full(&flags->codec_strip, free);
@ -2115,6 +2120,58 @@ static const char *media_block_match(struct call **call, struct call_monologue *
return NULL;
}
static void add_ml_to_sub_list(GQueue *q, struct call_monologue *ml) {
struct call_subscription *cs = g_slice_alloc0(sizeof(*cs));
cs->monologue = ml;
g_queue_push_tail(q, cs);
}
static const char *media_block_match_mult(struct call **call, GQueue *mls,
struct sdp_ng_flags *flags, bencode_item_t *input, enum call_opmode opmode)
{
call_ng_process_flags(flags, input, opmode);
if (!flags->call_id.s)
return "No call-id in message";
*call = call_get_opmode(&flags->call_id, opmode);
if (!*call)
return "Unknown call-ID";
if (flags->all) {
// get and add all offer/answer mls
for (GList *l = (*call)->monologues.head; l; l = l->next) {
struct call_monologue *ml = l->data;
if (ml->tagtype != FROM_TAG && ml->tagtype != TO_TAG)
continue;
add_ml_to_sub_list(mls, ml);
}
return NULL;
}
// is a single ml given?
struct call_monologue *ml = NULL;
const char *err = media_block_match1(*call, &ml, flags);
if (err)
return err;
if (ml) {
add_ml_to_sub_list(mls, ml);
return NULL;
}
// handle from-tag list
for (GList *l = flags->from_tags.head; l; l = l->next) {
str *s = l->data;
struct call_monologue *ml = call_get_monologue(*call, s);
if (!ml)
ilog(LOG_WARN, "Given from-tag " STR_FORMAT_M " not found", STR_FMT_M(s));
else
add_ml_to_sub_list(mls, ml);
}
if (!mls->length)
return "No monologues matched";
return NULL;
}
// XXX these are all identical - unify and use a flags int and/or callback
const char *call_start_forwarding_ng(bencode_item_t *input, bencode_item_t *output) {
@ -2585,20 +2642,28 @@ const char *call_subscribe_request_ng(bencode_item_t *input, bencode_item_t *out
AUTO_CLEANUP(struct sdp_ng_flags flags, call_ng_free_flags);
char rand_buf[65];
AUTO_CLEANUP_NULL(struct call *call, call_unlock_release);
struct call_monologue *source_ml;
AUTO_CLEANUP(GQueue srcs, call_subscriptions_clear) = G_QUEUE_INIT;
AUTO_CLEANUP(str sdp_out, str_free_dup) = STR_NULL;
// get source monologue
err = media_block_match(&call, &source_ml, &flags, input, OP_REQUEST);
err = media_block_match_mult(&call, &srcs, &flags, input, OP_REQUEST);
if (err)
return err;
if (flags.sdp.len)
ilog(LOG_INFO, "Subscribe-request with SDP received - ignoring SDP");
if (!source_ml)
if (!srcs.length)
return "No call participant specified";
if (!source_ml->last_in_sdp.len || !source_ml->last_in_sdp_parsed.length)
return "No SDP known for this from-tag";
// special case with just one source: we can use the original SDP
struct call_monologue *sdp_ml = NULL;
if (srcs.length == 1) {
struct call_subscription *cs = srcs.head->data;
sdp_ml = cs->monologue;
if (!sdp_ml->last_in_sdp.len || !sdp_ml->last_in_sdp_parsed.length)
sdp_ml = NULL;
}
// the `label=` option was possibly used above to select the from-tag --
// switch it out with `to-label=` or `set-label=` for monologue_subscribe_request
@ -2613,20 +2678,52 @@ const char *call_subscribe_request_ng(bencode_item_t *input, bencode_item_t *out
}
struct call_monologue *dest_ml = call_get_or_create_monologue(call, &flags.to_tag);
struct sdp_chopper *chopper = sdp_chopper_new(&source_ml->last_in_sdp);
bencode_buffer_destroy_add(output->buffer, (free_func_t) sdp_chopper_destroy, chopper);
// rewrite SDP, or create new?
struct sdp_chopper *chopper = NULL;
if (sdp_ml) {
chopper = sdp_chopper_new(&sdp_ml->last_in_sdp);
bencode_buffer_destroy_add(output->buffer, (free_func_t) sdp_chopper_destroy, chopper);
}
int ret = monologue_subscribe_request(source_ml, dest_ml, &flags);
int ret = monologue_subscribe_request(&srcs, dest_ml, &flags);
if (ret)
return "Failed to request subscription";
ret = sdp_replace(chopper, &source_ml->last_in_sdp_parsed, dest_ml, &flags);
if (ret)
return "Failed to rewrite SDP";
if (chopper && sdp_ml) {
ret = sdp_replace(chopper, &sdp_ml->last_in_sdp_parsed, dest_ml, &flags);
if (ret)
return "Failed to rewrite SDP";
}
else {
// create new SDP
ret = sdp_create(&sdp_out, dest_ml, &flags);
}
// place return output SDP
if (chopper) {
if (chopper->output->len)
bencode_dictionary_add_string_len(output, "sdp", chopper->output->str,
chopper->output->len);
}
else if (sdp_out.len) {
bencode_buffer_destroy_add(output->buffer, g_free, sdp_out.s);
bencode_dictionary_add_str(output, "sdp", &sdp_out);
sdp_out = STR_NULL; // ownership passed to output
}
// add single response ml tag if there's just one, but always add a list
if (srcs.length == 1) {
struct call_subscription *cs = srcs.head->data;
struct call_monologue *source_ml = cs->monologue;
bencode_dictionary_add_str_dup(output, "from-tag", &source_ml->tag);
}
bencode_item_t *from_list = bencode_dictionary_add_list(output, "from-tags");
for (GList *l = srcs.head; l; l = l->next) {
struct call_subscription *cs = l->data;
struct call_monologue *source_ml = cs->monologue;
bencode_list_add_str_dup(from_list, &source_ml->tag);
}
if (chopper->output->len)
bencode_dictionary_add_string_len(output, "sdp", chopper->output->str, chopper->output->len);
bencode_dictionary_add_str_dup(output, "from-tag", &source_ml->tag);
bencode_dictionary_add_str_dup(output, "to-tag", &dest_ml->tag);
return NULL;

@ -877,9 +877,9 @@ INLINE struct codec_handler *codec_handler_lookup(GHashTable *ht, int pt, struct
void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
const struct sdp_ng_flags *flags, const struct stream_params *sp)
{
ilogs(codec, LOG_DEBUG, "Setting up codec handlers for " STR_FORMAT_M " -> " STR_FORMAT_M " (media #%u)",
STR_FMT_M(&receiver->monologue->tag), STR_FMT_M(&sink->monologue->tag),
receiver->index);
ilogs(codec, LOG_DEBUG, "Setting up codec handlers for " STR_FORMAT_M " #%u -> " STR_FORMAT_M " #%u",
STR_FMT_M(&receiver->monologue->tag), receiver->index,
STR_FMT_M(&sink->monologue->tag), sink->index);
MEDIA_CLEAR(receiver, GENERATOR);
MEDIA_CLEAR(sink, GENERATOR);

@ -431,7 +431,13 @@ static const char *janus_videoroom_join(struct websocket_message *wm, struct jan
flags.no_rtcp_attr = 1;
flags.sdes_off = 1;
int ret = monologue_subscribe_request(source_ml, dest_ml, &flags);
AUTO_CLEANUP(GQueue srcs, call_subscriptions_clear) = G_QUEUE_INIT;
struct call_subscription *cs = g_slice_alloc0(sizeof(*cs));
cs->monologue = source_ml;
g_queue_push_tail(&srcs, cs);
int ret = monologue_subscribe_request(&srcs, dest_ml, &flags);
if (ret)
return "Subscribe error";

@ -1586,7 +1586,7 @@ static int json_link_tags(struct call *c, struct redis_list *tags, struct redis_
other_ml = l->data;
if (!other_ml)
return -1;
__add_subscription(ml, other_ml, true);
__add_subscription(ml, other_ml, true, 0);
}
g_queue_clear(&q);
@ -1596,7 +1596,7 @@ static int json_link_tags(struct call *c, struct redis_list *tags, struct redis_
other_ml = l->data;
if (!other_ml)
return -1;
__add_subscription(ml, other_ml, false);
__add_subscription(ml, other_ml, false, 0); // XXX fix indexing
}
g_queue_clear(&q);
@ -1604,7 +1604,7 @@ static int json_link_tags(struct call *c, struct redis_list *tags, struct redis_
if (!ml->subscriptions.length) {
other_ml = redis_list_get_ptr(tags, &tags->rh[i], "active");
if (other_ml)
__add_subscription(ml, other_ml, true);
__add_subscription(ml, other_ml, true, 0);
}
if (json_build_list(&q, c, "other_tags", &c->callid, i, tags, root_reader))

@ -403,6 +403,7 @@ struct call_media {
struct call_subscription {
struct call_monologue *monologue;
GList *link; // link into the corresponding opposite list
unsigned int media_offset; // 0 if media indexes match up
unsigned int offer_answer:1; // bidirectional, exclusive
};
@ -600,11 +601,13 @@ struct call_monologue *__monologue_create(struct call *call);
void __monologue_tag(struct call_monologue *ml, const str *tag);
void __monologue_viabranch(struct call_monologue *ml, const str *viabranch);
struct packet_stream *__packet_stream_new(struct call *call);
void __add_subscription(struct call_monologue *ml, struct call_monologue *other, bool offer_answer);
void __add_subscription(struct call_monologue *ml, struct call_monologue *other, bool offer_answer,
unsigned int media_offset);
void free_sink_handler(void *);
void __add_sink_handler(GQueue *, struct packet_stream *);
void call_subscription_free(void *);
void call_subscriptions_clear(GQueue *q);
struct call *call_get_or_create(const str *callid, bool foreign, bool exclusive);
@ -620,7 +623,7 @@ int monologue_offer_answer(struct call_monologue *dialogue[2], GQueue *streams,
void codecs_offer_answer(struct call_media *media, struct call_media *other_media,
struct stream_params *sp, struct sdp_ng_flags *flags);
int monologue_publish(struct call_monologue *ml, GQueue *streams, struct sdp_ng_flags *flags);
int monologue_subscribe_request(struct call_monologue *src, struct call_monologue *dst, struct sdp_ng_flags *);
int monologue_subscribe_request(const GQueue *srcs, struct call_monologue *dst, struct sdp_ng_flags *);
int monologue_subscribe_answer(struct call_monologue *dst, struct sdp_ng_flags *,
GQueue *);
int monologue_unsubscribe(struct call_monologue *dst, struct sdp_ng_flags *);

@ -21,6 +21,7 @@ struct sdp_ng_flags {
enum call_opmode opmode;
str call_id;
str from_tag;
GQueue from_tags;
str to_tag;
str via_branch;
str sdp;

@ -160,7 +160,7 @@ sub subscribe_request {
my ($name, $req, $sdp_exp) = @_;
my $resp = rtpe_req('subscribe request', $name, $req);
my @matches = sdp_match('subscribe request', $name, $resp->{sdp}, $sdp_exp);
return ($resp->{'from-tag'}, $resp->{'to-tag'}, @matches);
return ($resp->{'from-tag'}, $resp->{'to-tag'}, $resp->{'from-tags'}, @matches);
}
sub subscribe_answer {
my ($name, $req, $sdp) = @_;

@ -11,16 +11,419 @@ use POSIX;
autotest_start(qw(--config-file=none -t -1 -i 203.0.113.1 -i 2001:db8:4321::1
-n 2223 -c 12345 -f -L 7 -E -u 2222 --silence-detect=1))
-n 2223 -c 12345 -f -L 7 -E -u 2222 --log-level-internals=7))
or die;
my ($sock_a, $sock_b, $sock_c, $sock_d, $port_a, $port_b, $port_c, $ssrc_a, $ssrc_b, $resp,
$sock_ax, $sock_bx, $port_ax, $port_bx,
$sock_ax, $sock_bx, $port_ax, $port_bx, $port_d,
$srtp_ctx_a, $srtp_ctx_b, $srtp_ctx_a_rev, $srtp_ctx_b_rev, $ufrag_a, $ufrag_b,
@ret1, @ret2, @ret3, @ret4, $srtp_key_a, $srtp_key_b, $ts, $seq,
$ftr, $ttr, $ttr2);
$ftr, $ttr, $fts, $ttr2);
($sock_a, $sock_b, $sock_c, $sock_d) =
new_call([qw(198.51.100.14 6080)], [qw(198.51.100.14 6082)], [qw(198.51.100.14 6084)],
[qw(198.51.100.14 6086)]);
($port_a) = offer('"all" sub',
{ }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6080 RTP/AVP 0 8
c=IN IP4 198.51.100.14
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.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
($port_b) = answer('"all" sub',
{ }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6082 RTP/AVP 0 8
c=IN IP4 198.51.100.14
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.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
snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 4000, 7000, 0x6543, "\x00" x 160));
($ftr, $ttr, $fts, $port_c, undef, $port_d) = subscribe_request('"all" sub',
{ 'flags' => ['all'] }, <<SDP);
v=0
o=- SDP_VERSION IN IP4 203.0.113.1
s=RTPE_VERSION
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=sendonly
a=rtcp:PORT
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=sendonly
a=rtcp:PORT
SDP
is $ftr, undef, 'from-tag matches';
is_deeply $fts, [ft(), tt()], 'from-tags match';
subscribe_answer('"all" sub',
{ 'to-tag' => $ttr, flags => ['allow transcoding'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6084 RTP/AVP 8
c=IN IP4 198.51.100.14
a=recvonly
m=audio 6086 RTP/AVP 8
c=IN IP4 198.51.100.14
a=recvonly
SDP
snd($sock_a, $port_b, rtp(0, 4001, 7160, 0x6543, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 4001, 7160, 0x6543, "\x00" x 160));
rcv($sock_c, $port_c, rtpm(8, 4001, 7160, -1, "\x2a" x 160));
snd($sock_b, $port_a, rtp(0, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_d, $port_d, rtpm(8, 2001, 4160, -1, "\x2a" x 160));
($sock_a, $sock_b, $sock_c, $sock_d) =
new_call([qw(198.51.100.14 6088)], [qw(198.51.100.14 6090)], [qw(198.51.100.14 6092)],
[qw(198.51.100.14 6094)]);
($port_a) = offer('sub to multiple tags',
{ }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6088 RTP/AVP 0 8
c=IN IP4 198.51.100.14
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.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
($port_b) = answer('sub to multiple tags',
{ }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6090 RTP/AVP 0 8
c=IN IP4 198.51.100.14
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.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
snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 4000, 7000, 0x6543, "\x00" x 160));
($ftr, $ttr, $fts, $port_c, undef, $port_d) = subscribe_request('sub to multiple tags',
{ 'from-tags' => [ft(), tt()] }, <<SDP);
v=0
o=- SDP_VERSION IN IP4 203.0.113.1
s=RTPE_VERSION
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=sendonly
a=rtcp:PORT
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=sendonly
a=rtcp:PORT
SDP
is $ftr, undef, 'from-tag matches';
is_deeply $fts, [ft(), tt()], 'from-tags match';
subscribe_answer('sub to multiple tags',
{ 'to-tag' => $ttr, flags => ['allow transcoding'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6092 RTP/AVP 8
c=IN IP4 198.51.100.14
a=recvonly
m=audio 6094 RTP/AVP 8
c=IN IP4 198.51.100.14
a=recvonly
SDP
snd($sock_a, $port_b, rtp(0, 4001, 7160, 0x6543, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 4001, 7160, 0x6543, "\x00" x 160));
rcv($sock_c, $port_c, rtpm(8, 4001, 7160, -1, "\x2a" x 160));
snd($sock_b, $port_a, rtp(0, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_d, $port_d, rtpm(8, 2001, 4160, -1, "\x2a" x 160));
($sock_a, $sock_b, $sock_c, $sock_d) =
new_call([qw(198.51.100.14 6096)], [qw(198.51.100.14 6098)], [qw(198.51.100.14 6100)],
[qw(198.51.100.14 6102)]);
($port_a) = offer('sub to multiple tags via flags',
{ }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6096 RTP/AVP 0 8
c=IN IP4 198.51.100.14
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.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
($port_b) = answer('sub to multiple tags via flags',
{ }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6098 RTP/AVP 0 8
c=IN IP4 198.51.100.14
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.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
snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 4000, 7000, 0x6543, "\x00" x 160));
($ftr, $ttr, $fts, $port_c, undef, $port_d) = subscribe_request('sub to multiple tags via flags',
{ flags => ['from-tags-' . ft(), 'from-tags-' . tt()] }, <<SDP);
v=0
o=- SDP_VERSION IN IP4 203.0.113.1
s=RTPE_VERSION
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=sendonly
a=rtcp:PORT
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=sendonly
a=rtcp:PORT
SDP
is $ftr, undef, 'from-tag matches';
is_deeply $fts, [ft(), tt()], 'from-tags match';
subscribe_answer('sub to multiple tags via flags',
{ 'to-tag' => $ttr, flags => ['allow transcoding'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6100 RTP/AVP 8
c=IN IP4 198.51.100.14
a=recvonly
m=audio 6102 RTP/AVP 8
c=IN IP4 198.51.100.14
a=recvonly
SDP
snd($sock_a, $port_b, rtp(0, 4001, 7160, 0x6543, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 4001, 7160, 0x6543, "\x00" x 160));
rcv($sock_c, $port_c, rtpm(8, 4001, 7160, -1, "\x2a" x 160));
snd($sock_b, $port_a, rtp(0, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_d, $port_d, rtpm(8, 2001, 4160, -1, "\x2a" x 160));
($sock_a, $sock_b, $sock_c, $sock_d) =
new_call([qw(198.51.100.14 6104)], [qw(198.51.100.14 6106)], [qw(198.51.100.14 6108)],
[qw(198.51.100.14 6110)]);
($port_a) = offer('sub to multiple tags - reverse',
{ }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6104 RTP/AVP 0 8
c=IN IP4 198.51.100.14
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.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
($port_b) = answer('sub to multiple tags - reverse',
{ }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6106 RTP/AVP 0 8
c=IN IP4 198.51.100.14
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.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
snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 4000, 7000, 0x6543, "\x00" x 160));
($ftr, $ttr, $fts, $port_c, undef, $port_d) = subscribe_request('sub to multiple tags - reverse',
{ 'from-tags' => [tt(), ft()] }, <<SDP);
v=0
o=- SDP_VERSION IN IP4 203.0.113.1
s=RTPE_VERSION
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=sendonly
a=rtcp:PORT
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=sendonly
a=rtcp:PORT
SDP
is $ftr, undef, 'from-tag matches';
is_deeply $fts, [tt(), ft()], 'from-tags match';
subscribe_answer('sub to multiple tags - reverse',
{ 'to-tag' => $ttr, flags => ['allow transcoding'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 6108 RTP/AVP 8
c=IN IP4 198.51.100.14
a=recvonly
m=audio 6110 RTP/AVP 8
c=IN IP4 198.51.100.14
a=recvonly
SDP
snd($sock_a, $port_b, rtp(0, 4001, 7160, 0x6543, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 4001, 7160, 0x6543, "\x00" x 160));
rcv($sock_d, $port_d, rtpm(8, 4001, 7160, -1, "\x2a" x 160));
snd($sock_b, $port_a, rtp(0, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_c, $port_c, rtpm(8, 2001, 4160, -1, "\x2a" x 160));
@ -79,7 +482,7 @@ snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
($ssrc_b) = rcv($sock_b, $port_a, rtpm(0, 4000, 7000, -1, "\x00" x 160));
($ftr, $ttr, $port_c) = subscribe_request('sub, multi codec, sub w diff codec',
($ftr, $ttr, undef, $port_c) = subscribe_request('sub, multi codec, sub w diff codec',
{ 'from-tag' => ft() }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -167,7 +570,7 @@ snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
($ssrc_b) = rcv($sock_b, $port_a, rtpm(0, 4000, 7000, -1, "\x00" x 160));
($ftr, $ttr, $port_c) = subscribe_request('sub w tc - acc',
($ftr, $ttr, undef, $port_c) = subscribe_request('sub w tc - acc',
{ 'from-tag' => ft(), codec => { transcode => ['PCMA'] } }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -254,7 +657,7 @@ snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
($ssrc_b) = rcv($sock_b, $port_a, rtpm(0, 4000, 7000, -1, "\x00" x 160));
($ftr, $ttr, $port_c) = subscribe_request('sub w tc - rej',
($ftr, $ttr, undef, $port_c) = subscribe_request('sub w tc - rej',
{ 'from-tag' => ft(), codec => { transcode => ['PCMA'] } }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -341,7 +744,7 @@ snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
($ssrc_b) = rcv($sock_b, $port_a, rtpm(0, 4000, 7000, -1, "\x00" x 160));
($ftr, $ttr, $port_c) = subscribe_request('simple sub',
($ftr, $ttr, undef, $port_c) = subscribe_request('simple sub',
{ 'from-tag' => ft() }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -427,7 +830,7 @@ snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
($ssrc_b) = rcv($sock_b, $port_a, rtpm(0, 4000, 7000, -1, "\x00" x 160));
($ftr, $ttr, $port_c) = subscribe_request('simple sub w label',
($ftr, $ttr, undef, $port_c) = subscribe_request('simple sub w label',
{ label => 'foo' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -513,7 +916,7 @@ snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
($ssrc_b) = rcv($sock_b, $port_a, rtpm(0, 4000, 7000, -1, "\x00" x 160));
($ftr, $ttr, $port_c) = subscribe_request('simple sub w to-tag label',
($ftr, $ttr, undef, $port_c) = subscribe_request('simple sub w to-tag label',
{ label => 'bar' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -599,7 +1002,7 @@ snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
($ssrc_b) = rcv($sock_b, $port_a, rtpm(0, 4000, 7000, -1, "\x00" x 160));
($ftr, $ttr, $port_c, undef, $srtp_key_a) = subscribe_request('SRTP sub',
($ftr, $ttr, undef, $port_c, undef, $srtp_key_a) = subscribe_request('SRTP sub',
{ 'from-tag' => ft(), 'transport-protocol' => 'RTP/SAVP',
SDES => ['no-AEAD_AES_256_GCM', 'no-AEAD_AES_128_GCM'] }, <<SDP);
v=0
@ -705,7 +1108,7 @@ snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
($ssrc_b) = rcv($sock_b, $port_a, rtpm(0, 4000, 7000, -1, "\x00" x 160));
($ftr, $ttr, $port_c, undef, undef, undef, undef, undef, $srtp_key_a) = subscribe_request('SRTP sub',
($ftr, $ttr, undef, $port_c, undef, undef, undef, undef, undef, $srtp_key_a) = subscribe_request('SRTP sub',
{ 'from-tag' => ft(), 'transport-protocol' => 'RTP/SAVP',
SDES => ['no-AEAD_AES_256_GCM', 'no-AEAD_AES_128_GCM'] }, <<SDP);
v=0
@ -838,7 +1241,7 @@ srtp_snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_b
srtp_snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160), $srtp_ctx_a);
($ssrc_b) = srtp_rcv($sock_b, $port_a, rtpm(0, 4000, 7000, -1, "\x00" x 160), $srtp_ctx_a);
($ftr, $ttr, $port_c) = subscribe_request('SRTP call RTP sub',
($ftr, $ttr, undef, $port_c) = subscribe_request('SRTP call RTP sub',
{ 'from-tag' => ft(), 'transport-protocol' => 'RTP/AVP', }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -925,7 +1328,7 @@ snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 4000, 7000, 0x6543, "\x00" x 160));
($ssrc_b) = rcv($sock_b, $port_a, rtpm(0, 4000, 7000, -1, "\x00" x 160));
($ftr, $ttr, $port_c) = subscribe_request('ICE sub',
($ftr, $ttr, undef, $port_c) = subscribe_request('ICE sub',
{ 'from-tag' => ft(), ICE => 'force' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -997,7 +1400,7 @@ SDP
snd($sock_a, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160));
($ftr, $ttr, $port_b) = subscribe_request('publish/subscribe',
($ftr, $ttr, undef, $port_b) = subscribe_request('publish/subscribe',
{ 'from-tag' => ft() }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -1026,7 +1429,7 @@ SDP
snd($sock_a, $port_a, rtp(0, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_b, $port_b, rtpm(0, 2001, 4160, 0x3456, "\x00" x 160));
($ftr, $ttr, $port_b) = subscribe_request('publish/subscribe',
($ftr, $ttr, undef, $port_b) = subscribe_request('publish/subscribe',
{ 'from-tag' => ft() }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -1087,7 +1490,7 @@ SDP
snd($sock_a, $port_a, rtp(8, 2000, 4000, 0x3456, "\x00" x 160));
($ftr, $ttr, $port_b) = subscribe_request('publish/subscribe w codec-accept',
($ftr, $ttr, undef, $port_b) = subscribe_request('publish/subscribe w codec-accept',
{ 'from-tag' => ft() }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -1116,7 +1519,7 @@ SDP
snd($sock_a, $port_a, rtp(8, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_b, $port_b, rtpm(8, 2001, 4160, 0x3456, "\x00" x 160));
($ftr, $ttr, $port_b) = subscribe_request('publish/subscribe w codec-accept',
($ftr, $ttr, undef, $port_b) = subscribe_request('publish/subscribe w codec-accept',
{ 'from-tag' => ft() }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -1177,7 +1580,7 @@ SDP
snd($sock_a, $port_a, rtp(8, 2000, 4000, 0x3456, "\x00" x 160));
($ftr, $ttr, $port_b) = subscribe_request('publish/subscribe w unsupp and t/c',
($ftr, $ttr, undef, $port_b) = subscribe_request('publish/subscribe w unsupp and t/c',
{ 'from-tag' => ft() }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
@ -1206,7 +1609,7 @@ SDP
snd($sock_a, $port_a, rtp(8, 2001, 4160, 0x3456, "\x00" x 160));
rcv($sock_b, $port_b, rtpm(8, 2001, 4160, 0x3456, "\x00" x 160));
($ftr, $ttr, $port_b) = subscribe_request('publish/subscribe w unsupp and t/c',
($ftr, $ttr, undef, $port_b) = subscribe_request('publish/subscribe w unsupp and t/c',
{ 'from-tag' => ft(), codec => { strip => ['PCMA'], transcode => ['PCMU'] } }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1

@ -99,6 +99,7 @@ GetOptions(
'set-label=s' => \$options{'set-label'},
'from-label=s' => \$options{'from-label'},
'to-label=s' => \$options{'to-label'},
'from-tags=s@' => \$options{'from-tags'},
) or die;
my $cmd = shift(@ARGV) or die;
@ -117,7 +118,7 @@ for my $x (split(/,/, 'trust address,symmetric,asymmetric,unidirectional,force,s
for my $x (split(/,/, 'origin,session connection,sdp version,username,session-name,zero-address')) {
defined($options{'replace-' . $x}) and push(@{$packet{replace}}, $x);
}
for my $x (split(/,/, 'rtcp-mux,SDES,supports,T.38,OSRTP,received-from')) {
for my $x (split(/,/, 'rtcp-mux,SDES,supports,T.38,OSRTP,received-from,from-tags')) {
$packet{$x} = $options{$x}
if defined($options{$x}) && ref($options{$x}) eq 'ARRAY';
}

Loading…
Cancel
Save