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
This commit is contained in:
parent
543f936147
commit
6faf76308d
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue