]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
bridge_simple: Added support for streams
authorKevin Harwell <kharwell@digium.com>
Tue, 25 Apr 2017 16:49:16 +0000 (11:49 -0500)
committerKevin Harwell <kharwell@digium.com>
Wed, 3 May 2017 21:36:22 +0000 (16:36 -0500)
This patch is the first cut at adding stream support to the bridging framework.
Changes were made to the framework that allows mapping of stream topologies to
a bridge's supported media types.

The first channel to enter a bridge initially defines the media types for a
bridge (i.e. a one to one mapping is created between the bridge and the first
channel). Subsequently added channels merge their media types into the bridge's
adding to it when necessary. This allows channels with different sized
topologies to map correctly to each other according to media type. The bridge
drops any frame that does not have a matching index into a given write stream.

For now though, bridge_simple will align its two channels according to size or
first to join. Once both channels join the bridge the one with the most streams
will indicate to the other channel to update its streams to be the same as that
of the other. If both channels have the same number of streams then the first
channel to join is chosen as the stream base.

A topology change source was also added to a channel when a stream toplogy
change request is made. This allows subsystems to know whether or not they
initiated a change request. Thus avoiding potential recursive situations.

ASTERISK-26966 #close

Change-Id: I1eb5987921dd80c3cdcf52accc136393ca2d4163

13 files changed:
bridges/bridge_simple.c
include/asterisk/bridge.h
include/asterisk/bridge_channel.h
include/asterisk/bridge_technology.h
include/asterisk/channel.h
include/asterisk/channel_internal.h
include/asterisk/stream.h
main/bridge.c
main/bridge_channel.c
main/channel.c
main/channel_internal_api.c
main/stream.c
tests/test_stream.c

index 35544f84fac1e7535e773d8fb8beaf187a737e88..47f41cbb3b3b82a34d2003f9ca3a75f67f268743 100644 (file)
 #include "asterisk/bridge.h"
 #include "asterisk/bridge_technology.h"
 #include "asterisk/frame.h"
+#include "asterisk/stream.h"
+
+static void simple_bridge_stream_topology_changed(struct ast_bridge *bridge,
+               struct ast_bridge_channel *bridge_channel);
 
 static int simple_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
@@ -56,7 +60,13 @@ static int simple_bridge_join(struct ast_bridge *bridge, struct ast_bridge_chann
                return 0;
        }
 
-       return ast_channel_make_compatible(c0, c1);
+       if (ast_channel_make_compatible(c0, c1)) {
+               return -1;
+       }
+
+       /* Align stream topologies */
+       simple_bridge_stream_topology_changed(bridge, NULL);
+       return 0;
 }
 
 static int simple_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
@@ -70,8 +80,34 @@ static struct ast_bridge_technology simple_bridge = {
        .preference = AST_BRIDGE_PREFERENCE_BASE_1TO1MIX,
        .join = simple_bridge_join,
        .write = simple_bridge_write,
+       .stream_topology_changed = simple_bridge_stream_topology_changed,
 };
 
+static void simple_bridge_stream_topology_changed(struct ast_bridge *bridge,
+               struct ast_bridge_channel *bridge_channel)
+{
+       struct ast_channel *c0 = AST_LIST_FIRST(&bridge->channels)->chan;
+       struct ast_channel *c1 = AST_LIST_LAST(&bridge->channels)->chan;
+       struct ast_stream_topology *t0 = ast_channel_get_stream_topology(c0);
+       struct ast_stream_topology *t1 = ast_channel_get_stream_topology(c1);
+
+       /*
+        * The bridge_channel should only be NULL after both channels join
+        * the bridge and their topologies are being aligned.
+        */
+       if (bridge_channel && ast_channel_get_stream_topology_change_source(
+                   bridge_channel->chan) == &simple_bridge) {
+               return;
+       }
+
+       /* Align topologies according to size or first channel to join */
+       if (ast_stream_topology_get_count(t0) < ast_stream_topology_get_count(t1)) {
+               ast_channel_request_stream_topology_change(c0, t1, &simple_bridge);
+       } else {
+               ast_channel_request_stream_topology_change(c1, t0, &simple_bridge);
+       }
+}
+
 static int unload_module(void)
 {
        ast_bridge_technology_unregister(&simple_bridge);
index ffe08da13ef1104ca85013a37dcdb947f2d7b7c7..a9b01a6bbec25e06e5410bd9c67c63bbb83bbea2 100644 (file)
@@ -323,6 +323,9 @@ struct ast_bridge {
                /*! Immutable bridge UUID. */
                AST_STRING_FIELD(uniqueid);
        );
+
+       /*! Type mapping used for media routing */
+       struct ast_vector_int media_types;
 };
 
 /*! \brief Bridge base class virtual method table. */
