From 647c53c41fa36ab8e5b3e8824952aea01c76e37e Mon Sep 17 00:00:00 2001 From: George Joseph <gjoseph@digium.com> Date: Mon, 20 Jul 2020 13:39:14 -0600 Subject: [PATCH] ACN: Changes specific to the core Allow passing a topology from the called channel back to the calling channel. * Added a new function ast_queue_answer() that accepts a stream topology and queues an ANSWER CONTROL frame with it as the data. This allows the called channel to indicate its resolved topology. * Added a new virtual function to the channel tech structure answer_with_stream_topology() that allows the calling channel to receive the called channel's topology. Added ast_raw_answer_with_stream_topology() that invokes that virtual function. * Modified app_dial.c and features.c to grab the topology from the ANSWER frame queued by the answering channel and send it to the calling channel with ast_raw_answer_with_stream_topology(). * Modified frame.c to automatically cleanup the reference to the topology on ANSWER frames. Added a few debugging messages to stream.c. Change-Id: I0115d2ed68d6bae0f87e85abcf16c771bdaf992c --- apps/app_dial.c | 32 ++++++++++++++++-- include/asterisk/channel.h | 67 ++++++++++++++++++++++++++++++++++++++ include/asterisk/frame.h | 8 +++-- main/channel.c | 24 ++++++++++++-- main/features.c | 8 ++++- main/frame.c | 9 +++++ main/stream.c | 17 ++++++++-- 7 files changed, 155 insertions(+), 10 deletions(-) diff --git a/apps/app_dial.c b/apps/app_dial.c index 95f36d7ab2..9e1148753c 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -1204,7 +1204,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct privacy_args *pa, const struct cause_args *num_in, int *result, char *dtmf_progress, const int ignore_cc, - struct ast_party_id *forced_clid, struct ast_party_id *stored_clid) + struct ast_party_id *forced_clid, struct ast_party_id *stored_clid, + struct ast_bridge_config *config) { struct cause_args num = *num_in; int prestart = num.busy + num.congestion + num.nochan; @@ -1418,6 +1419,18 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, } } peer = c; + /* Answer can optionally include a topology */ + if (f->subclass.topology) { + /* + * We need to bump the refcount on the topology to prevent it + * from being cleaned up when the frame is cleaned up. + */ + config->answer_topology = ao2_bump(f->subclass.topology); + ast_trace(2, "%s Found topology in frame: %p %p %s\n", + ast_channel_name(peer), f, config->answer_topology, + ast_str_tmp(256, ast_stream_topology_to_str(config->answer_topology, &STR_TMP))); + } + /* Inform everyone else that they've been canceled. * The dial end event for the peer will be sent out after * other Dial options have been handled. @@ -2217,7 +2230,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast struct dial_head out_chans = AST_LIST_HEAD_NOLOCK_INIT_VALUE; /* list of destinations */ struct chanlist *outgoing; struct chanlist *tmp; - struct ast_channel *peer; + struct ast_channel *peer = NULL; int to; /* timeout */ struct cause_args num = { chan, 0, 0, 0 }; int cause; @@ -2838,7 +2851,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast } peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result, - dtmf_progress, ignore_cc, &forced_clid, &stored_clid); + dtmf_progress, ignore_cc, &forced_clid, &stored_clid, &config); if (!peer) { if (result) { @@ -3267,6 +3280,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0); } setup_peer_after_bridge_goto(chan, peer, &opts, opt_args); + res = ast_bridge_call(chan, peer, &config); } } @@ -3304,6 +3318,18 @@ out: } done: + if (config.answer_topology) { + ast_trace(2, "%s Cleaning up topology: %p %s\n", + peer ? ast_channel_name(peer) : "<no channel>", &config.answer_topology, + ast_str_tmp(256, ast_stream_topology_to_str(config.answer_topology, &STR_TMP))); + + /* + * At this point, the channel driver that answered should have bumped the + * topology refcount for itself. Here we're cleaning up the reference we added + * in wait_for_answer(). + */ + ast_stream_topology_free(config.answer_topology); + } if (config.warning_sound) { ast_free((char *)config.warning_sound); } diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index cc90c8304a..baefeddaed 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -707,6 +707,19 @@ struct ast_channel_tech { /*! \brief Answer the channel */ int (* const answer)(struct ast_channel *chan); + /*! + * \brief Answer the channel with topology + * \since 18.0.0 + * + * \param chan The channel to answer + * \param topology The topology to use, probably the peer's. + * + * \note The topology may be NULL when the peer doesn't support streams + * or, in the case where transcoding is in effect, when this channel should use + * its existing topology. + */ + int (* const answer_with_stream_topology)(struct ast_channel *chan, struct ast_stream_topology *topology); + /*! * \brief Read a frame (or chain of frames from the same stream), in standard format (see frame.h) * @@ -1081,6 +1094,10 @@ struct ast_bridge_config { * exist when the end_bridge_callback is called, then it needs to be fixed up properly */ void (*end_bridge_callback_data_fixup)(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator); + /*! If the bridge answers the channel this topology should be passed to the channel + * and used if the channel supports the answer_with_stream_topology callback. + */ + struct ast_stream_topology *answer_topology; }; struct chanmon; @@ -1378,6 +1395,17 @@ int ast_queue_control(struct ast_channel *chan, enum ast_control_frame_type cont int ast_queue_control_data(struct ast_channel *chan, enum ast_control_frame_type control, const void *data, size_t datalen); +/*! + * \brief Queue an ANSWER control frame with topology + * + * \param chan channel to queue frame onto + * \param topology topology to be passed through the core to the peer channel + * + * \retval 0 success + * \retval non-zero failure + */ +int ast_queue_answer(struct ast_channel *chan, const struct ast_stream_topology *topology); + /*! * \brief Change channel name * @@ -1801,6 +1829,31 @@ int ast_auto_answer(struct ast_channel *chan); */ int ast_raw_answer(struct ast_channel *chan); +/*! + * \brief Answer a channel passing in a stream topology + * \since 18.0.0 + * + * \param chan channel to answer + * \param topology the peer's stream topology + * + * This function answers a channel and handles all necessary call + * setup functions. + * + * \note The channel passed does not need to be locked, but is locked + * by the function when needed. + * + * \note Unlike ast_answer(), this function will not wait for media + * flow to begin. The caller should be careful before sending media + * to the channel before incoming media arrives, as the outgoing + * media may be lost. + * + * \note The topology is usually that of the peer channel and may be NULL. + * + * \retval 0 on success + * \retval non-zero on failure + */ +int ast_raw_answer_with_stream_topology(struct ast_channel *chan, struct ast_stream_topology *topology); + /*! * \brief Answer a channel, with a selectable delay before returning * @@ -5054,4 +5107,18 @@ int ast_channel_stream_topology_changed_externally(struct ast_channel *chan); */ void *ast_channel_get_stream_topology_change_source(struct ast_channel *chan); +/*! + * \brief Checks if a channel's technology implements a particular callback function + * \since 18.0.0 + * + * \param chan The channel + * \param function The function to look for + * + * \retval 1 if the channel has a technology set and it implements the function + * \retval 0 if the channel doesn't have a technology set or it doesn't implement the function + */ +#define ast_channel_has_tech_function(chan, function) \ + (ast_channel_tech(chan) ? ast_channel_tech(chan)->function != NULL : 0) + + #endif /* _ASTERISK_CHANNEL_H */ diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index c8359ec525..f5a5f2cec5 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -147,8 +147,12 @@ enum { struct ast_frame_subclass { /*! A frame specific code */ int integer; - /*! The asterisk media format */ - struct ast_format *format; + union { + /*! The asterisk media format */ + struct ast_format *format; + /*! The asterisk stream topology */ + struct ast_stream_topology *topology; + }; /*! For video formats, an indication that a frame ended */ unsigned int frame_ending; }; diff --git a/main/channel.c b/main/channel.c index 8dd008dd15..c26089a245 100644 --- a/main/channel.c +++ b/main/channel.c @@ -1238,6 +1238,17 @@ int ast_queue_control_data(struct ast_channel *chan, enum ast_control_frame_type return ast_queue_frame(chan, &f); } +/*! \brief Queue an ANSWER control frame with topology */ +int ast_queue_answer(struct ast_channel *chan, const struct ast_stream_topology *topology) +{ + struct ast_frame f = { + AST_FRAME_CONTROL, + .subclass.integer = AST_CONTROL_ANSWER, + .subclass.topology = (struct ast_stream_topology *)topology, + }; + return ast_queue_frame(chan, &f); +} + /*! \brief Set defer DTMF flag on channel */ int ast_channel_defer_dtmf(struct ast_channel *chan) { @@ -2619,7 +2630,8 @@ static void set_channel_answer_time(struct ast_channel *chan) } } -int ast_raw_answer(struct ast_channel *chan) + +int ast_raw_answer_with_stream_topology(struct ast_channel *chan, struct ast_stream_topology *topology) { int res = 0; SCOPE_TRACE(1, "%s\n", ast_channel_name(chan)); @@ -2650,7 +2662,10 @@ int ast_raw_answer(struct ast_channel *chan) case AST_STATE_RINGING: case AST_STATE_RING: ast_channel_lock(chan); - if (ast_channel_tech(chan)->answer) { + if (ast_channel_tech(chan)->answer_with_stream_topology) { + res = ast_channel_tech(chan)->answer_with_stream_topology(chan, topology); + + } else if (ast_channel_tech(chan)->answer) { res = ast_channel_tech(chan)->answer(chan); } ast_setstate(chan, AST_STATE_UP); @@ -2667,6 +2682,11 @@ int ast_raw_answer(struct ast_channel *chan) return res; } +int ast_raw_answer(struct ast_channel *chan) +{ + return ast_raw_answer_with_stream_topology(chan, NULL); +} + int __ast_answer(struct ast_channel *chan, unsigned int delay) { int res = 0; diff --git a/main/features.c b/main/features.c index 51cc3ed98d..9810866466 100644 --- a/main/features.c +++ b/main/features.c @@ -76,6 +76,7 @@ #include "asterisk/stasis_channels.h" #include "asterisk/features_config.h" #include "asterisk/max_forwards.h" +#include "asterisk/stream.h" /*** DOCUMENTATION <application name="Bridge" language="en_US"> @@ -558,12 +559,17 @@ static int pre_bridge_setup(struct ast_channel *chan, struct ast_channel *peer, set_config_flags(chan, config); /* Answer if need be */ + + res = 0; + if (ast_channel_state(chan) != AST_STATE_UP) { - if (ast_raw_answer(chan)) { + res = ast_raw_answer_with_stream_topology(chan, config->answer_topology); + if (res != 0) { return -1; } } + #ifdef FOR_DEBUG /* show the two channels and cdrs involved in the bridge for debug & devel purposes */ ast_channel_log("Pre-bridge CHAN Channel info", chan); diff --git a/main/frame.c b/main/frame.c index 4eeb3b60d5..3a5ee91bd4 100644 --- a/main/frame.c +++ b/main/frame.c @@ -138,6 +138,8 @@ static void __frame_free(struct ast_frame *fr, int cache) || fr->frametype == AST_FRAME_VIDEO || fr->frametype == AST_FRAME_IMAGE) { ao2_cleanup(fr->subclass.format); + } else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass.integer == AST_CONTROL_ANSWER) { + ao2_cleanup(fr->subclass.topology); } AST_LIST_INSERT_HEAD(&frames->list, fr, frame_list); @@ -160,6 +162,8 @@ static void __frame_free(struct ast_frame *fr, int cache) || fr->frametype == AST_FRAME_VIDEO || fr->frametype == AST_FRAME_IMAGE) { ao2_cleanup(fr->subclass.format); + } else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass.integer == AST_CONTROL_ANSWER) { + ao2_cleanup(fr->subclass.topology); } ast_free(fr); @@ -218,6 +222,8 @@ struct ast_frame *__ast_frisolate(struct ast_frame *fr, const char *file, int li if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) || (fr->frametype == AST_FRAME_IMAGE)) { ao2_bump(out->subclass.format); + } else if (fr->frametype == AST_FRAME_VOICE && fr->subclass.integer == AST_CONTROL_ANSWER) { + ao2_bump(out->subclass.topology); } out->datalen = fr->datalen; out->samples = fr->samples; @@ -348,7 +354,10 @@ struct ast_frame *__ast_frdup(const struct ast_frame *f, const char *file, int l if ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_VIDEO) || (f->frametype == AST_FRAME_IMAGE)) { ao2_bump(out->subclass.format); + } else if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_ANSWER) { + ao2_bump(out->subclass.topology); } + out->datalen = f->datalen; out->samples = f->samples; out->delivery = f->delivery; diff --git a/main/stream.c b/main/stream.c index a21177d10c..01b07cad19 100644 --- a/main/stream.c +++ b/main/stream.c @@ -602,6 +602,16 @@ struct ast_stream *ast_stream_create_resolved(struct ast_stream *pending_stream, ast_format_cap_append(joint_caps, single, 0); ao2_ref(single, -1); } + } else { + if (error_message) { + ast_str_append(error_message, 0, "No common formats available for media type '%s' ", + ast_codec_media_type2str(pending_stream->type)); + ast_format_cap_append_names(preferred_caps, error_message); + ast_str_append(error_message, 0, "<>"); + ast_format_cap_append_names(nonpreferred_caps, error_message); + ast_str_append(error_message, 0, " with prefs: "); + ast_stream_codec_prefs_to_str(prefs, error_message); + } } joint_stream = ast_stream_clone(pending_stream, NULL); @@ -613,7 +623,7 @@ struct ast_stream *ast_stream_create_resolved(struct ast_stream *pending_stream, /* ref to joint_caps will be transferred to the stream */ ast_stream_set_formats(joint_stream, joint_caps); - if (TRACE_ATLEAST(1)) { + if (TRACE_ATLEAST(3)) { struct ast_str *buf = ast_str_create((AST_FORMAT_CAP_NAMES_LEN * 3) + AST_STREAM_MAX_CODEC_PREFS_LENGTH); if (buf) { ast_str_set(&buf, 0, "Resolved '%s' stream ", ast_codec_media_type2str(pending_stream->type)); @@ -1040,7 +1050,10 @@ struct ast_stream_topology *ast_stream_topology_create_resolved( ast_stream_set_state(joint_stream, AST_STREAM_STATE_REMOVED); } else { joint_stream = ast_stream_create_resolved(pending_stream, configured_stream, prefs, error_message); - if (ast_stream_get_format_count(joint_stream) == 0) { + if (!joint_stream) { + ao2_cleanup(joint_topology); + return NULL; + } else if (ast_stream_get_format_count(joint_stream) == 0) { ast_stream_set_state(joint_stream, AST_STREAM_STATE_REMOVED); } } -- GitLab