From 7bf431a1fad2a1c93d193384b81102cbbe3d1fae Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 14 Aug 2024 11:39:08 -0400 Subject: [PATCH] MT#55283 support bencode format to Redis Change-Id: Ib66b740558f16f59890cc1ce0f1fb444dee9ad41 --- daemon/main.c | 13 + daemon/redis.c | 125 +- docs/rtpengine.md | 10 + include/main.h | 6 + t/Makefile | 7 +- t/auto-daemon-tests-redis-json.pl | 2631 +++++++++++++++++++++++++++++ t/auto-daemon-tests-redis.pl | 4 +- utils/ng-load-tester.py | 70 +- 8 files changed, 2806 insertions(+), 60 deletions(-) create mode 100755 t/auto-daemon-tests-redis-json.pl diff --git a/daemon/main.c b/daemon/main.c index 5b5c9da42..2e1496676 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -516,6 +516,7 @@ static void options(int *argc, char ***argv) { bool nftables_status = false; g_autoptr(char) nftables_family = NULL; #endif + g_autoptr(char) redis_format = NULL; GOptionEntry e[] = { { "table", 't', 0, G_OPTION_ARG_INT, &rtpe_config.kernel_table, "Kernel table to use", "INT" }, @@ -559,6 +560,7 @@ static void options(int *argc, char ***argv) { { "redis-disable-time", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_disable_time, "Number of seconds redis communication is disabled because of errors", "INT" }, { "redis-cmd-timeout", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_cmd_timeout, "Sets a timeout in milliseconds for redis commands", "INT" }, { "redis-connect-timeout", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_connect_timeout, "Sets a timeout in milliseconds for redis connections", "INT" }, + { "redis-format", 0, 0, G_OPTION_ARG_STRING, &redis_format, "Format for persistent storage in Redis/KeyDB", "native|bencode|JSON" }, #if 0 // temporarily disabled, see discussion on https://github.com/sipwise/rtpengine/commit/2ebf5a1526c1ce8093b3011a1e23c333b3f99400 // related to Change-Id: I83d9b9a844f4f494ad37b44f5d1312f272beff3f @@ -787,6 +789,17 @@ static void options(int *argc, char ***argv) { } } + if (redis_format) { + if (!strcasecmp(redis_format, "native")) + rtpe_config.redis_format = 0; + else if (!strcasecmp(redis_format, "bencode")) + rtpe_config.redis_format = REDIS_FORMAT_BENCODE; + else if (!strcasecmp(redis_format, "JSON")) + rtpe_config.redis_format = REDIS_FORMAT_JSON; + else + die("Invalid --redis-format value given"); + } + parse_listen_list(&rtpe_config.tcp_listen_ep, listenps, "listen-tcp"); parse_listen_list(&rtpe_config.udp_listen_ep, listenudps, "listen-udp"); parse_listen_list(&rtpe_config.ng_listen_ep, listenngs, "listen-ng"); diff --git a/daemon/redis.c b/daemon/redis.c index 9f1da720e..8ff67c161 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -50,7 +50,11 @@ struct redis *rtpe_redis_write_disabled; struct redis *rtpe_redis_notify; -static const ng_parser_t *redis_parser = &ng_parser_json; +static __thread const ng_parser_t *redis_parser = &ng_parser_json; +static const ng_parser_t *const redis_format_parsers[__REDIS_FORMAT_MAX] = { + &ng_parser_native, + &ng_parser_json, +}; INLINE redisReply *redis_expect(int type, redisReply *r) { @@ -1983,7 +1987,9 @@ static void json_restore_call(struct redis *r, const str *callid, bool foreign) int i; atomic64 a64; JsonNode *json_root = NULL; - JsonParser *parser =0; + JsonParser *parser = NULL; + bencode_item_t *benc_root = NULL; + bencode_buffer_t buf = {0}; mutex_lock(&r->lock); rr_jsonStr = redis_get(r, REDIS_REPLY_STRING, "GET " PB, PBSTR(callid)); @@ -1996,16 +2002,34 @@ static void json_restore_call(struct redis *r, const str *callid, bool foreign) if (!rr_jsonStr) goto err1; - parser = json_parser_new(); - err = "could not parse JSON data"; - if (!json_parser_load_from_data (parser, rr_jsonStr->str, -1, NULL)) - goto err1; - json_root = json_parser_get_root(parser); - err = "could not read JSON data"; - if (!json_root) - goto err1; parser_arg root = {.json = json_root}; + if (rr_jsonStr->str[0] == '{') { + parser = json_parser_new(); + err = "could not parse JSON data"; + if (!json_parser_load_from_data (parser, rr_jsonStr->str, -1, NULL)) + goto err1; + json_root = json_parser_get_root(parser); + err = "could not read JSON data"; + if (!json_root) + goto err1; + root.json = json_root; + redis_parser = &ng_parser_json; + } + else if (rr_jsonStr->str[0] == 'd') { + int ret = bencode_buffer_init(&buf); + err = "failed to initialise bencode buffer"; + if (ret) + goto err1; + err = "failed to decode bencode dictionary"; + benc_root = bencode_decode_expect_str(&buf, &STR_LEN(rr_jsonStr->str, rr_jsonStr->len), + BENCODE_DICTIONARY); + if (!benc_root) + goto err1; + redis_parser = &ng_parser_native; + root.benc = benc_root; + } + c = call_get_or_create(callid, false); err = "failed to create call struct"; if (!c) @@ -2147,6 +2171,7 @@ err1: g_object_unref (parser); if (rr_jsonStr) freeReplyObject(rr_jsonStr); + bencode_buffer_free(&buf); if (err) { mutex_lock(&r->lock); if (r->ctx && r->ctx->err) @@ -2291,37 +2316,37 @@ err: int len = snprintf(tmp,sizeof(tmp), f, __VA_ARGS__); \ char enc[len * 3 + 1]; \ str_uri_encode_len(enc, tmp, len); \ - redis_parser->list_add_str_dup(inner, &STR_NC(enc)); \ + parser->list_add_str_dup(inner, &STR_NC(enc)); \ } while (0) #define JSON_SET_NSTRING(a,b,c,...) do { \ int len = snprintf(tmp,sizeof(tmp), c, __VA_ARGS__); \ char enc[len * 3 + 1]; \ str_uri_encode_len(enc, tmp, len); \ snprintf(tmp,sizeof(tmp), a,b); \ - redis_parser->dict_add_str_dup(inner, tmp, &STR_NC(enc)); \ + parser->dict_add_str_dup(inner, tmp, &STR_NC(enc)); \ } while (0) #define JSON_SET_NSTRING_CSTR(a,b,d) JSON_SET_NSTRING_LEN(a, b, strlen(d), d) #define JSON_SET_NSTRING_LEN(a,b,l,d) do { \ char enc[l * 3 + 1]; \ str_uri_encode_len(enc, d, l); \ snprintf(tmp,sizeof(tmp), a,b); \ - redis_parser->dict_add_str_dup(inner, tmp, &STR_NC(enc)); \ + parser->dict_add_str_dup(inner, tmp, &STR_NC(enc)); \ } while (0) #define JSON_SET_SIMPLE(a,c,...) do { \ int len = snprintf(tmp,sizeof(tmp), c, __VA_ARGS__); \ char enc[len * 3 + 1]; \ str_uri_encode_len(enc, tmp, len); \ - redis_parser->dict_add_str_dup(inner, a, &STR_NC(enc)); \ + parser->dict_add_str_dup(inner, a, &STR_NC(enc)); \ } while (0) #define JSON_SET_SIMPLE_LEN(a,l,d) do { \ char enc[l * 3 + 1]; \ str_uri_encode_len(enc, d, l); \ - redis_parser->dict_add_str_dup(inner, a, &STR_NC(enc)); \ + parser->dict_add_str_dup(inner, a, &STR_NC(enc)); \ } while (0) -#define JSON_SET_SIMPLE_CSTR(a,d) redis_parser->dict_add_str_dup(inner, a, &STR(d)) -#define JSON_SET_SIMPLE_STR(a,d) redis_parser->dict_add_str_dup(inner, a, d) +#define JSON_SET_SIMPLE_CSTR(a,d) parser->dict_add_str_dup(inner, a, &STR(d)) +#define JSON_SET_SIMPLE_STR(a,d) parser->dict_add_str_dup(inner, a, d) -static void json_update_crypto_params(parser_arg inner, const char *key, struct crypto_params *p) { +static void json_update_crypto_params(const ng_parser_t *parser, parser_arg inner, const char *key, struct crypto_params *p) { char tmp[2048]; if (!p->crypto_suite) @@ -2339,7 +2364,7 @@ static void json_update_crypto_params(parser_arg inner, const char *key, struct JSON_SET_NSTRING_LEN("%s-mki", key, p->mki_len, (char *) p->mki); } -static int json_update_sdes_params(parser_arg inner, const char *pref, +static int json_update_sdes_params(const ng_parser_t *parser, parser_arg inner, const char *pref, unsigned int unique_id, const char *k, sdes_q *q) { @@ -2356,7 +2381,7 @@ static int json_update_sdes_params(parser_arg inner, const char *pref, return -1; JSON_SET_NSTRING("%s_tag", key, "%u", cps->tag); - json_update_crypto_params(inner, key, p); + json_update_crypto_params(parser, inner, key, p); snprintf(keybuf, sizeof(keybuf), "%s-%u", k, iter++); key = keybuf; @@ -2365,7 +2390,7 @@ static int json_update_sdes_params(parser_arg inner, const char *pref, return 0; } -static void json_update_dtls_fingerprint(parser_arg inner, const char *pref, +static void json_update_dtls_fingerprint(const ng_parser_t *parser, parser_arg inner, const char *pref, unsigned int unique_id, const struct dtls_fingerprint *f) { @@ -2383,11 +2408,12 @@ static void json_update_dtls_fingerprint(parser_arg inner, const char *pref, static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { char tmp[2048]; + const ng_parser_t *parser = ctx->parser; - parser_arg root = redis_parser->dict(ctx); + parser_arg root = parser->dict(ctx); { - parser_arg inner = redis_parser->dict_add_dict(root, "json"); + parser_arg inner = parser->dict_add_dict(root, "json"); { JSON_SET_SIMPLE("created","%lli", timeval_us(&c->created)); @@ -2424,7 +2450,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { stream_fd *sfd = l->data; snprintf(tmp, sizeof(tmp), "sfd-%u", sfd->unique_id); - inner = redis_parser->dict_add_dict_dup(root, tmp); + inner = parser->dict_add_dict_dup(root, tmp); { JSON_SET_SIMPLE_CSTR("pref_family", sfd->local_intf->logical->preferred_family->rfc_name); @@ -2434,7 +2460,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { JSON_SET_SIMPLE("local_intf_uid","%u", sfd->local_intf->unique_id); JSON_SET_SIMPLE("stream","%u", sfd->stream->unique_id); - json_update_crypto_params(inner, "", &sfd->crypto.params); + json_update_crypto_params(parser, inner, "", &sfd->crypto.params); } } // --- for @@ -2446,7 +2472,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { LOCK(&ps->out_lock); snprintf(tmp, sizeof(tmp), "stream-%u", ps->unique_id); - inner = redis_parser->dict_add_dict_dup(root, tmp); + inner = parser->dict_add_dict_dup(root, tmp); { JSON_SET_SIMPLE("media","%u",ps->media->unique_id); @@ -2461,7 +2487,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { JSON_SET_SIMPLE("stats-bytes","%" PRIu64, atomic64_get_na(&ps->stats_in->bytes)); JSON_SET_SIMPLE("stats-errors","%" PRIu64, atomic64_get_na(&ps->stats_in->errors)); - json_update_crypto_params(inner, "", &ps->crypto.params); + json_update_crypto_params(parser, inner, "", &ps->crypto.params); } // stream_sfds was here before @@ -2477,14 +2503,14 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { LOCK(&ps->out_lock); snprintf(tmp, sizeof(tmp), "stream_sfds-%u", ps->unique_id); - inner = redis_parser->dict_add_list_dup(root, tmp); + inner = parser->dict_add_list_dup(root, tmp); for (__auto_type k = ps->sfds.head; k; k = k->next) { stream_fd *sfd = k->data; JSON_ADD_LIST_STRING("%u", sfd->unique_id); } snprintf(tmp, sizeof(tmp), "rtp_sinks-%u", ps->unique_id); - inner = redis_parser->dict_add_list_dup(root, tmp); + inner = parser->dict_add_list_dup(root, tmp); for (__auto_type k = ps->rtp_sinks.head; k; k = k->next) { struct sink_handler *sh = k->data; struct packet_stream *sink = sh->sink; @@ -2492,7 +2518,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { } snprintf(tmp, sizeof(tmp), "rtcp_sinks-%u", ps->unique_id); - inner = redis_parser->dict_add_list_dup(root, tmp); + inner = parser->dict_add_list_dup(root, tmp); for (__auto_type k = ps->rtcp_sinks.head; k; k = k->next) { struct sink_handler *sh = k->data; struct packet_stream *sink = sh->sink; @@ -2505,7 +2531,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { struct call_monologue *ml = l->data; snprintf(tmp, sizeof(tmp), "tag-%u", ml->unique_id); - inner = redis_parser->dict_add_dict_dup(root, tmp); + inner = parser->dict_add_dict_dup(root, tmp); { @@ -2566,7 +2592,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { // XXX these should all go into the above loop GList *k = g_hash_table_get_values(ml->associated_tags); snprintf(tmp, sizeof(tmp), "associated_tags-%u", ml->unique_id); - inner = redis_parser->dict_add_list_dup(root, tmp); + inner = parser->dict_add_list_dup(root, tmp); for (GList *m = k; m; m = m->next) { struct call_monologue *ml2 = m->data; JSON_ADD_LIST_STRING("%u", ml2->unique_id); @@ -2575,7 +2601,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { g_list_free(k); snprintf(tmp, sizeof(tmp), "medias-%u", ml->unique_id); - inner = redis_parser->dict_add_list_dup(root, tmp); + inner = parser->dict_add_list_dup(root, tmp); for (unsigned int j = 0; j < ml->medias->len; j++) { struct call_media *media = ml->medias->pdata[j]; JSON_ADD_LIST_STRING("%u", media ? media->unique_id : -1); @@ -2585,10 +2611,10 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { rwlock_lock_r(&ml->ssrc_hash->lock); k = g_hash_table_get_values(ml->ssrc_hash->ht); snprintf(tmp, sizeof(tmp), "ssrc_table-%u", ml->unique_id); - parser_arg list = redis_parser->dict_add_list_dup(root, tmp); + parser_arg list = parser->dict_add_list_dup(root, tmp); for (GList *m = k; m; m = m->next) { struct ssrc_entry_call *se = m->data; - inner = redis_parser->list_add_dict(list); + inner = parser->list_add_dict(list); JSON_SET_SIMPLE("ssrc", "%" PRIu32, se->h.ssrc); // XXX use function for in/out @@ -2613,7 +2639,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { /* store media subscriptions */ snprintf(tmp, sizeof(tmp), "media-subscriptions-%u", media->unique_id); - inner = redis_parser->dict_add_list_dup(root, tmp); + inner = parser->dict_add_list_dup(root, tmp); for (__auto_type sub = media->media_subscriptions.head; sub; sub = sub->next) { @@ -2626,7 +2652,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { } snprintf(tmp, sizeof(tmp), "media-%u", media->unique_id); - inner = redis_parser->dict_add_dict_dup(root, tmp); + inner = parser->dict_add_dict_dup(root, tmp); { JSON_SET_SIMPLE("tag","%u", media->monologue->unique_id); @@ -2650,11 +2676,11 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { if (media->bandwidth_rs >= 0) JSON_SET_SIMPLE("bandwidth_rs","%i", media->bandwidth_rs); - json_update_sdes_params(inner, "media", media->unique_id, "sdes_in", + json_update_sdes_params(parser, inner, "media", media->unique_id, "sdes_in", &media->sdes_in); - json_update_sdes_params(inner, "media", media->unique_id, "sdes_out", + json_update_sdes_params(parser, inner, "media", media->unique_id, "sdes_out", &media->sdes_out); - json_update_dtls_fingerprint(inner, "media", media->unique_id, &media->fingerprint); + json_update_dtls_fingerprint(parser, inner, "media", media->unique_id, &media->fingerprint); } } // --- for medias.head @@ -2664,21 +2690,21 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { struct call_media *media = l->data; snprintf(tmp, sizeof(tmp), "streams-%u", media->unique_id); - inner = redis_parser->dict_add_list_dup(root, tmp); + inner = parser->dict_add_list_dup(root, tmp); for (__auto_type m = media->streams.head; m; m = m->next) { struct packet_stream *ps = m->data; JSON_ADD_LIST_STRING("%u", ps->unique_id); } snprintf(tmp, sizeof(tmp), "maps-%u", media->unique_id); - inner = redis_parser->dict_add_list_dup(root, tmp); + inner = parser->dict_add_list_dup(root, tmp); for (__auto_type m = media->endpoint_maps.head; m; m = m->next) { struct endpoint_map *ep = m->data; JSON_ADD_LIST_STRING("%u", ep->unique_id); } snprintf(tmp, sizeof(tmp), "payload_types-%u", media->unique_id); - inner = redis_parser->dict_add_list_dup(root, tmp); + inner = parser->dict_add_list_dup(root, tmp); for (__auto_type m = media->codecs.codec_prefs.head; m; m = m->next) { rtp_payload_type *pt = m->data; JSON_ADD_LIST_STRING("%u/" STR_FORMAT "/%u/" STR_FORMAT "/" STR_FORMAT "/%i/%i", @@ -2692,7 +2718,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { struct endpoint_map *ep = l->data; snprintf(tmp, sizeof(tmp), "map-%u", ep->unique_id); - inner = redis_parser->dict_add_dict_dup(root, tmp); + inner = parser->dict_add_dict_dup(root, tmp); { JSON_SET_SIMPLE("wildcard","%i", ep->wildcard); @@ -2710,7 +2736,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { struct endpoint_map *ep = l->data; snprintf(tmp, sizeof(tmp), "map_sfds-%u", ep->unique_id); - inner = redis_parser->dict_add_list_dup(root, tmp); + inner = parser->dict_add_list_dup(root, tmp); for (__auto_type m = ep->intf_sfds.head; m; m = m->next) { struct sfd_intf_list *il = m->data; JSON_ADD_LIST_STRING("loc-%u", il->local_intf->unique_id); @@ -2724,7 +2750,7 @@ static str redis_encode_json(ng_parser_ctx_t *ctx, call_t *c) { } str ret; - redis_parser->collapse(ctx, root, &ret); + parser->collapse(ctx, root, &ret); return ret; } @@ -2754,8 +2780,11 @@ void redis_update_onekey(call_t *c, struct redis *r) { goto err; } - ng_parser_ctx_t ctx = {.parser = redis_parser}; - ctx.ngbuf = ng_buffer_new(NULL); + ng_parser_ctx_t ctx = {.parser = redis_format_parsers[rtpe_config.redis_format]}; + ctx.ngbuf = ng_buffer_new(NULL); // XXX make conditional + int ret = bencode_buffer_init(&ctx.ngbuf->buffer); // XXX make conditional and/or optimise + if (ret) + goto err; str result = redis_encode_json(&ctx, c); if (!result.len) diff --git a/docs/rtpengine.md b/docs/rtpengine.md index d282edd92..2a0d86a69 100644 --- a/docs/rtpengine.md +++ b/docs/rtpengine.md @@ -599,6 +599,16 @@ call to inject-DTMF won't be sent to __\-\-dtmf-log-dest=__ or __\-\-listen-tcp- The default value for the connection timeout is 1000ms. This parameter can also be set or listed via __rtpengine-ctl__. +- __\-\-redis-format=bencode__\|__JSON__ + + Selects the format for serialised call data written to Redis or KeyDB. The + old default (and previously only option) was as a JSON object. The new + default is using *bencode* formatting. Using *bencode* has the benefit of + yielding better performance and lower CPU usage, while making the data less + human readable. + + Both formats can be restored from, regardless of this setting. + - __-b__, __\-\-b2b-url=__*STRING* Enables and sets the URI for an XMLRPC callback to be made when a call is diff --git a/include/main.h b/include/main.h index 2f4a743d3..22139598b 100644 --- a/include/main.h +++ b/include/main.h @@ -90,6 +90,12 @@ struct rtpengine_config { int redis_delete_async_interval; char *redis_auth; char *redis_write_auth; + enum { + REDIS_FORMAT_BENCODE = 0, + REDIS_FORMAT_JSON, + + __REDIS_FORMAT_MAX + } redis_format; gboolean active_switchover; int num_threads; int media_num_threads; diff --git a/t/Makefile b/t/Makefile index fdcbb3762..124a110c0 100644 --- a/t/Makefile +++ b/t/Makefile @@ -100,7 +100,7 @@ include ../lib/common.Makefile .PHONY: all-tests unit-tests daemon-tests daemon-tests \ daemon-tests-main daemon-tests-jb daemon-tests-dtx daemon-tests-dtx-cn daemon-tests-pubsub \ daemon-tests-intfs daemon-tests-stats daemon-tests-delay-buffer daemon-tests-delay-timing \ - daemon-tests-evs daemon-tests-player-cache daemon-tests-redis + daemon-tests-evs daemon-tests-player-cache daemon-tests-redis daemon-tests-redis-json TESTS= test-bitstr aes-crypt aead-aes-crypt test-const_str_hash.strhash ifeq ($(with_transcoding),yes) @@ -138,7 +138,7 @@ daemon-tests: daemon-tests-main daemon-tests-jb daemon-tests-pubsub daemon-tests daemon-tests-evs daemon-tests-async-tc \ daemon-tests-audio-player daemon-tests-audio-player-play-media \ daemon-tests-intfs daemon-tests-stats daemon-tests-player-cache daemon-tests-redis \ - daemon-tests-rtpp-flags + daemon-tests-rtpp-flags daemon-tests-redis-json daemon-test-deps: tests-preload.so $(MAKE) -C ../daemon @@ -182,6 +182,9 @@ daemon-tests-player-cache: daemon-test-deps daemon-tests-redis: daemon-test-deps ./auto-test-helper "$@" perl -I../perl auto-daemon-tests-redis.pl +daemon-tests-redis-json: daemon-test-deps + ./auto-test-helper "$@" perl -I../perl auto-daemon-tests-redis-json.pl + daemon-tests-audio-player: daemon-test-deps ./auto-test-helper "$@" perl -I../perl auto-daemon-tests-audio-player.pl diff --git a/t/auto-daemon-tests-redis-json.pl b/t/auto-daemon-tests-redis-json.pl new file mode 100755 index 000000000..481982436 --- /dev/null +++ b/t/auto-daemon-tests-redis-json.pl @@ -0,0 +1,2631 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use NGCP::Rtpengine::Test; +use NGCP::Rtpclient::SRTP; +use NGCP::Rtpengine::AutoTest; +use Test::More; +use Test2::Tools::Compare qw(like); +use Socket qw(AF_INET SOCK_STREAM sockaddr_in pack_sockaddr_in inet_aton); +use JSON; +use Data::Dumper; + +$Data::Dumper::Sortkeys = 1; + + +# fake Redis listener +my $redis_listener; +socket($redis_listener, AF_INET, SOCK_STREAM, 0) or die; +bind($redis_listener, sockaddr_in(6379, inet_aton('203.0.113.42'))) or die; +listen($redis_listener, 10) or die; + +my $redis_fd; + + +sub redis_i { + my ($i, $n) = @_; + my $buf; + alarm(1); + recv($redis_fd, $buf, length($i), 0) or die; + alarm(0); + is($buf, $i, $n); +} +sub redis_io { + my ($i, $o, $n) = @_; + redis_i($i, $n); + send($redis_fd, $o, 0) or die; +}; + + +$NGCP::Rtpengine::AutoTest::launch_cb = sub { + # accept Redis connection and read preamble + + accept($redis_fd, $redis_listener) or die; + + redis_io("*2\r\n\$4\r\nAUTH\r\n\$4\r\nauth\r\n", "+OK\r\n", "AUTH"); + redis_io("*2\r\n\$6\r\nSELECT\r\n\$1\r\n2\r\n", "+OK\r\n", "SELECT 1"); + redis_io("*1\r\n\$4\r\nINFO\r\n", "\$13\r\nrole:master\r\n\r\n", "INFO"); + redis_io("*2\r\n\$4\r\nTYPE\r\n\$5\r\ncalls\r\n", "+none\r\n", "TYPE"); + + redis_io("*1\r\n\$4\r\nPING\r\n", "+PONG\r\n", "PING"); + redis_io("*2\r\n\$4\r\nKEYS\r\n\$1\r\n*\r\n", "*0\r\n", "KEYS"); +}; + + +autotest_start(qw(--config-file=none -t -1 -i foo/203.0.113.1 -i foo/2001:db8:4321::1 + --redis-format=json + -i bar/203.0.113.2 -i bar/2001:db8:4321::2 + -n 2223 -f -L 7 -E --redis=auth@203.0.113.42:6379/2)) + or die; + + + +my $json_exp; +$NGCP::Rtpengine::req_cb = sub { + redis_io("*1\r\n\$4\r\nPING\r\n", "+PONG\r\n", "req PING"); + redis_i("*3\r\n\$3\r\nSET\r\n\$" . length(cid()) . "\r\n" . cid() . "\r\n\$", "req intro"); + # dumbly expect 4-digit number + my $buf; + alarm(1); + recv($redis_fd, $buf, 6, 0) or die; + alarm(0); + is(substr($buf, 4, 2), "\r\n", "4-digit number"); + my $len = int($buf); + alarm(1); + recv($redis_fd, $buf, $len, 0) or die; + alarm(0); + my $json = decode_json($buf); + #print Dumper($json); + like($json, $json_exp, "JSON"); + redis_io("\r\n*3\r\n\$6\r\nEXPIRE\r\n\$" . length(cid()) . "\r\n" . cid() . "\r\n\$5\r\n86400\r\n", + "+OK\r\n+OK\r\n", + "req EXPIRE"); +}; + + + +new_call; + +$json_exp = { + 'associated_tags-0' => [ + '1' + ], + 'associated_tags-1' => [ + '0' + ], + 'json' => { + 'block_dtmf' => '0', + 'call_flags' => 65536, + 'created' => qr/^\d+$/, + 'created_from' => qr//, + 'created_from_addr' => qr//, + 'deleted' => '0', + 'destroyed' => '0', + 'last_signal' => qr/^\d+$/, + 'ml_deleted' => '0', + 'num_maps' => '2', + 'num_medias' => '2', + 'num_sfds' => '4', + 'num_streams' => '4', + 'num_tags' => '2', + 'recording_metadata' => '', + 'redis_hosted_db' => '2', + 'tos' => '0' + }, + 'map-0' => { + 'endpoint' => '198.51.100.1:3000', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '0' + }, + 'map-1' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map_sfds-0' => [ + 'loc-0', + '0', + '1' + ], + 'map_sfds-1' => [ + 'loc-0', + '2', + '3' + ], + 'maps-0' => [ + '0' + ], + 'maps-1' => [ + '1' + ], + 'media-0' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '2228236', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '1', + 'type' => 'audio' + }, + 'media-1' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '65548', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '0', + 'type' => 'audio' + }, + 'medias-0' => [ + '1' + ], + 'medias-1' => [ + '0' + ], + 'payload_types-0' => [ + '0/PCMU/8000///0/20', + '8/PCMA/8000///0/20' + ], + 'payload_types-1' => [ + '0/PCMU/8000///0/20', + '8/PCMA/8000///0/20' + ], + 'rtcp_sinks-0' => [], + 'rtcp_sinks-1' => [ + '3' + ], + 'rtcp_sinks-2' => [], + 'rtcp_sinks-3' => [ + '1' + ], + 'rtp_sinks-0' => [ + '2' + ], + 'rtp_sinks-1' => [], + 'rtp_sinks-2' => [ + '0' + ], + 'rtp_sinks-3' => [], + 'sfd-0' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '0' + }, + 'sfd-1' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '1' + }, + 'sfd-2' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '2' + }, + 'sfd-3' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '3' + }, + 'ssrc_table-0' => [], + 'ssrc_table-1' => [], + 'stream-0' => { + 'advertised_endpoint' => '', + 'component' => '1', + 'endpoint' => '', + 'last_packet' => qr/^\d+$/, + 'media' => '0', + 'ps_flags' => '65536', + 'rtcp_sibling' => '1', + 'sfd' => '0', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-1' => { + 'advertised_endpoint' => '', + 'component' => '2', + 'endpoint' => '', + 'last_packet' => qr/^\d+$/, + 'media' => '0', + 'ps_flags' => '131072', + 'rtcp_sibling' => '4294967295', + 'sfd' => '1', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-2' => { + 'advertised_endpoint' => '198.51.100.1:3000', + 'component' => '1', + 'endpoint' => '198.51.100.1:3000', + 'last_packet' => qr/^\d+$/, + 'media' => '1', + 'ps_flags' => '68222976', + 'rtcp_sibling' => '3', + 'sfd' => '2', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-3' => { + 'advertised_endpoint' => '198.51.100.1:3001', + 'component' => '2', + 'endpoint' => '198.51.100.1:3001', + 'last_packet' => qr/^\d+$/, + 'media' => '1', + 'ps_flags' => '68288513', + 'rtcp_sibling' => '4294967295', + 'sfd' => '3', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream_sfds-0' => [ + '0' + ], + 'stream_sfds-1' => [ + '1' + ], + 'stream_sfds-2' => [ + '2' + ], + 'stream_sfds-3' => [ + '3' + ], + 'streams-0' => [ + '0', + '1' + ], + 'streams-1' => [ + '2', + '3' + ], + 'media-subscriptions-0' => [ + '1/1/0/0' + ], + 'media-subscriptions-1' => [ + '0/1/0/0' + ], + 'tag-0' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'desired_family' => 'IP4', + 'deleted' => '0', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => ft() + }, + 'tag-1' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'desired_family' => 'IP4', + 'deleted' => '0', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + } +}; + +offer('simple call', + { }, < [ + '1' + ], + 'associated_tags-1' => [ + '0' + ], + 'json' => { + 'block_dtmf' => '0', + 'call_flags' => 1376256, + 'created' => qr/^\d+$/, + 'created_from' => qr//, + 'created_from_addr' => qr//, + 'deleted' => '0', + 'destroyed' => '0', + 'last_signal' => qr/^\d+$/, + 'ml_deleted' => '0', + 'num_maps' => '2', + 'num_medias' => '2', + 'num_sfds' => '4', + 'num_streams' => '4', + 'num_tags' => '2', + 'recording_metadata' => '', + 'redis_hosted_db' => '2', + 'tos' => '0' + }, + 'map-0' => { + 'endpoint' => '198.51.100.1:3000', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '0' + }, + 'map-1' => { + 'endpoint' => '198.51.100.4:3000', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '0' + }, + 'map_sfds-0' => [ + 'loc-0', + '0', + '1' + ], + 'map_sfds-1' => [ + 'loc-0', + '2', + '3' + ], + 'maps-0' => [ + '0' + ], + 'maps-1' => [ + '1' + ], + 'media-0' => { + 'desired_family' => 'IP4', + 'format_str' => '8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '2293772', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '1', + 'type' => 'audio' + }, + 'media-1' => { + 'desired_family' => 'IP4', + 'format_str' => '8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '65548', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '0', + 'type' => 'audio' + }, + 'medias-0' => [ + '1' + ], + 'medias-1' => [ + '0' + ], + 'payload_types-0' => [ + '8/PCMA/8000///0/20' + ], + 'payload_types-1' => [ + '8/PCMA/8000///0/20' + ], + 'rtcp_sinks-0' => [], + 'rtcp_sinks-1' => [ + '3' + ], + 'rtcp_sinks-2' => [], + 'rtcp_sinks-3' => [ + '1' + ], + 'rtp_sinks-0' => [ + '2' + ], + 'rtp_sinks-1' => [], + 'rtp_sinks-2' => [ + '0' + ], + 'rtp_sinks-3' => [], + 'sfd-0' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '0' + }, + 'sfd-1' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '1' + }, + 'sfd-2' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '2' + }, + 'sfd-3' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '3' + }, + 'ssrc_table-0' => [], + 'ssrc_table-1' => [], + 'stream-0' => { + 'advertised_endpoint' => '198.51.100.4:3000', + 'component' => '1', + 'endpoint' => '198.51.100.4:3000', + 'last_packet' => qr/^\d+$/, + 'media' => '0', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '1', + 'sfd' => '0', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-1' => { + 'advertised_endpoint' => '198.51.100.4:3001', + 'component' => '2', + 'endpoint' => '198.51.100.4:3001', + 'last_packet' => qr/^\d+$/, + 'media' => '0', + 'ps_flags' => '1179649', + 'rtcp_sibling' => '4294967295', + 'sfd' => '1', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-2' => { + 'advertised_endpoint' => '198.51.100.1:3000', + 'component' => '1', + 'endpoint' => '198.51.100.1:3000', + 'last_packet' => qr/^\d+$/, + 'media' => '1', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '3', + 'sfd' => '2', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-3' => { + 'advertised_endpoint' => '198.51.100.1:3001', + 'component' => '2', + 'endpoint' => '198.51.100.1:3001', + 'last_packet' => qr/^\d+$/, + 'media' => '1', + 'ps_flags' => '1179649', + 'rtcp_sibling' => '4294967295', + 'sfd' => '3', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream_sfds-0' => [ + '0' + ], + 'stream_sfds-1' => [ + '1' + ], + 'stream_sfds-2' => [ + '2' + ], + 'stream_sfds-3' => [ + '3' + ], + 'streams-0' => [ + '0', + '1' + ], + 'streams-1' => [ + '2', + '3' + ], + 'media-subscriptions-0' => [ + '1/1/0/0' + ], + 'media-subscriptions-1' => [ + '0/1/0/0' + ], + 'tag-0' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => ft() + }, + 'tag-1' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => tt() + } +}; + +answer('simple call', + { }, < [ + '1' + ], + 'associated_tags-1' => [ + '0' + ], + 'json' => { + 'block_dtmf' => '0', + 'call_flags' => 65536, + 'created' => qr/^\d+$/, + 'created_from' => qr//, + 'created_from_addr' => qr//, + 'deleted' => '0', + 'destroyed' => '0', + 'last_signal' => qr/^\d+$/, + 'ml_deleted' => '0', + 'num_maps' => '2', + 'num_medias' => '2', + 'num_sfds' => '4', + 'num_streams' => '4', + 'num_tags' => '2', + 'recording_metadata' => '', + 'redis_hosted_db' => '2', + 'tos' => '0' + }, + 'map-0' => { + 'endpoint' => '198.51.100.14:6088', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '0' + }, + 'map-1' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map_sfds-0' => [ + 'loc-0', + '0', + '1' + ], + 'map_sfds-1' => [ + 'loc-0', + '2', + '3' + ], + 'maps-0' => [ + '0' + ], + 'maps-1' => [ + '1' + ], + 'media-0' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '2228236', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '1', + 'type' => 'audio' + }, + 'media-1' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '65548', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '0', + 'type' => 'audio' + }, + 'medias-0' => [ + '1' + ], + 'medias-1' => [ + '0' + ], + 'payload_types-0' => [ + '0/PCMU/8000///0/20', + '8/PCMA/8000///0/20' + ], + 'payload_types-1' => [ + '0/PCMU/8000///0/20', + '8/PCMA/8000///0/20' + ], + 'rtcp_sinks-0' => [], + 'rtcp_sinks-1' => [ + '3' + ], + 'rtcp_sinks-2' => [], + 'rtcp_sinks-3' => [ + '1' + ], + 'rtp_sinks-0' => [ + '2' + ], + 'rtp_sinks-1' => [], + 'rtp_sinks-2' => [ + '0' + ], + 'rtp_sinks-3' => [], + 'sfd-0' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '0' + }, + 'sfd-1' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '1' + }, + 'sfd-2' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '2' + }, + 'sfd-3' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '3' + }, + 'ssrc_table-0' => [], + 'ssrc_table-1' => [], + 'stream-0' => { + 'advertised_endpoint' => '', + 'component' => '1', + 'endpoint' => '', + 'last_packet' => qr/^\d+$/, + 'media' => '0', + 'ps_flags' => '65536', + 'rtcp_sibling' => '1', + 'sfd' => '0', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-1' => { + 'advertised_endpoint' => '', + 'component' => '2', + 'endpoint' => '', + 'last_packet' => qr/^\d+$/, + 'media' => '0', + 'ps_flags' => '131072', + 'rtcp_sibling' => '4294967295', + 'sfd' => '1', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-2' => { + 'advertised_endpoint' => '198.51.100.14:6088', + 'component' => '1', + 'endpoint' => '198.51.100.14:6088', + 'last_packet' => qr/^\d+$/, + 'media' => '1', + 'ps_flags' => '68222976', + 'rtcp_sibling' => '3', + 'sfd' => '2', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-3' => { + 'advertised_endpoint' => '198.51.100.14:6089', + 'component' => '2', + 'endpoint' => '198.51.100.14:6089', + 'last_packet' => qr/^\d+$/, + 'media' => '1', + 'ps_flags' => '68288513', + 'rtcp_sibling' => '4294967295', + 'sfd' => '3', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream_sfds-0' => [ + '0' + ], + 'stream_sfds-1' => [ + '1' + ], + 'stream_sfds-2' => [ + '2' + ], + 'stream_sfds-3' => [ + '3' + ], + 'streams-0' => [ + '0', + '1' + ], + 'streams-1' => [ + '2', + '3' + ], + 'media-subscriptions-0' => [ + '1/1/0/0' + ], + 'media-subscriptions-1' => [ + '0/1/0/0' + ], + 'tag-0' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => ft() + }, + 'tag-1' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + } +}; + +offer('sub to multiple tags', + { }, < [ + '1' + ], + 'associated_tags-1' => [ + '0' + ], + 'json' => { + 'block_dtmf' => '0', + 'call_flags' => 1376256, + 'created' => qr/^\d+$/, + 'created_from' => qr//, + 'created_from_addr' => qr//, + 'deleted' => '0', + 'destroyed' => '0', + 'last_signal' => qr/^\d+$/, + 'ml_deleted' => '0', + 'num_maps' => '2', + 'num_medias' => '2', + 'num_sfds' => '4', + 'num_streams' => '4', + 'num_tags' => '2', + 'recording_metadata' => '', + 'redis_hosted_db' => '2', + 'tos' => '0' + }, + 'map-0' => { + 'endpoint' => '198.51.100.14:6088', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '0' + }, + 'map-1' => { + 'endpoint' => '198.51.100.14:6090', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '0' + }, + 'map_sfds-0' => [ + 'loc-0', + '0', + '1' + ], + 'map_sfds-1' => [ + 'loc-0', + '2', + '3' + ], + 'maps-0' => [ + '0' + ], + 'maps-1' => [ + '1' + ], + 'media-0' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '2293772', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '1', + 'type' => 'audio' + }, + 'media-1' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '65548', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '0', + 'type' => 'audio' + }, + 'medias-0' => [ + '1' + ], + 'medias-1' => [ + '0' + ], + 'payload_types-0' => [ + '0/PCMU/8000///0/20', + '8/PCMA/8000///0/20' + ], + 'payload_types-1' => [ + '0/PCMU/8000///0/20', + '8/PCMA/8000///0/20' + ], + 'rtcp_sinks-0' => [], + 'rtcp_sinks-1' => [ + '3' + ], + 'rtcp_sinks-2' => [], + 'rtcp_sinks-3' => [ + '1' + ], + 'rtp_sinks-0' => [ + '2' + ], + 'rtp_sinks-1' => [], + 'rtp_sinks-2' => [ + '0' + ], + 'rtp_sinks-3' => [], + 'sfd-0' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '0' + }, + 'sfd-1' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '1' + }, + 'sfd-2' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '2' + }, + 'sfd-3' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '3' + }, + 'ssrc_table-0' => [], + 'ssrc_table-1' => [], + 'stream-0' => { + 'advertised_endpoint' => '198.51.100.14:6090', + 'component' => '1', + 'endpoint' => '198.51.100.14:6090', + 'last_packet' => qr/^\d+$/, + 'media' => '0', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '1', + 'sfd' => '0', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-1' => { + 'advertised_endpoint' => '198.51.100.14:6091', + 'component' => '2', + 'endpoint' => '198.51.100.14:6091', + 'last_packet' => qr/^\d+$/, + 'media' => '0', + 'ps_flags' => '1179649', + 'rtcp_sibling' => '4294967295', + 'sfd' => '1', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-2' => { + 'advertised_endpoint' => '198.51.100.14:6088', + 'component' => '1', + 'endpoint' => '198.51.100.14:6088', + 'last_packet' => qr/^\d+$/, + 'media' => '1', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '3', + 'sfd' => '2', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-3' => { + 'advertised_endpoint' => '198.51.100.14:6089', + 'component' => '2', + 'endpoint' => '198.51.100.14:6089', + 'last_packet' => qr/^\d+$/, + 'media' => '1', + 'ps_flags' => '1179649', + 'rtcp_sibling' => '4294967295', + 'sfd' => '3', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream_sfds-0' => [ + '0' + ], + 'stream_sfds-1' => [ + '1' + ], + 'stream_sfds-2' => [ + '2' + ], + 'stream_sfds-3' => [ + '3' + ], + 'streams-0' => [ + '0', + '1' + ], + 'streams-1' => [ + '2', + '3' + ], + 'media-subscriptions-0' => [ + '1/1/0/0' + ], + 'media-subscriptions-1' => [ + '0/1/0/0' + ], + 'tag-0' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => ft() + }, + 'tag-1' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => tt() + } +}; + +answer('sub to multiple tags', + { }, < [ + '1' + ], + 'associated_tags-1' => [ + '0' + ], + 'associated_tags-2' => [], + 'json' => { + 'block_dtmf' => '0', + 'call_flags' => 1376256, + 'created' => qr/^\d+$/, + 'created_from' => qr//, + 'created_from_addr' => qr//, + 'deleted' => '0', + 'destroyed' => '0', + 'last_signal' => qr/^\d+$/, + 'ml_deleted' => '0', + 'num_maps' => '4', + 'num_medias' => '4', + 'num_sfds' => '8', + 'num_streams' => '8', + 'num_tags' => '3', + 'recording_metadata' => '', + 'redis_hosted_db' => '2', + 'tos' => '0' + }, + 'map-0' => { + 'endpoint' => '198.51.100.14:6088', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '0' + }, + 'map-1' => { + 'endpoint' => '198.51.100.14:6090', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '0' + }, + 'map-2' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map-3' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map_sfds-0' => [ + 'loc-0', + '0', + '1' + ], + 'map_sfds-1' => [ + 'loc-0', + '2', + '3' + ], + 'map_sfds-2' => [ + 'loc-0', + '4', + '5' + ], + 'map_sfds-3' => [ + 'loc-0', + '6', + '7' + ], + 'maps-0' => [ + '0' + ], + 'maps-1' => [ + '1' + ], + 'maps-2' => [ + '2' + ], + 'maps-3' => [ + '3' + ], + 'media-0' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '2293772', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '1', + 'type' => 'audio' + }, + 'media-1' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '65548', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '0', + 'type' => 'audio' + }, + 'media-2' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '2097156', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '2', + 'type' => 'audio' + }, + 'media-3' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8', + 'index' => '2', + 'logical_intf' => 'foo', + 'media_flags' => '2097156', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '2', + 'type' => 'audio' + }, + 'medias-0' => [ + '1' + ], + 'medias-1' => [ + '0' + ], + 'medias-2' => [ + '2', + '3' + ], + 'payload_types-0' => [ + '0/PCMU/8000///0/20', + '8/PCMA/8000///0/20' + ], + 'payload_types-1' => [ + '0/PCMU/8000///0/20', + '8/PCMA/8000///0/20' + ], + 'payload_types-2' => [ + '0/PCMU/8000///0/20', + '8/PCMA/8000///0/20' + ], + 'payload_types-3' => [ + '0/PCMU/8000///0/20', + '8/PCMA/8000///0/20' + ], + 'rtcp_sinks-0' => [], + 'rtcp_sinks-1' => [ + '3', + '7' + ], + 'rtcp_sinks-2' => [], + 'rtcp_sinks-3' => [ + '1', + '5' + ], + 'rtcp_sinks-4' => [], + 'rtcp_sinks-5' => [], + 'rtcp_sinks-6' => [], + 'rtcp_sinks-7' => [], + 'rtp_sinks-0' => [ + '2', + '6' + ], + 'rtp_sinks-1' => [], + 'rtp_sinks-2' => [ + '0', + '4' + ], + 'rtp_sinks-3' => [], + 'rtp_sinks-4' => [], + 'rtp_sinks-5' => [], + 'rtp_sinks-6' => [], + 'rtp_sinks-7' => [], + 'sfd-0' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '0' + }, + 'sfd-1' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '1' + }, + 'sfd-2' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '2' + }, + 'sfd-3' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '3' + }, + 'sfd-4' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '4' + }, + 'sfd-5' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '5' + }, + 'sfd-6' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '6' + }, + 'sfd-7' => { + 'fd' => qr/^\d+$/, + 'local_intf_uid' => '0', + 'localport' => qr/^\d+$/, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '7' + }, + 'ssrc_table-0' => [], + 'ssrc_table-1' => [], + 'ssrc_table-2' => [], + 'stream-0' => { + 'advertised_endpoint' => '198.51.100.14:6090', + 'component' => '1', + 'endpoint' => '198.51.100.14:6090', + 'last_packet' => qr/^\d+$/, + 'media' => '0', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '1', + 'sfd' => '0', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-1' => { + 'advertised_endpoint' => '198.51.100.14:6091', + 'component' => '2', + 'endpoint' => '198.51.100.14:6091', + 'last_packet' => qr/^\d+$/, + 'media' => '0', + 'ps_flags' => '68288513', + 'rtcp_sibling' => '4294967295', + 'sfd' => '1', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-2' => { + 'advertised_endpoint' => '198.51.100.14:6088', + 'component' => '1', + 'endpoint' => '198.51.100.14:6088', + 'last_packet' => qr/^\d+$/, + 'media' => '1', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '3', + 'sfd' => '2', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-3' => { + 'advertised_endpoint' => '198.51.100.14:6089', + 'component' => '2', + 'endpoint' => '198.51.100.14:6089', + 'last_packet' => qr/^\d+$/, + 'media' => '1', + 'ps_flags' => '68288513', + 'rtcp_sibling' => '4294967295', + 'sfd' => '3', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-4' => { + 'advertised_endpoint' => '', + 'component' => '1', + 'endpoint' => '', + 'last_packet' => qr/^\d+$/, + 'media' => '2', + 'ps_flags' => '65536', + 'rtcp_sibling' => '5', + 'sfd' => '4', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-5' => { + 'advertised_endpoint' => '', + 'component' => '2', + 'endpoint' => '', + 'last_packet' => qr/^\d+$/, + 'media' => '2', + 'ps_flags' => '131072', + 'rtcp_sibling' => '4294967295', + 'sfd' => '5', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-6' => { + 'advertised_endpoint' => '', + 'component' => '1', + 'endpoint' => '', + 'last_packet' => qr/^\d+$/, + 'media' => '3', + 'ps_flags' => '65536', + 'rtcp_sibling' => '7', + 'sfd' => '6', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-7' => { + 'advertised_endpoint' => '', + 'component' => '2', + 'endpoint' => '', + 'last_packet' => qr/^\d+$/, + 'media' => '3', + 'ps_flags' => '131072', + 'rtcp_sibling' => '4294967295', + 'sfd' => '7', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream_sfds-0' => [ + '0' + ], + 'stream_sfds-1' => [ + '1' + ], + 'stream_sfds-2' => [ + '2' + ], + 'stream_sfds-3' => [ + '3' + ], + 'stream_sfds-4' => [ + '4' + ], + 'stream_sfds-5' => [ + '5' + ], + 'stream_sfds-6' => [ + '6' + ], + 'stream_sfds-7' => [ + '7' + ], + 'streams-0' => [ + '0', + '1' + ], + 'streams-1' => [ + '2', + '3' + ], + 'streams-2' => [ + '4', + '5' + ], + 'streams-3' => [ + '6', + '7' + ], + 'media-subscriptions-0' => [ + '1/1/0/0' + ], + 'media-subscriptions-1' => [ + '0/1/0/0' + ], + 'media-subscriptions-2' => [ + '1/0/0/0' + ], + 'tag-0' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => ft() + }, + 'tag-1' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => tt() + }, + 'tag-2' => { + 'block_dtmf' => '0', + 'created' => qr/^\d+$/, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => qr// + } + }; + +my ($ftr, $ttr, $fts) = subscribe_request('sub to multiple tags', + { 'from-tags' => [ft(), tt()] }, <{'media-0'}{media_flags} = '2293772'; +$json_exp->{'media-1'}{media_flags} = '65548'; +$json_exp->{'media-2'}{format_str} = '8'; +$json_exp->{'media-2'}{media_flags} = '2162692'; +$json_exp->{'media-3'}{format_str} = '8'; +$json_exp->{'media-3'}{media_flags} = '2162692'; +$json_exp->{'payload_types-2'}[0] = '8/PCMA/8000///0/20'; +$#{$json_exp->{'payload_types-2'}} = 0; +$json_exp->{'payload_types-3'}[0] = '8/PCMA/8000///0/20'; +$#{$json_exp->{'payload_types-3'}} = 0; +$json_exp->{'stream-1'}{ps_flags} = '1179649'; +$json_exp->{'stream-3'}{ps_flags} = '1179649'; +$json_exp->{'stream-4'}{advertised_endpoint} = '198.51.100.14:6092'; +$json_exp->{'stream-4'}{endpoint} = '198.51.100.14:6092'; +$json_exp->{'stream-4'}{ps_flags} = '1114112'; +$json_exp->{'stream-5'}{advertised_endpoint} = '198.51.100.14:6093'; +$json_exp->{'stream-5'}{endpoint} = '198.51.100.14:6093'; +$json_exp->{'stream-5'}{ps_flags} = '1179649'; +$json_exp->{'stream-6'}{advertised_endpoint} = '198.51.100.14:6094'; +$json_exp->{'stream-6'}{endpoint} = '198.51.100.14:6094'; +$json_exp->{'stream-6'}{ps_flags} = '1114112'; +$json_exp->{'stream-7'}{advertised_endpoint} = '198.51.100.14:6095'; +$json_exp->{'stream-7'}{endpoint} = '198.51.100.14:6095'; +$json_exp->{'stream-7'}{ps_flags} = '1179649'; + +subscribe_answer('sub to multiple tags', + { 'to-tag' => $ttr, flags => ['allow transcoding'] }, < [], + 'json' => { + 'block_dtmf' => '0', + 'call_flags' => 0, + 'created' => qr//, + 'created_from' => qr//, + 'created_from_addr' => qr//, + 'deleted' => '0', + 'destroyed' => '0', + 'last_signal' => qr//, + 'ml_deleted' => '0', + 'num_maps' => '1', + 'num_medias' => '1', + 'num_sfds' => '2', + 'num_streams' => '2', + 'num_tags' => '1', + 'recording_metadata' => '', + 'redis_hosted_db' => '2', + 'tos' => '0' + }, + 'map-0' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map_sfds-0' => [ + 'loc-0', + '0', + '1' + ], + 'maps-0' => [ + '0' + ], + 'media-0' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8 9', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '65544', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '0', + 'type' => 'audio' + }, + 'medias-0' => [ + '0' + ], + 'payload_types-0' => [ + '0/PCMU/8000///0/20' + ], + 'rtcp_sinks-0' => [], + 'rtcp_sinks-1' => [], + 'rtp_sinks-0' => [], + 'rtp_sinks-1' => [], + 'sfd-0' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '0' + }, + 'sfd-1' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '1' + }, + 'ssrc_table-0' => [], + 'stream-0' => { + 'advertised_endpoint' => '198.51.100.14:6042', + 'component' => '1', + 'endpoint' => '198.51.100.14:6042', + 'last_packet' => qr//, + 'media' => '0', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '1', + 'sfd' => '0', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-1' => { + 'advertised_endpoint' => '198.51.100.14:6043', + 'component' => '2', + 'endpoint' => '198.51.100.14:6043', + 'last_packet' => qr//, + 'media' => '0', + 'ps_flags' => '1179649', + 'rtcp_sibling' => '4294967295', + 'sfd' => '1', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream_sfds-0' => [ + '0' + ], + 'stream_sfds-1' => [ + '1' + ], + 'streams-0' => [ + '0', + '1' + ], + 'media-subscriptions-0' => [], + 'tag-0' => { + 'block_dtmf' => '0', + 'created' => qr//, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => ft() + } +}; + +publish('publish/subscribe', + { }, < [], + 'associated_tags-1' => [], + 'json' => { + 'block_dtmf' => '0', + 'call_flags' => 0, + 'created' => qr//, + 'created_from' => qr//, + 'created_from_addr' => qr//, + 'deleted' => '0', + 'destroyed' => '0', + 'last_signal' => qr//, + 'ml_deleted' => '0', + 'num_maps' => '2', + 'num_medias' => '2', + 'num_sfds' => '4', + 'num_streams' => '4', + 'num_tags' => '2', + 'recording_metadata' => '', + 'redis_hosted_db' => '2', + 'tos' => '0' + }, + 'map-0' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map-1' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map_sfds-0' => [ + 'loc-0', + '0', + '1' + ], + 'map_sfds-1' => [ + 'loc-0', + '2', + '3' + ], + 'maps-0' => [ + '0' + ], + 'maps-1' => [ + '1' + ], + 'media-0' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8 9', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '65544', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '0', + 'type' => 'audio' + }, + 'media-1' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8 9', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '2097156', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '1', + 'type' => 'audio' + }, + 'medias-0' => [ + '0' + ], + 'medias-1' => [ + '1' + ], + 'payload_types-0' => [ + '0/PCMU/8000///0/20' + ], + 'payload_types-1' => [ + '0/PCMU/8000///0/20' + ], + 'rtcp_sinks-0' => [], + 'rtcp_sinks-1' => [ + '3' + ], + 'rtcp_sinks-2' => [], + 'rtcp_sinks-3' => [], + 'rtp_sinks-0' => [ + '2' + ], + 'rtp_sinks-1' => [], + 'rtp_sinks-2' => [], + 'rtp_sinks-3' => [], + 'sfd-0' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '0' + }, + 'sfd-1' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '1' + }, + 'sfd-2' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '2' + }, + 'sfd-3' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '3' + }, + 'ssrc_table-0' => [], + 'ssrc_table-1' => [], + 'stream-0' => { + 'advertised_endpoint' => '198.51.100.14:6042', + 'component' => '1', + 'endpoint' => '198.51.100.14:6042', + 'last_packet' => qr//, + 'media' => '0', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '1', + 'sfd' => '0', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-1' => { + 'advertised_endpoint' => '198.51.100.14:6043', + 'component' => '2', + 'endpoint' => '198.51.100.14:6043', + 'last_packet' => qr//, + 'media' => '0', + 'ps_flags' => '68288513', + 'rtcp_sibling' => '4294967295', + 'sfd' => '1', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-2' => { + 'advertised_endpoint' => '', + 'component' => '1', + 'endpoint' => '', + 'last_packet' => qr//, + 'media' => '1', + 'ps_flags' => '65536', + 'rtcp_sibling' => '3', + 'sfd' => '2', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-3' => { + 'advertised_endpoint' => '', + 'component' => '2', + 'endpoint' => '', + 'last_packet' => qr//, + 'media' => '1', + 'ps_flags' => '131072', + 'rtcp_sibling' => '4294967295', + 'sfd' => '3', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream_sfds-0' => [ + '0' + ], + 'stream_sfds-1' => [ + '1' + ], + 'stream_sfds-2' => [ + '2' + ], + 'stream_sfds-3' => [ + '3' + ], + 'streams-0' => [ + '0', + '1' + ], + 'streams-1' => [ + '2', + '3' + ], + 'media-subscriptions-0' => [], + 'media-subscriptions-1' => [ + '0/0/0/0' + ], + 'tag-0' => { + 'block_dtmf' => '0', + 'created' => qr//, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => ft() + }, + 'tag-1' => { + 'block_dtmf' => '0', + 'created' => qr//, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => qr//, + } +}; + +($ftr, $ttr, undef) = subscribe_request('publish/subscribe', + { 'from-tag' => ft() }, < [], + 'associated_tags-1' => [], + 'json' => { + 'block_dtmf' => '0', + 'call_flags' => 0, + 'created' => qr//, + 'created_from' => qr//, + 'created_from_addr' => qr//, + 'deleted' => '0', + 'destroyed' => '0', + 'last_signal' => qr//, + 'ml_deleted' => '0', + 'num_maps' => '2', + 'num_medias' => '2', + 'num_sfds' => '4', + 'num_streams' => '4', + 'num_tags' => '2', + 'recording_metadata' => '', + 'redis_hosted_db' => '2', + 'tos' => '0' + }, + 'map-0' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map-1' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map_sfds-0' => [ + 'loc-0', + '0', + '1' + ], + 'map_sfds-1' => [ + 'loc-0', + '2', + '3' + ], + 'maps-0' => [ + '0' + ], + 'maps-1' => [ + '1' + ], + 'media-0' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8 9', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '65544', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '0', + 'type' => 'audio' + }, + 'media-1' => { + 'desired_family' => 'IP4', + 'format_str' => '0', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '2162692', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '1', + 'type' => 'audio' + }, + 'medias-0' => [ + '0' + ], + 'medias-1' => [ + '1' + ], + 'payload_types-0' => [ + '0/PCMU/8000///0/20' + ], + 'payload_types-1' => [ + '0/PCMU/8000///0/20' + ], + 'rtcp_sinks-0' => [], + 'rtcp_sinks-1' => [ + '3' + ], + 'rtcp_sinks-2' => [], + 'rtcp_sinks-3' => [], + 'rtp_sinks-0' => [ + '2' + ], + 'rtp_sinks-1' => [], + 'rtp_sinks-2' => [], + 'rtp_sinks-3' => [], + 'sfd-0' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '0' + }, + 'sfd-1' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '1' + }, + 'sfd-2' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '2' + }, + 'sfd-3' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '3' + }, + 'ssrc_table-0' => [], + 'ssrc_table-1' => [], + 'stream-0' => { + 'advertised_endpoint' => '198.51.100.14:6042', + 'component' => '1', + 'endpoint' => '198.51.100.14:6042', + 'last_packet' => qr//, + 'media' => '0', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '1', + 'sfd' => '0', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-1' => { + 'advertised_endpoint' => '198.51.100.14:6043', + 'component' => '2', + 'endpoint' => '198.51.100.14:6043', + 'last_packet' => qr//, + 'media' => '0', + 'ps_flags' => '1179649', + 'rtcp_sibling' => '4294967295', + 'sfd' => '1', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-2' => { + 'advertised_endpoint' => '198.51.100.14:6044', + 'component' => '1', + 'endpoint' => '198.51.100.14:6044', + 'last_packet' => qr//, + 'media' => '1', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '3', + 'sfd' => '2', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-3' => { + 'advertised_endpoint' => '198.51.100.14:6045', + 'component' => '2', + 'endpoint' => '198.51.100.14:6045', + 'last_packet' => qr//, + 'media' => '1', + 'ps_flags' => '1179649', + 'rtcp_sibling' => '4294967295', + 'sfd' => '3', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream_sfds-0' => [ + '0' + ], + 'stream_sfds-1' => [ + '1' + ], + 'stream_sfds-2' => [ + '2' + ], + 'stream_sfds-3' => [ + '3' + ], + 'streams-0' => [ + '0', + '1' + ], + 'streams-1' => [ + '2', + '3' + ], + 'media-subscriptions-0' => [], + 'media-subscriptions-1' => [ + '0/0/0/0' + ], + 'tag-0' => { + 'block_dtmf' => '0', + 'created' => qr//, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => ft() + }, + 'tag-1' => { + 'block_dtmf' => '0', + 'created' => qr//, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => qr//, + } +}; + +subscribe_answer('publish/subscribe', + { 'to-tag' => $ttr }, < [], + 'associated_tags-1' => [], + 'associated_tags-2' => [], + 'json' => { + 'block_dtmf' => '0', + 'call_flags' => 0, + 'created' => qr//, + 'created_from' => qr//, + 'created_from_addr' => qr//, + 'deleted' => '0', + 'destroyed' => '0', + 'last_signal' => qr//, + 'ml_deleted' => '0', + 'num_maps' => '3', + 'num_medias' => '3', + 'num_sfds' => '6', + 'num_streams' => '6', + 'num_tags' => '3', + 'recording_metadata' => '', + 'redis_hosted_db' => '2', + 'tos' => '0' + }, + 'map-0' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map-1' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map-2' => { + 'endpoint' => '', + 'intf_preferred_family' => 'IP4', + 'logical_intf' => 'foo', + 'num_ports' => '2', + 'wildcard' => '1' + }, + 'map_sfds-0' => [ + 'loc-0', + '0', + '1' + ], + 'map_sfds-1' => [ + 'loc-0', + '2', + '3' + ], + 'map_sfds-2' => [ + 'loc-0', + '4', + '5' + ], + 'maps-0' => [ + '0' + ], + 'maps-1' => [ + '1' + ], + 'maps-2' => [ + '2' + ], + 'media-0' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8 9', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '65544', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '0', + 'type' => 'audio' + }, + 'media-1' => { + 'desired_family' => 'IP4', + 'format_str' => '0', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '2162692', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '1', + 'type' => 'audio' + }, + 'media-2' => { + 'desired_family' => 'IP4', + 'format_str' => '0 8 9', + 'index' => '1', + 'logical_intf' => 'foo', + 'media_flags' => '2097156', + 'protocol' => 'RTP/AVP', + 'ptime' => '0', + 'tag' => '2', + 'type' => 'audio' + }, + 'medias-0' => [ + '0' + ], + 'medias-1' => [ + '1' + ], + 'medias-2' => [ + '2' + ], + 'payload_types-0' => [ + '0/PCMU/8000///0/20' + ], + 'payload_types-1' => [ + '0/PCMU/8000///0/20' + ], + 'payload_types-2' => [ + '0/PCMU/8000///0/20' + ], + 'rtcp_sinks-0' => [], + 'rtcp_sinks-1' => [ + '3', + '5' + ], + 'rtcp_sinks-2' => [], + 'rtcp_sinks-3' => [], + 'rtcp_sinks-4' => [], + 'rtcp_sinks-5' => [], + 'rtp_sinks-0' => [ + '2', + '4' + ], + 'rtp_sinks-1' => [], + 'rtp_sinks-2' => [], + 'rtp_sinks-3' => [], + 'rtp_sinks-4' => [], + 'rtp_sinks-5' => [], + 'sfd-0' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '0' + }, + 'sfd-1' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '1' + }, + 'sfd-2' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '2' + }, + 'sfd-3' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '3' + }, + 'sfd-4' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '4' + }, + 'sfd-5' => { + 'fd' => qr//, + 'local_intf_uid' => '0', + 'localport' => qr//, + 'logical_intf' => 'foo', + 'pref_family' => 'IP4', + 'stream' => '5' + }, + 'ssrc_table-0' => [], + 'ssrc_table-1' => [], + 'ssrc_table-2' => [], + 'stream-0' => { + 'advertised_endpoint' => '198.51.100.14:6042', + 'component' => '1', + 'endpoint' => '198.51.100.14:6042', + 'last_packet' => qr//, + 'media' => '0', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '1', + 'sfd' => '0', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-1' => { + 'advertised_endpoint' => '198.51.100.14:6043', + 'component' => '2', + 'endpoint' => '198.51.100.14:6043', + 'last_packet' => qr//, + 'media' => '0', + 'ps_flags' => '68288513', + 'rtcp_sibling' => '4294967295', + 'sfd' => '1', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-2' => { + 'advertised_endpoint' => '198.51.100.14:6044', + 'component' => '1', + 'endpoint' => '198.51.100.14:6044', + 'last_packet' => qr//, + 'media' => '1', + 'ps_flags' => '1114112', + 'rtcp_sibling' => '3', + 'sfd' => '2', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-3' => { + 'advertised_endpoint' => '198.51.100.14:6045', + 'component' => '2', + 'endpoint' => '198.51.100.14:6045', + 'last_packet' => qr//, + 'media' => '1', + 'ps_flags' => '1179649', + 'rtcp_sibling' => '4294967295', + 'sfd' => '3', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-4' => { + 'advertised_endpoint' => '', + 'component' => '1', + 'endpoint' => '', + 'last_packet' => qr//, + 'media' => '2', + 'ps_flags' => '65536', + 'rtcp_sibling' => '5', + 'sfd' => '4', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream-5' => { + 'advertised_endpoint' => '', + 'component' => '2', + 'endpoint' => '', + 'last_packet' => qr//, + 'media' => '2', + 'ps_flags' => '131072', + 'rtcp_sibling' => '4294967295', + 'sfd' => '5', + 'stats-bytes' => '0', + 'stats-errors' => '0', + 'stats-packets' => '0' + }, + 'stream_sfds-0' => [ + '0' + ], + 'stream_sfds-1' => [ + '1' + ], + 'stream_sfds-2' => [ + '2' + ], + 'stream_sfds-3' => [ + '3' + ], + 'stream_sfds-4' => [ + '4' + ], + 'stream_sfds-5' => [ + '5' + ], + 'streams-0' => [ + '0', + '1' + ], + 'streams-1' => [ + '2', + '3' + ], + 'streams-2' => [ + '4', + '5' + ], + 'media-subscriptions-0' => [], + 'media-subscriptions-1' => [ + '0/0/0/0' + ], + 'media-subscriptions-2' => [ + '0/0/0/0' + ], + 'tag-0' => { + 'block_dtmf' => '0', + 'created' => qr//, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => ft() + }, + 'tag-1' => { + 'block_dtmf' => '0', + 'created' => qr//, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => qr//, + }, + 'tag-2' => { + 'block_dtmf' => '0', + 'created' => qr//, + 'deleted' => '0', + 'desired_family' => 'IP4', + 'logical_intf' => 'foo', + 'ml_flags' => 0, + 'tag' => qr//, + } +}; + +($ftr, $ttr, undef) = subscribe_request('publish/subscribe', + { 'from-tag' => ft() }, <{'media-2'}{format_str} = '0'; +$json_exp->{'media-2'}{media_flags} = '2162692'; +$json_exp->{'stream-1'}{ps_flags} = '1179649'; +$json_exp->{'stream-4'}{advertised_endpoint} = '198.51.100.14:6046'; +$json_exp->{'stream-4'}{endpoint} = '198.51.100.14:6046'; +$json_exp->{'stream-4'}{ps_flags} = '1114112'; +$json_exp->{'stream-5'}{advertised_endpoint} = '198.51.100.14:6047'; +$json_exp->{'stream-5'}{endpoint} = '198.51.100.14:6047'; +$json_exp->{'stream-5'}{ps_flags} = '1179649'; + +subscribe_answer('publish/subscribe', + { 'to-tag' => $ttr }, <