index a7971df27f28015475ed03fbe4ea6dde98ff43b1..dd72f3275fc5d199d56d6e74997242b1dec04cbf 100644 (file)
@@ -183,6 +183,12 @@ struct ast_bridge_channel {
                        unsigned int padding:30;
                };
        };
+       struct {
+               /*! An index mapping of where a channel's media needs to be routed */
+               struct ast_vector_int to_bridge;
+               /*! An index mapping of where a bridge's media needs to be routed */
+               struct ast_vector_int to_channel;
+       } stream_map;
 };
 
 /*!
@@ -704,6 +710,27 @@ void ast_bridge_channel_feature_digit_add(struct ast_bridge_channel *bridge_chan
  */
 void ast_bridge_channel_feature_digit(struct ast_bridge_channel *bridge_channel, int digit);
 
+/*!
+ * \brief Maps a channel's stream topology to and from the bridge
+ * \since 15.0.0
+ *
+ * When a channel joins a bridge or its associated stream topology is updated, each stream
+ * in the topology needs to be mapped according to its media type to the bridge. Calling
+ * this method creates a mapping of each stream on the channel indexed to the bridge's
+ * supported media types and vice versa (i.e. bridge's media types indexed to channel
+ * streams).
+ *
+ * The first channel to join the bridge creates the initial order for the bridge's media
+ * types (e.g. a one to one mapping is made). Subsequently added channels are mapped to
+ * that order adding more media types if/when the newly added channel has more streams
+ * and/or media types specified by the bridge.
+ *
+ * \param bridge_channel Channel to map
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_stream_map(struct ast_bridge_channel *bridge_channel);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
index 843d93ccfc614dde01e34590ab6363a12d8ed444..09b0fc0e8d84c5f908794be1910a894db5cb447c 100644 (file)
@@ -158,6 +158,25 @@ struct ast_bridge_technology {
         * \note On entry, bridge is already locked.
         */
        int (*write)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame);
+       /*!
+        * \brief Callback for when a request has been made to change a stream topology on a channel
+        *
+        * This is called when a bridge receives a request to change the topology on the channel. A bridge
+        * technology should define a handler for this callback if it needs to update internals or intercept
+        * the request and not pass it on to other channels. This can be done by returning a nonzero value.
+        *
+        * \retval 0 Frame accepted by the bridge.
+        * \retval -1 Frame rejected.
+        */
+       int (*stream_topology_request_change)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
+       /*!
+        * \brief Callback for when a stream topology changes on the channel
+        *
+        * This is called when a bridge receives an indication that a topology has been changed on a channel
+        * and the new topology has been mapped to the bridge. A bridge technology should define a handler for
+        * this callback if it needs to update internals due to a channel's topology changing.
+        */
+       void (*stream_topology_changed)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
        /*! TRUE if the bridge technology is currently suspended. */
        unsigned int suspended:1;
        /*! Module this bridge technology belongs to. It is used for reference counting bridges using the technology. */
index 128cd3056c98711ec45690c6c3600397a7332e77..889d3ff07e18bd786e7439ad57fb2611013b4e1e 100644 (file)
@@ -4904,6 +4904,7 @@ int ast_channel_is_multistream(struct ast_channel *chan);
  *
  * \param chan The channel to change
  * \param topology The new stream topology
