]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
channel: Add ability to request an outgoing channel with stream topology.
authorJoshua Colp <jcolp@digium.com>
Mon, 24 Apr 2017 15:59:44 +0000 (15:59 +0000)
committerJoshua Colp <jcolp@digium.com>
Thu, 27 Apr 2017 10:39:46 +0000 (10:39 +0000)
This change extends the ast_request functionality by adding another
function and callback to create an outgoing channel with a requested
stream topology. Fallback is provided by either converting the
requested stream topology into a format capabilities structure if
the channel driver does not support streams or by converting the
requested format capabilities into a stream topology if the channel
driver does support streams.

The Dial application has also been updated to request an outgoing
channel with the stream topology of the calling channel.

ASTERISK-26959

Change-Id: Ifa9037a672ac21d42dd7125aa09816dc879a70e6

apps/app_dial.c
include/asterisk/channel.h
include/asterisk/stream.h
main/channel.c
main/stream.c
tests/test_stream.c

index c8fcf4696ff611e6b838a62f4eb509e4ea2049c4..79e2a9b0bb79dc33cec3099f1400b53c973a60d0 100644 (file)
@@ -66,6 +66,7 @@
 #include "asterisk/bridge_after.h"
 #include "asterisk/features_config.h"
 #include "asterisk/max_forwards.h"
+#include "asterisk/stream.h"
 
 /*** DOCUMENTATION
        <application name="Dial" language="en_US">
@@ -970,16 +971,16 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
                c = o->chan = NULL;
                cause = AST_CAUSE_BUSY;
        } else {
-               struct ast_format_cap *nativeformats;
+               struct ast_stream_topology *topology;
 
                ast_channel_lock(in);
-               nativeformats = ao2_bump(ast_channel_nativeformats(in));
+               topology = ast_stream_topology_clone(ast_channel_get_stream_topology(in));
                ast_channel_unlock(in);
 
                /* Setup parameters */
-               c = o->chan = ast_request(tech, nativeformats, NULL, in, stuff, &cause);
+               c = o->chan = ast_request_with_stream_topology(tech, topology, NULL, in, stuff, &cause);
 
