summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/app_dial.c17
-rw-r--r--include/asterisk/channel.h41
-rw-r--r--include/asterisk/stream.h16
-rw-r--r--main/channel.c106
-rw-r--r--main/stream.c26
-rw-r--r--tests/test_stream.c59
6 files changed, 224 insertions, 41 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c
index c8fcf4696..79e2a9b0b 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -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 */
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 32c9c7f67..70856a96f 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -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,
diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h
index 1becae25a..924bfb65c 100644
--- a/include/asterisk/stream.h
+++ b/include/asterisk/stream.h
@@ -375,6 +375,22 @@ 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
*
* \param topology The topology of streams
diff --git a/main/channel.c b/main/channel.c
index 31f363938..e37d66525 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -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);
}
/*!
diff --git a/main/stream.c b/main/stream.c
index 9d36dbf25..cf2633e1b 100644
--- a/main/stream.c
+++ b/main/stream.c
@@ -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)
diff --git a/tests/test_stream.c b/tests/test_stream.c
index 3bab67c2c..7eecf373b 100644
--- a/tests/test_stream.c
+++ b/tests/test_stream.c
@@ -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;
}