+ * \param change_source The source that initiated the change
  *
  * \pre chan is locked
  *
@@ -4918,7 +4919,8 @@ int ast_channel_is_multistream(struct ast_channel *chan);
  * \note This interface is provided for applications and resources to request that the topology change.
  *       It is not for use by the channel driver itself.
  */
-int ast_channel_request_stream_topology_change(struct ast_channel *chan, struct ast_stream_topology *topology);
+int ast_channel_request_stream_topology_change(struct ast_channel *chan,
+       struct ast_stream_topology *topology, void *change_source);
 
 /*!
  * \brief Provide notice to a channel that the stream topology has changed
@@ -4936,4 +4938,13 @@ int ast_channel_request_stream_topology_change(struct ast_channel *chan, struct
  */
 int ast_channel_stream_topology_changed(struct ast_channel *chan, struct ast_stream_topology *topology);
 
+/*!
+ * \brief Retrieve the source that initiated the last stream topology change
+ *
+ * \param chan The channel
+ *
+ * \retval The channel's stream topology change source
+ */
+void *ast_channel_get_stream_topology_change_source(struct ast_channel *chan);
+
 #endif /* _ASTERISK_CHANNEL_H */
index 3de2b14aa1ca4b0c97466260925db140dd1f4a41..dd791a4169ca6c5e4eba592780e035dc286e1639 100644 (file)
@@ -29,5 +29,7 @@ void ast_channel_internal_errno_set(enum ast_channel_error error);
 enum ast_channel_error ast_channel_internal_errno(void);
 void ast_channel_internal_set_stream_topology(struct ast_channel *chan,
        struct ast_stream_topology *topology);
+void ast_channel_internal_set_stream_topology_change_source(
+       struct ast_channel *chan, void *change_source);
 void ast_channel_internal_swap_stream_topology(struct ast_channel *chan1,
        struct ast_channel *chan2);
index 1e07407a9f834625633800c393821d70f44a2a87..1bb34b72a27d73daad4a66d5eaf8f1206196acf0 100644 (file)
@@ -27,6 +27,7 @@
 #define _AST_STREAM_H_
 
 #include "asterisk/codec.h"
+#include "asterisk/vector.h"
 
 /*!
  * \brief Forward declaration for a stream, as it is opaque
@@ -43,6 +44,11 @@ struct ast_format_cap;
  */
 struct ast_stream_topology;
 
+/*!
+ * \brief A mapping of two topologies.
+ */
+struct ast_stream_topology_map;
+
 typedef void (*ast_stream_data_free_fn)(void *);
 
 /*!
@@ -405,4 +411,21 @@ struct ast_stream *ast_stream_topology_get_first_stream_by_type(
        const struct ast_stream_topology *topology,
        enum ast_media_type type);
 
+/*!
+ * \brief Map a given topology's streams to the given types.
+ *
+ * \note The given vectors in which mapping values are placed are reset by
+ *       this function. This means if those vectors already contain mapping
+ *       values they will be lost.
+ *
+ * \param topology The topology to map
+ * \param types The media types to be mapped
+ * \param v0 Index mapping of topology to types
+ * \param v1 Index mapping of types to topology
+ *
+ * \since 15
+ */
+void ast_stream_topology_map(const struct ast_stream_topology *topology,
+       struct ast_vector_int *types, struct ast_vector_int *v0, struct ast_vector_int *v1);
+
 #endif /* _AST_STREAM_H */
index d40c3ec9ac999ae07b2ab604483274247a3b2261..9d9a3118bbe8b12fda44d71604709382fc640565 100644 (file)
@@ -440,6 +440,13 @@ static void bridge_channel_complete_join(struct ast_bridge *bridge, struct ast_b
        }
 
        bridge_channel->just_joined = 0;
