]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
ACN: Changes specific to the core
authorGeorge Joseph <gjoseph@digium.com>
Mon, 20 Jul 2020 19:39:14 +0000 (13:39 -0600)
committerJoshua Colp <jcolp@sangoma.com>
Tue, 18 Aug 2020 10:26:24 +0000 (05:26 -0500)
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
include/asterisk/channel.h
include/asterisk/frame.h
main/channel.c
main/features.c
main/frame.c
main/stream.c

index 95f36d7ab26eeb19ef99028d631fb8a4daa8964a..9e1148753c50d23a7c7bca458f8e91b73275556a 100644 (file)
@@ -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);
        }
index cc90c8304a6a03778ce37100fe76253fef530a09..baefeddaed7db70f5f4f33e94ab08b8577754197 100644 (file)
@@ -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 */
index c8359ec525ae99afaa9e47ad0f47d29f2b0fa349..f5a5f2cec5180c83ef1df26263ad9d6364e863d2 100644 (file)
@@ -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;
 };
index 8dd008dd15ba2e60e15c4578a9c75faf634d60bb..c26089a24551ced33b24b3c6747034f173ae9d83 100644 (file)
@@ -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;
index 51cc3ed98d67af883f93ce80a17dee7565f2321c..9810866466fb2a18ca02d58dfdc32621ec0265e0 100644 (file)
@@ -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);
index 4eeb3b60d5be0c9ad0906229cd94cb27443dab27..3a5ee91bd4e1bab59f986ef2bdc0bb50047c521e 100644 (file)
@@ -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;
index a21177d10c52660d79bcc86420cd1da78684d404..01b07cad1904070dbc02cd13be54f4de416250a1 100644 (file)
@@ -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);
                        }
                }