-               ao2_cleanup(nativeformats);
+               ast_stream_topology_free(topology);
 
                if (c) {
                        if (single && !caller_entertained) {
@@ -2444,7 +2445,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                char *tech = strsep(&number, "/");
                size_t tech_len;
                size_t number_len;
-               struct ast_format_cap *nativeformats;
+               struct ast_stream_topology *topology;
 
                num_dialed++;
                if (ast_strlen_zero(number)) {
@@ -2496,13 +2497,13 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                 */
                ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(chan));
 
-               nativeformats = ao2_bump(ast_channel_nativeformats(chan));
+               topology = ast_stream_topology_clone(ast_channel_get_stream_topology(chan));
 
                ast_channel_unlock(chan);
 
-               tc = ast_request(tmp->tech, nativeformats, NULL, chan, tmp->number, &cause);
+               tc = ast_request_with_stream_topology(tmp->tech, topology, NULL, chan, tmp->number, &cause);
 
-               ao2_cleanup(nativeformats);
+               ast_stream_topology_free(topology);
 
                if (!tc) {
                        /* If we can't, just go on to the next call */
index 32c9c7f67f2d6c14fdfee55303c944e5a34733bf..70856a96fc07ec511f8ddc596c36cc77b2e24bf7 100644 (file)
@@ -203,6 +203,8 @@ enum ast_bridge_result {
 
 typedef unsigned long long ast_group_t;
 
+struct ast_stream_topology;
+
 /*! \todo Add an explanation of an Asterisk generator
 */
 struct ast_generator {
@@ -630,6 +632,26 @@ struct ast_channel_tech {
         */
        struct ast_channel *(* const requester)(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause);
 
+       /*!
+        * \brief Requester - to set up call data structures (pvt's) with stream topology
+        *
+        * \param type type of channel to request
+        * \param topology Stream topology for requested channel
+        * \param assignedid Unique ID string to assign to channel
+        * \param requestor channel asking for data
+        * \param addr destination of the call
+        * \param cause Cause of failure
+        *
+        * \details
+        * Request a channel of a given type, with addr as optional information used
+        * by the low level module
+        *
+        * \retval NULL failure
+        * \retval non-NULL channel on success
+        */
+       struct ast_channel *(* const requester_with_stream_topology)(const char *type, struct ast_stream_topology *topology, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause);
+
+
        int (* const devicestate)(const char *device_number);   /*!< Devicestate call back */
        int (* const presencestate)(const char *presence_provider, char **subtype, char **message); /*!< Presencestate callback */
 
@@ -1393,6 +1415,25 @@ struct ast_channel *ast_channel_release(struct ast_channel *chan);
  */
 struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause);
 
+/*!
+ * \brief Requests a channel (specifying stream topology)
+ *
+ * \param type type of channel to request
+ * \param topology Stream topology for requested channel
+ * \param assignedids Unique ID to create channel with
+ * \param requestor channel asking for data
+ * \param addr destination of the call
+ * \param cause Cause of failure
+ *
+ * \details
+ * Request a channel of a given type, with addr as optional information used
+ * by the low level module
+ *
+ * \retval NULL failure
+ * \retval non-NULL channel on success
+ */
+struct ast_channel *ast_request_with_stream_topology(const char *type, struct ast_stream_topology *topology, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause);
+
 enum ast_channel_requestor_relationship {
        /*! The requestor is the future bridge peer of the channel. */
        AST_CHANNEL_REQUESTOR_BRIDGE_PEER,
index 1becae25a4fe1845ebabb792d57e914d25113742..924bfb65cdf408e79866fc1bc6261f283634bee7 100644 (file)
@@ -374,6 +374,22 @@ int ast_stream_topology_set_stream(struct ast_stream_topology *topology,
 struct ast_stream_topology *ast_stream_topology_create_from_format_cap(
        struct ast_format_cap *cap);
 
+/*!
+ * \brief A helper function that, given a stream topology, creates a format
+ * capabilities structure containing all formats from all streams.
+ *
+ * \param topology The topology of streams
+ *
+  * \retval non-NULL success
+  * \retval NULL failure
+  *
+  * \note The stream topology is NOT altered by this function.
+  *
+  * \since 15
+  */
+struct ast_format_cap *ast_format_cap_from_stream_topology(
+    struct ast_stream_topology *topology);
+
 /*!
  * \brief Gets the first stream of a specific type from the topology
  *
index 31f363938f1e1779892575fe739c5835be285dcb..e37d6652557c1f70dd74f11f30e37b9ca56d661b 100644 (file)
@@ -5976,7 +5976,8 @@ static int set_security_requirements(const struct ast_channel *requestor, struct
        return 0;
 }
 
-struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
+static struct ast_channel *request_channel(const char *type, struct ast_format_cap *request_cap, struct ast_stream_topology *topology,
+       const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
 {
        struct chanlist *chan;
        struct ast_channel *c;
@@ -5993,13 +5994,47 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
        }
 
        AST_RWLIST_TRAVERSE(&backends, chan, list) {
+               if (strcasecmp(type, chan->tech->type)) {
+                       continue;
+               }
+
+               break;
+       }
+
+       AST_RWLIST_UNLOCK(&backends);
+
+       if (!chan) {
+               ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
+               *cause = AST_CAUSE_NOSUCHDRIVER;
+               return NULL;
+       }
+
+       /* Allow either format capabilities or stream topology to be provided and adapt */
+       if (chan->tech->requester_with_stream_topology) {
+               struct ast_stream_topology *tmp_converted_topology = NULL;
+
+               if (!topology && request_cap) {
+                       /* Turn the requested capabilities into a stream topology */
+                       topology = tmp_converted_topology = ast_stream_topology_create_from_format_cap(request_cap);
+               }
+
+               c = chan->tech->requester_with_stream_topology(type, topology, assignedids, requestor, addr, cause);
+
+               ast_stream_topology_free(tmp_converted_topology);
+               if (!c) {
+                       return NULL;
+               }
+       } else if (chan->tech->requester) {
+               struct ast_format_cap *tmp_converted_cap = NULL;
                struct ast_format_cap *tmp_cap;
                RAII_VAR(struct ast_format *, tmp_fmt, NULL, ao2_cleanup);
                RAII_VAR(struct ast_format *, best_audio_fmt, NULL, ao2_cleanup);
                struct ast_format_cap *joint_cap;
 
-               if (strcasecmp(type, chan->tech->type))
-                       continue;
+               if (!request_cap && topology) {
+                       /* Turn the request stream topology into capabilities */
+                       request_cap = tmp_converted_cap = ast_format_cap_from_stream_topology(topology);
+               }
 
                /* find the best audio format to use */
                tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
@@ -6018,13 +6053,10 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
                                        ast_format_cap_get_names(chan->tech->capabilities, &tech_codecs),
                                        ast_format_cap_get_names(request_cap, &request_codecs));
                                *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
-                               AST_RWLIST_UNLOCK(&backends);
+                               ao2_cleanup(tmp_converted_cap);
                                return NULL;
                        }
                }
-               AST_RWLIST_UNLOCK(&backends);
-               if (!chan->tech->requester)
-                       return NULL;
 
                /* XXX Only the audio format calculated as being the best for translation
                 * purposes is used for the request. This is because we don't have the ability
@@ -6033,50 +6065,58 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
                 */
                joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
                if (!joint_cap) {
+                       ao2_cleanup(tmp_converted_cap);
                        return NULL;
                }
                ast_format_cap_append_from_cap(joint_cap, request_cap, AST_MEDIA_TYPE_UNKNOWN);
                ast_format_cap_remove_by_type(joint_cap, AST_MEDIA_TYPE_AUDIO);
                ast_format_cap_append(joint_cap, best_audio_fmt, 0);
+               ao2_cleanup(tmp_converted_cap);
+
+               c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause);
 
-               if (!(c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause))) {
+               if (!c) {
                        ao2_ref(joint_cap, -1);
                        return NULL;
                }
+       } else {
+               return NULL;
+       }
 
-               if (requestor) {
-                       ast_callid callid;
-
-                       ast_channel_lock_both(c, (struct ast_channel *) requestor);
+       if (requestor) {
+               ast_callid callid;
 
-                       /* Set the newly created channel's callid to the same as the requestor. */
-                       callid = ast_channel_callid(requestor);
-                       if (callid) {
-                               ast_channel_callid_set(c, callid);
-                       }
+               ast_channel_lock_both(c, (struct ast_channel *) requestor);
 
-                       ast_channel_unlock(c);
-                       ast_channel_unlock((struct ast_channel *) requestor);
+               /* Set the newly created channel's callid to the same as the requestor. */
+               callid = ast_channel_callid(requestor);
+               if (callid) {
+                       ast_channel_callid_set(c, callid);
                }
 
-               ao2_ref(joint_cap, -1);
-
-               if (set_security_requirements(requestor, c)) {
-                       ast_log(LOG_WARNING, "Setting security requirements failed\n");
-                       ast_hangup(c);
-                       *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
-                       return NULL;
-               }
+               ast_channel_unlock(c);
+               ast_channel_unlock((struct ast_channel *) requestor);
+       }
 
-               /* no need to generate a Newchannel event here; it is done in the channel_alloc call */
-               return c;
+       if (set_security_requirements(requestor, c)) {
+               ast_log(LOG_WARNING, "Setting security requirements failed\n");
+               ast_hangup(c);
+               *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
+               return NULL;
        }
 
-       ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
-       *cause = AST_CAUSE_NOSUCHDRIVER;
-       AST_RWLIST_UNLOCK(&backends);
+       /* no need to generate a Newchannel event here; it is done in the channel_alloc call */
+       return c;
+}
 
-       return NULL;
+struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
+{
+       return request_channel(type, request_cap, NULL, assignedids, requestor, addr, cause);
+}
+
+struct ast_channel *ast_request_with_stream_topology(const char *type, struct ast_stream_topology *topology, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
+{
+       return request_channel(type, NULL, topology, assignedids, requestor, addr, cause);
 }
 
 /*!
index 9d36dbf25af4a6c9b2b55ff241e408aad42876e6..cf2633e1b76a7c6effa41196fc635295f8e06554 100644 (file)
@@ -392,6 +392,32 @@ struct ast_stream_topology *ast_stream_topology_create_from_format_cap(
        return topology;
 }
 
+struct ast_format_cap *ast_format_cap_from_stream_topology(
+    struct ast_stream_topology *topology)
+{
+       struct ast_format_cap *caps;
+       int i;
+
+       ast_assert(topology != NULL);
+
+       caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+       if (!caps) {
+               return NULL;
+       }
+
+       for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {
+               struct ast_stream *stream = AST_VECTOR_GET(&topology->streams, i);
+
+               if (!stream->formats) {
+                       continue;
+               }
+
+               ast_format_cap_append_from_cap(caps, stream->formats, AST_MEDIA_TYPE_UNKNOWN);
+       }
+
+       return caps;
+}
+
 struct ast_stream *ast_stream_topology_get_first_stream_by_type(
        const struct ast_stream_topology *topology,
        enum ast_media_type type)
index 3bab67c2cf4c771ab93fb8d9d14b46bd599e395f..7eecf373babbb0647c0e06df968e2c91e2b4df34 100644 (file)
@@ -1773,6 +1773,63 @@ done:
        return res;
 }
 
+AST_TEST_DEFINE(format_cap_from_stream_topology)
+{
+       RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_format_cap *, stream_caps, NULL, ao2_cleanup);
+       struct ast_stream_topology *topology;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "format_cap_from_stream_topology";
+               info->category = "/main/stream/";
+               info->summary = "stream topology to format capabilities conversion test";
+               info->description =
+                       "Test that converting a stream topology to format capabilities results in expected formats";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+       if (!caps) {
+               ast_test_status_update(test, "Could not allocate an empty format capabilities structure\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (ast_format_cap_append(caps, ast_format_ulaw, 0)) {
+               ast_test_status_update(test, "Failed to append a ulaw format to capabilities for channel nativeformats\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (ast_format_cap_append(caps, ast_format_h264, 0)) {
+               ast_test_status_update(test, "Failed to append an h264 format to capabilities for channel nativeformats\n");
+               return AST_TEST_FAIL;
+       }
+
+       topology = ast_stream_topology_create_from_format_cap(caps);
+       if (!topology) {
+               ast_test_status_update(test, "Failed to create a stream topology from format capabilities of ulaw and h264\n");
+               return AST_TEST_FAIL;
+       }
+
+       stream_caps = ast_format_cap_from_stream_topology(topology);
+       if (!stream_caps) {
+               ast_test_status_update(test, "Failed to create a format capabilities from a stream topology\n");
+               ast_stream_topology_free(topology);
+               return AST_TEST_FAIL;
+       }
+
+       ast_stream_topology_free(topology);
+
+       if (!ast_format_cap_identical(caps, stream_caps)) {
+               ast_test_status_update(test, "Converting format capabilities into topology and back resulted in different formats\n");
+               return AST_TEST_FAIL;
+       }
+
+       return AST_TEST_PASS;
+}
+
 static int unload_module(void)
 {
        AST_TEST_UNREGISTER(stream_create);
@@ -1797,6 +1854,7 @@ static int unload_module(void)
        AST_TEST_UNREGISTER(stream_topology_change_request_from_channel_non_multistream);
        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);
        return 0;
 }
 
@@ -1823,6 +1881,7 @@ static int load_module(void)
        AST_TEST_REGISTER(stream_topology_change_request_from_channel_non_multistream);
        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);
        return AST_MODULE_LOAD_SUCCESS;
 }