+
+       /*
+        * When a channel joins the bridge its streams need to be mapped to the bridge's
+        * media types vector. This way all streams map to the same media type index for
+        * a given channel.
+        */
+       ast_bridge_channel_stream_map(bridge_channel);
 }
 
 /*!
@@ -689,6 +696,8 @@ static void destroy_bridge(void *obj)
                bridge->technology = NULL;
        }
 
+       AST_VECTOR_FREE(&bridge->media_types);
+
        bridge->callid = 0;
 
        cleanup_video_mode(bridge);
@@ -744,6 +753,8 @@ struct ast_bridge *bridge_alloc(size_t size, const struct ast_bridge_methods *v_
 
        bridge->v_table = v_table;
 
+       AST_VECTOR_INIT(&bridge->media_types, AST_MEDIA_TYPE_END);
+
        return bridge;
 }
 
index 89222d365e71bd2399c0d696fa7e011afe7cf17f..4f166fff06f9d84562c1d7566773fa2c355cd316 100644 (file)
@@ -55,6 +55,7 @@
 #include "asterisk/causes.h"
 #include "asterisk/test.h"
 #include "asterisk/sem.h"
+#include "asterisk/stream.h"
 
 /*!
  * \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge.
@@ -982,6 +983,16 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st
                return 0;
        }
 
+       if (ast_channel_is_multistream(bridge_channel->chan) &&
+           (fr->frametype == AST_FRAME_IMAGE || fr->frametype == AST_FRAME_TEXT ||
+            fr->frametype == AST_FRAME_VIDEO || fr->frametype == AST_FRAME_VOICE)) {
+               /* Media frames need to be mapped to an appropriate write stream */
+               dup->stream_num = AST_VECTOR_GET(
+                       &bridge_channel->stream_map.to_bridge, fr->stream_num);
+       } else {
+               dup->stream_num = -1;
+       }
+
        AST_LIST_INSERT_TAIL(&bridge_channel->wr_queue, dup, frame_list);
        if (ast_alertpipe_write(bridge_channel->alert_pipe)) {
                ast_log(LOG_ERROR, "We couldn't write alert pipe for %p(%s)... something is VERY wrong\n",
@@ -2249,6 +2260,9 @@ static void bridge_channel_handle_control(struct ast_bridge_channel *bridge_chan
                /* Should never happen. */
                ast_assert(0);
                break;
+       case AST_CONTROL_STREAM_TOPOLOGY_CHANGED:
+               ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
+               break;
        default:
                ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
                break;
@@ -2268,6 +2282,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe
 {
        struct ast_frame *fr;
        struct sync_payload *sync_payload;
+       int num;
 
        ast_bridge_channel_lock(bridge_channel);
 
@@ -2324,9 +2339,18 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe
        case AST_FRAME_NULL:
                break;
        default:
+               if (fr->stream_num >= (int)AST_VECTOR_SIZE(&bridge_channel->stream_map.to_channel)) {
+                       /* Nowhere to write to, so drop it */
+                       break;
+               }
+
+               /* Find what stream number to write to for the channel */
+               num = fr->stream_num < 0 ? -1 :
+                       AST_VECTOR_GET(&bridge_channel->stream_map.to_channel, fr->stream_num);
+
                /* Write the frame to the channel. */
                bridge_channel->activity = BRIDGE_CHANNEL_THREAD_SIMPLE;
-               ast_write(bridge_channel->chan, fr);
+               ast_write_stream(bridge_channel->chan, num, fr);
                break;
        }
        bridge_frame_free(fr);
@@ -2435,6 +2459,27 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
                case AST_CONTROL_ANSWER:
                        ast_channel_publish_dial(NULL, bridge_channel->chan, NULL, controls[frame->subclass.integer]);
                        break;
+               case AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE:
+                       if (bridge_channel->bridge->technology->stream_topology_request_change &&
+                           bridge_channel->bridge->technology->stream_topology_request_change(
+                                   bridge_channel->bridge, bridge_channel)) {
+                               /* Topology change was denied so drop frame */
+                               bridge_frame_free(frame);
+                               return;
+                       }
+                       break;
+               case AST_CONTROL_STREAM_TOPOLOGY_CHANGED:
+                       /*
+                        * If a stream topology has changed then the bridge_channel's
+                        * media mapping needs to be updated.
+                        */
+                       ast_bridge_channel_stream_map(bridge_channel);
+
+                       if (bridge_channel->bridge->technology->stream_topology_changed) {
+                               bridge_channel->bridge->technology->stream_topology_changed(
+                                       bridge_channel->bridge, bridge_channel);
+                       }
+                       break;
                default:
                        break;
                }
@@ -2885,6 +2930,9 @@ static void bridge_channel_destroy(void *obj)
 
        ao2_cleanup(bridge_channel->write_format);
        ao2_cleanup(bridge_channel->read_format);
+
+       AST_VECTOR_FREE(&bridge_channel->stream_map.to_bridge);
+       AST_VECTOR_FREE(&bridge_channel->stream_map.to_channel);
 }
 
 struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *bridge)
