channel: Add ability to request an outgoing channel with stream topology.

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
pull/7/head
Joshua Colp 8 years ago
parent dc6654d969
commit 2b22c3c84b

@ -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 */

@ -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,

@ -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
*

@ -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);
}
/*!

@ -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)

@ -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;
}

Loading…
Cancel
Save