mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-03 11:25:35 +00:00
Merge "channel: Add ability to request an outgoing channel with stream topology."
This commit is contained in:
@@ -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 */
|
||||
|
@@ -204,6 +204,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 {
|
||||
@@ -631,6 +633,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 */
|
||||
|
||||
@@ -1394,6 +1416,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
|
||||
*
|
||||
|
118
main/channel.c
118
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);
|
||||
|
||||
if (!(c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause))) {
|
||||
c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause);
|
||||
|
||||
if (!c) {
|
||||
ao2_ref(joint_cap, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (requestor) {
|
||||
ast_callid callid;
|
||||
|
||||
ast_channel_lock_both(c, (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);
|
||||
}
|
||||
|
||||
ast_channel_unlock(c);
|
||||
ast_channel_unlock((struct ast_channel *) requestor);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* no need to generate a Newchannel event here; it is done in the channel_alloc call */
|
||||
return c;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
|
||||
*cause = AST_CAUSE_NOSUCHDRIVER;
|
||||
AST_RWLIST_UNLOCK(&backends);
|
||||
if (requestor) {
|
||||
ast_callid callid;
|
||||
|
||||
return NULL;
|
||||
ast_channel_lock_both(c, (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);
|
||||
}
|
||||
|
||||
ast_channel_unlock(c);
|
||||
ast_channel_unlock((struct ast_channel *) requestor);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* no need to generate a Newchannel event here; it is done in the channel_alloc call */
|
||||
return c;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user