@@ -2905,5 +2953,14 @@ struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *brid
                ao2_ref(bridge_channel->bridge, +1);
        }
 
+       /* The stream_map is initialized later - see ast_bridge_channel_stream_map */
+
        return bridge_channel;
 }
+
+void ast_bridge_channel_stream_map(struct ast_bridge_channel *bridge_channel)
+{
+       ast_stream_topology_map(ast_channel_get_stream_topology(bridge_channel->chan),
+               &bridge_channel->bridge->media_types, &bridge_channel->stream_map.to_bridge,
+               &bridge_channel->stream_map.to_channel);
+}
index e37d6652557c1f70dd74f11f30e37b9ca56d661b..dbf2354995b8a9e5002e9d425be624d3e3b0a8a7 100644 (file)
@@ -10909,7 +10909,8 @@ enum ast_channel_error ast_channel_errno(void)
        return ast_channel_internal_errno();
 }
 
-int ast_channel_request_stream_topology_change(struct ast_channel *chan, struct ast_stream_topology *topology)
+int ast_channel_request_stream_topology_change(struct ast_channel *chan,
+               struct ast_stream_topology *topology, void *change_source)
 {
        ast_assert(chan != NULL);
        ast_assert(topology != NULL);
@@ -10918,6 +10919,8 @@ int ast_channel_request_stream_topology_change(struct ast_channel *chan, struct
                return -1;
        }
 
+       ast_channel_internal_set_stream_topology_change_source(chan, change_source);
+
        return ast_channel_tech(chan)->indicate(chan, AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE, topology, sizeof(topology));
 }
 
index 7f32b219665ab75f8d17e0eca0f9f16b6ef6b6b8..5e7df8983a77b275acbf2fd5e344455b44037f40 100644 (file)
@@ -219,6 +219,7 @@ struct ast_channel {
        struct stasis_forward *endpoint_forward;        /*!< Subscription for event forwarding to endpoint's topic */
        struct stasis_forward *endpoint_cache_forward; /*!< Subscription for cache updates to endpoint's topic */
        struct ast_stream_topology *stream_topology; /*!< Stream topology */
+       void *stream_topology_change_source; /*!< Source that initiated a stream topology change */
        struct ast_stream *default_streams[AST_MEDIA_TYPE_END]; /*!< Default streams indexed by media type */
 };
 
@@ -838,6 +839,17 @@ void ast_channel_internal_set_stream_topology(struct ast_channel *chan,
        channel_set_default_streams(chan);
 }
 
