diff options
-rw-r--r-- | apps/app_dial.c | 17 | ||||
-rw-r--r-- | include/asterisk/channel.h | 41 | ||||
-rw-r--r-- | include/asterisk/stream.h | 16 | ||||
-rw-r--r-- | main/channel.c | 106 | ||||
-rw-r--r-- | main/stream.c | 26 | ||||
-rw-r--r-- | tests/test_stream.c | 59 |
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; } |