diff --git a/README.md b/README.md index ab723bd87..54e4e2d85 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/daemon/call.c b/daemon/call.c index 49606f81d..5656a1ada 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -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; } diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 126cf3433..76df9432d 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -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; diff --git a/daemon/codec.c b/daemon/codec.c index ca1a9542d..f781411e8 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -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); diff --git a/daemon/janus.c b/daemon/janus.c index 6a209a8d6..6d90cff1f 100644 --- a/daemon/janus.c +++ b/daemon/janus.c @@ -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"; diff --git a/daemon/redis.c b/daemon/redis.c index 4fe6902cc..8d60030c7 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -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)) diff --git a/include/call.h b/include/call.h index 35c5592a4..2e26d74ab 100644 --- a/include/call.h +++ b/include/call.h @@ -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 *); diff --git a/include/call_interfaces.h b/include/call_interfaces.h index 1cb4defef..01584a074 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -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; diff --git a/perl/NGCP/Rtpengine/AutoTest.pm b/perl/NGCP/Rtpengine/AutoTest.pm index d28002eba..e4cd6790e 100644 --- a/perl/NGCP/Rtpengine/AutoTest.pm +++ b/perl/NGCP/Rtpengine/AutoTest.pm @@ -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) = @_; diff --git a/t/auto-daemon-tests-pubsub.pl b/t/auto-daemon-tests-pubsub.pl index 28da91a0f..87dffae30 100755 --- a/t/auto-daemon-tests-pubsub.pl +++ b/t/auto-daemon-tests-pubsub.pl @@ -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 diff --git a/utils/rtpengine-ng-client b/utils/rtpengine-ng-client index 8bedb6a18..84cf1753e 100755 --- a/utils/rtpengine-ng-client +++ b/utils/rtpengine-ng-client @@ -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'; }