+void ast_channel_internal_set_stream_topology_change_source(
+       struct ast_channel *chan, void *change_source)
+{
+       chan->stream_topology_change_source = change_source;
+}
+
+void *ast_channel_get_stream_topology_change_source(struct ast_channel *chan)
+{
+       return chan->stream_topology_change_source;
+}
+
 void ast_channel_nativeformats_set(struct ast_channel *chan,
        struct ast_format_cap *value)
 {
index 39b6b1b13f63db2fc2effe6d96014d13ee33b2a8..0f2393359002736ce285d7fc0af25fda5422ebea 100644 (file)
@@ -434,3 +434,48 @@ struct ast_stream *ast_stream_topology_get_first_stream_by_type(
 
        return NULL;
 }
+
+void ast_stream_topology_map(const struct ast_stream_topology *topology,
+       struct ast_vector_int *types, struct ast_vector_int *v0, struct ast_vector_int *v1)
+{
+       int i;
+       int nths[AST_MEDIA_TYPE_END] = {0};
+       int size = ast_stream_topology_get_count(topology);
+
+       /*
+        * Clear out any old mappings and initialize the new ones
+        */
+       AST_VECTOR_FREE(v0);
+       AST_VECTOR_FREE(v1);
+
+       /*
+        * Both vectors are sized to the topology. The media types vector is always
+        * guaranteed to be the size of the given topology or greater.
+        */
+       AST_VECTOR_INIT(v0, size);
+       AST_VECTOR_INIT(v1, size);
+
+       for (i = 0; i < size; ++i) {
+               struct ast_stream *stream = ast_stream_topology_get_stream(topology, i);
+               enum ast_media_type type = ast_stream_get_type(stream);
+               int index = AST_VECTOR_GET_INDEX_NTH(types, ++nths[type],
+                       type, AST_VECTOR_ELEM_DEFAULT_CMP);
+
+               if (index == -1) {
+                       /*
+                        * If a given type is not found for an index level then update the
+                        * media types vector with that type. This keeps the media types
+                        * vector always at the max topology size.
+                        */
+                       AST_VECTOR_APPEND(types, type);
+                       index = AST_VECTOR_SIZE(types) - 1;
+               }
+
+               /*
+                * The mapping is reflexive in the sense that if it maps in one direction
+                * then the reverse direction maps back to the other's index.
+                */
+               AST_VECTOR_REPLACE(v0, i, index);
+               AST_VECTOR_REPLACE(v1, index, i);
+       }
+}
index 7eecf373babbb0647c0e06df968e2c91e2b4df34..a2a970181091129bfaf3ae17adbe03b1a9860993 100644 (file)
@@ -1592,7 +1592,7 @@ AST_TEST_DEFINE(stream_topology_change_request_from_application_non_multistream)
        topology = ast_stream_topology_alloc();
        ast_test_validate_cleanup(test, topology, res, done);
 
-       change_res = ast_channel_request_stream_topology_change(mock_channel, topology);
+       change_res = ast_channel_request_stream_topology_change(mock_channel, topology, NULL);
 
        ast_test_validate_cleanup(test, change_res == -1, res, done);
        ast_test_validate_cleanup(test, !pvt->indicated_change_request, res, done);
@@ -1700,7 +1700,7 @@ AST_TEST_DEFINE(stream_topology_change_request_from_application)
        topology = ast_stream_topology_alloc();
        ast_test_validate_cleanup(test, topology, res, done);
 
-       change_res = ast_channel_request_stream_topology_change(mock_channel, topology);
+       change_res = ast_channel_request_stream_topology_change(mock_channel, topology, NULL);
 
        ast_test_validate_cleanup(test, !change_res, res, done);
        ast_test_validate_cleanup(test, pvt->indicated_change_request, res, done);
@@ -1830,6 +1830,97 @@ AST_TEST_DEFINE(format_cap_from_stream_topology)
        return AST_TEST_PASS;
 }
 
+#define topology_append_stream(topology, name, type, res, label) \
+       do { \
+               struct ast_stream *__stream = ast_stream_alloc((name), (type)); \
+               ast_test_validate_cleanup(test, __stream, res, label); \
+               if (ast_stream_topology_append_stream((topology), __stream) < 0) { \
+                       ast_stream_free(__stream); \
+                       res = AST_TEST_FAIL; \
+                       goto label;          \
+               } \
+       } while(0)
+
+AST_TEST_DEFINE(stream_topology_map_create)
+{
+       RAII_VAR(struct ast_stream_topology *, t0, NULL, ast_stream_topology_free);
+
+       struct ast_vector_int types = { NULL };
+       struct ast_vector_int v0 = { NULL };
+       struct ast_vector_int v1 = { NULL };
+
+       enum ast_test_result_state res = AST_TEST_PASS;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "stream_topology_map_create";
+               info->category = "/main/stream/";
+               info->summary = "stream topology map creation unit test";
+               info->description =
+                       "Test that creating a stream topology map works";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       ast_test_validate(test, AST_VECTOR_INIT(&types, 5) == 0);
+
+       /* Map a first topology and check that it mapped one to one */
+       ast_test_validate_cleanup(test, (t0 = ast_stream_topology_alloc()), res, done);
+       topology_append_stream(t0, "audio", AST_MEDIA_TYPE_AUDIO, res, done);
+       topology_append_stream(t0, "video", AST_MEDIA_TYPE_VIDEO, res, done);
+
+       ast_stream_topology_map(t0, &types, &v0, &v1);
+       ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&types) == 2, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&types, 0) == AST_MEDIA_TYPE_AUDIO, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&types, 1) == AST_MEDIA_TYPE_VIDEO, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v0, 0) == 0, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v0, 1) == 1, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v1, 0) == 0, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v1, 1) == 1, res, done);
+
+       /* Map a second topology and check that it merged */
+       ast_stream_topology_free(t0);
+       ast_test_validate_cleanup(test, (t0 = ast_stream_topology_alloc()), res, done);
+       topology_append_stream(t0, "video", AST_MEDIA_TYPE_VIDEO, res, done);
+       topology_append_stream(t0, "audio", AST_MEDIA_TYPE_AUDIO, res, done);
+
+       ast_stream_topology_map(t0, &types, &v0, &v1);
+       ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&types) == 2, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&types, 0) == AST_MEDIA_TYPE_AUDIO, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&types, 1) == AST_MEDIA_TYPE_VIDEO, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v0, 0) == 1, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v0, 1) == 0, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v1, 0) == 1, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v1, 1) == 0, res, done);
+
+       /* Map a third topology with more streams and check that it merged */
+       ast_stream_topology_free(t0);
+       ast_test_validate_cleanup(test, (t0 = ast_stream_topology_alloc()), res, done);
+       topology_append_stream(t0, "video", AST_MEDIA_TYPE_VIDEO, res, done);
+       topology_append_stream(t0, "audio", AST_MEDIA_TYPE_AUDIO, res, done);
+       topology_append_stream(t0, "audio", AST_MEDIA_TYPE_AUDIO, res, done);
+
+       ast_stream_topology_map(t0, &types, &v0, &v1);
+       ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&types) == 3, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&types, 0) == AST_MEDIA_TYPE_AUDIO, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&types, 1) == AST_MEDIA_TYPE_VIDEO, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&types, 2) == AST_MEDIA_TYPE_AUDIO, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v0, 0) == 1, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v0, 1) == 0, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v0, 2) == 2, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v1, 0) == 1, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v1, 1) == 0, res, done);
+       ast_test_validate_cleanup(test, AST_VECTOR_GET(&v1, 2) == 2, res, done);
+
+done:
+       AST_VECTOR_FREE(&v1);
+       AST_VECTOR_FREE(&v0);
+       AST_VECTOR_FREE(&types);
+
+       return res;
+}
+
 static int unload_module(void)
 {
        AST_TEST_UNREGISTER(stream_create);
@@ -1855,6 +1946,7 @@ static int unload_module(void)
        AST_TEST_UNREGISTER(stream_topology_change_request_from_application);
        AST_TEST_UNREGISTER(stream_topology_change_request_from_channel);
        AST_TEST_UNREGISTER(format_cap_from_stream_topology);
+       AST_TEST_UNREGISTER(stream_topology_map_create);
        return 0;
 }
 
@@ -1882,6 +1974,7 @@ static int load_module(void)
        AST_TEST_REGISTER(stream_topology_change_request_from_application);
        AST_TEST_REGISTER(stream_topology_change_request_from_channel);
        AST_TEST_REGISTER(format_cap_from_stream_topology);
+       AST_TEST_REGISTER(stream_topology_map_create);
        return AST_MODULE_LOAD_SUCCESS;
 }