Massively clean up app_queue.

This essentially makes app_queue usable again. From reviewboard:

* Reporting of transfers and call completion is done by creating stasis 
  subscriptions and listening for specific events in order to determine
  when the call is finished (either via a transfer or hangup).
* Dial end messages have been added where they were previously missing.
* Queue stats are properly being updated again once calls have finished.
* AgentComplete stasis messages and AMI events are now occurring again.
* Mixmonitor starting has been factored into its own function and uses the
  Mixmonitor API now instead of using ast_pbx_run()

In addition to the changes in app_queue, there are several supplementary changes as well:

* Queue logging now differentiates between attended and blind transfers. A
  note about this is in the CHANGES file.
* Local channel optimization events now report more information. This
  includes which of the two local channels involved is the destination of
  the optimization, the channel that is replacing the destination local channel,
  and an identifier so that begin and end events can be matched to each other.
  The end events are now sent whether the optimization was successful or not and
  includes an indicator of whether the optimization was successful.
* Changes were made to features and bridging_basic so that additional flags may
  be set on a bridge. This is necessary because the queue requires that its
  bridge only allows move-swap local channel optimizations into the bridge.

(closes issue ASTERISK-21517)
Reported by Matt Jordan

(closes issue ASTERISK-21943)
Reported by Matt Jordan

Review: https://reviewboard.asterisk.org/r/2694



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@397451 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Mark Michelson 12 years ago
parent 8049bf94f7
commit 00baddb906

@ -98,6 +98,14 @@ Queue
AgentConnect, AgentComplete, AgentDump, and AgentRingNoAnswer will always be
sent. The "Variable" fields will also no longer exist on the Agent* events.
* The queue log now differentiates between blind and attended transfers. A
blind transfer will result in a BLINDTRANSFER message with the destination
context and extension. An attended transfer will result in an
ATTENDEDTRANSFER message. This message will indicate the method by which
the attended transfer was completed: "BRIDGE" for a bridge merge, "APP"
for running an application on a bridge or channel, or "LINK" for linking
two bridges together with local channels.
* Queues now support a hint for member paused state. The hint uses the form
'Queue:{queue_name}_pause_{member_name}', where {queue_name} and {member_name}
are the name of the queue and the name of the member to subscribe to,

File diff suppressed because it is too large Load Diff

@ -1284,6 +1284,22 @@ struct stasis_message_type *ast_mwi_state_type(void);
*/
struct stasis_message_type *ast_mwi_vm_app_type(void);
/*!
* \brief Get the \ref stasis topic for queue messages
* \retval The topic structure for queue messages
* \retval NULL if it has not been allocated
* \since 12
*/
struct stasis_topic *ast_queue_topic_all(void);
/*!
* \brief Get the \ref stasis topic for queue messages for a particular queue name
* \param queuename The name for which to get the topic
* \retval The topic structure for queue messages for a given name
* \retval NULL if it failed to be found or allocated
* \since 12
*/
struct stasis_topic *ast_queue_topic(const char *queuename);
/*! @} */
/*!

@ -33,6 +33,7 @@
extern "C" {
#endif
#define AST_TRANSFERER_ROLE_NAME "transferer"
/* ------------------------------------------------------------------- */
/*!
@ -126,6 +127,17 @@ extern struct ast_bridge_methods ast_bridge_basic_v_table;
*/
struct ast_bridge *ast_bridge_basic_new(void);
/*!
* \brief Set feature flags on a basic bridge
*
* Using this function instead of setting flags directly will
* ensure that after operations such as an attended transfer,
* the bridge will maintain the flags that were set on it.
*
* \params Flags to set on the bridge. These are added to the flags already set.
*/
void ast_bridge_basic_set_flags(struct ast_bridge *bridge, unsigned int flags);
/*! Initialize the basic bridge class for use by the system. */
void ast_bridging_init_basic(void);

@ -46,6 +46,11 @@ struct ast_callid;
struct ast_unreal_pvt;
enum ast_unreal_channel_indicator {
AST_UNREAL_OWNER,
AST_UNREAL_CHAN,
};
/*!
* \brief Callbacks that can be provided by concrete implementations of the unreal
* channel driver that will be called when events occur in the unreal layer
@ -55,8 +60,14 @@ struct ast_unreal_pvt_callbacks {
* \brief Called when an optimization attempt has started
* \note p is locked when this callback is called
* \param p The \ref ast_unreal_pvt object
* \param source The channel that is optimizing into an unreal_pvt channel's bridge.
* If NULL, the optimization is being accomplished via a bridge merge.
* \param dest Indicator of which channel's bridge in the unreal_pvt will survive the
* optimization
* \param id Unique identifier for this optimization operation.
*/
void (* const optimization_started)(struct ast_unreal_pvt *p);
void (* const optimization_started)(struct ast_unreal_pvt *p, struct ast_channel *source,
enum ast_unreal_channel_indicator dest, unsigned int id);
/*!
* \brief Called when an optimization attempt completed successfully
@ -64,8 +75,10 @@ struct ast_unreal_pvt_callbacks {
* \param p The \ref ast_unreal_pvt object
* \param success Non-zero if the optimization succeeded, zero if the optimization
* met with fatal and permanent error
* \param id Unique identifier for this optimization. Same as the one from the optimization_started
* call
*/
void (* const optimization_finished)(struct ast_unreal_pvt *p);
void (* const optimization_finished)(struct ast_unreal_pvt *p, int success, unsigned int id);
};
/*!

@ -42,6 +42,20 @@ enum {
/*! \brief Bridge a call, optionally allowing redirection */
int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config);
/*!
* \brief Bridge a call, and add additional flags to the bridge
*
* This does the same thing as \ref ast_bridge_call, except that once the bridge
* is created, the provided flags are set on the bridge. The provided flags are
* added to the bridge's flags; they will not clear any flags already set.
*
* \param chan The calling channel
* \param peer The called channel
* \param config Bridge configuration for the channels
* \param flags Additional flags to set on the created bridge
*/
int ast_bridge_call_with_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, unsigned int flags);
/*!
* \brief Add an arbitrary channel to a bridge
* \since 12.0.0

@ -85,12 +85,15 @@ struct zombie {
static AST_LIST_HEAD_STATIC(zombies, zombie);
/*
* @{ \brief Define \ref stasis topic objects for MWI
* @{ \brief Define \ref stasis topic objects
*/
static struct stasis_topic *mwi_topic_all;
static struct stasis_cache *mwi_state_cache;
static struct stasis_caching_topic *mwi_topic_cached;
static struct stasis_topic_pool *mwi_topic_pool;
static struct stasis_topic *queue_topic_all;
static struct stasis_topic_pool *queue_topic_pool;
/* @} */
/*
@ -2978,8 +2981,22 @@ struct stasis_message *ast_mwi_blob_create(struct ast_mwi_state *mwi_state,
return msg;
}
struct stasis_topic *ast_queue_topic_all(void)
{
return queue_topic_all;
}
struct stasis_topic *ast_queue_topic(const char *queuename)
{
return stasis_topic_pool_get_topic(queue_topic_pool, queuename);
}
static void app_cleanup(void)
{
ao2_cleanup(queue_topic_pool);
queue_topic_pool = NULL;
ao2_cleanup(queue_topic_all);
queue_topic_all = NULL;
ao2_cleanup(mwi_topic_pool);
mwi_topic_pool = NULL;
ao2_cleanup(mwi_topic_all);
@ -3015,7 +3032,14 @@ int app_init(void)
if (!mwi_topic_pool) {
return -1;
}
queue_topic_all = stasis_topic_create("stasis_queue_topic");
if (!queue_topic_all) {
return -1;
}
queue_topic_pool = stasis_topic_pool_create(queue_topic_all);
if (!queue_topic_pool) {
return -1;
}
return 0;
}

@ -72,6 +72,8 @@ static struct ao2_container *bridges;
static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
static unsigned int optimization_id;
/* Initial starting point for the bridge array of channels */
#define BRIDGE_ARRAY_START 128
@ -2435,22 +2437,26 @@ static int try_swap_optimize_out(struct ast_bridge *chan_bridge,
other = ast_bridge_channel_peer(src_bridge_channel);
if (other && other->state == BRIDGE_CHANNEL_STATE_WAIT) {
unsigned int id = ast_atomic_fetchadd_int((int *) &optimization_id, +1);
ast_verb(3, "Move-swap optimizing %s <-- %s.\n",
ast_channel_name(dst_bridge_channel->chan),
ast_channel_name(other->chan));
if (pvt && !ast_test_flag(pvt, AST_UNREAL_OPTIMIZE_BEGUN) && pvt->callbacks
&& pvt->callbacks->optimization_started) {
pvt->callbacks->optimization_started(pvt);
pvt->callbacks->optimization_started(pvt, other->chan,
dst_bridge_channel->chan == pvt->owner ? AST_UNREAL_OWNER : AST_UNREAL_CHAN,
id);
ast_set_flag(pvt, AST_UNREAL_OPTIMIZE_BEGUN);
}
other->swap = dst_bridge_channel->chan;
if (!bridge_do_move(dst_bridge, other, 1, 1)) {
ast_bridge_channel_leave_bridge(src_bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
res = -1;
if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) {
pvt->callbacks->optimization_finished(pvt);
}
}
if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) {
pvt->callbacks->optimization_finished(pvt, res == 1, id);
}
}
return res;
@ -2528,6 +2534,7 @@ static int try_merge_optimize_out(struct ast_bridge *chan_bridge,
chan_bridge_channel,
peer_bridge_channel,
};
unsigned int id;
switch (bridges_allow_merge_optimization(chan_bridge, peer_bridge, ARRAY_LEN(kick_me), &merge)) {
case MERGE_ALLOWED:
@ -2551,14 +2558,18 @@ static int try_merge_optimize_out(struct ast_bridge *chan_bridge,
ast_channel_name(chan_bridge_channel->chan),
ast_channel_name(peer_bridge_channel->chan));
id = ast_atomic_fetchadd_int((int *) &optimization_id, +1);
if (pvt && !ast_test_flag(pvt, AST_UNREAL_OPTIMIZE_BEGUN) && pvt->callbacks
&& pvt->callbacks->optimization_started) {
pvt->callbacks->optimization_started(pvt);
pvt->callbacks->optimization_started(pvt, NULL,
merge.dest == ast_channel_internal_bridge(pvt->owner) ? AST_UNREAL_OWNER : AST_UNREAL_CHAN,
id);
ast_set_flag(pvt, AST_UNREAL_OPTIMIZE_BEGUN);
}
bridge_do_merge(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me), 1);
if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) {
pvt->callbacks->optimization_finished(pvt);
pvt->callbacks->optimization_finished(pvt, 1, id);
}
return -1;

@ -51,7 +51,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
| AST_BRIDGE_FLAG_SMART)
#define TRANSFER_FLAGS AST_BRIDGE_FLAG_SMART
#define TRANSFERER_ROLE_NAME "transferer"
struct attended_transfer_properties;
@ -1494,7 +1493,7 @@ static void attended_transfer_properties_shutdown(struct attended_transfer_prope
}
if (props->transferer) {
ast_channel_remove_bridge_role(props->transferer, TRANSFERER_ROLE_NAME);
ast_channel_remove_bridge_role(props->transferer, AST_TRANSFERER_ROLE_NAME);
}
clear_stimulus_queue(props);
@ -2581,14 +2580,14 @@ static int bridge_personality_atxfer_push(struct ast_bridge *self, struct ast_br
const char *swap_dtmf;
struct bridge_basic_personality *personality = self->personality;
if (!ast_channel_has_role(bridge_channel->chan, TRANSFERER_ROLE_NAME)) {
if (!ast_channel_has_role(bridge_channel->chan, AST_TRANSFERER_ROLE_NAME)) {
return 0;
}
abort_dtmf = ast_channel_get_role_option(bridge_channel->chan, TRANSFERER_ROLE_NAME, "abort");
complete_dtmf = ast_channel_get_role_option(bridge_channel->chan, TRANSFERER_ROLE_NAME, "complete");
threeway_dtmf = ast_channel_get_role_option(bridge_channel->chan, TRANSFERER_ROLE_NAME, "threeway");
swap_dtmf = ast_channel_get_role_option(bridge_channel->chan, TRANSFERER_ROLE_NAME, "swap");
abort_dtmf = ast_channel_get_role_option(bridge_channel->chan, AST_TRANSFERER_ROLE_NAME, "abort");
complete_dtmf = ast_channel_get_role_option(bridge_channel->chan, AST_TRANSFERER_ROLE_NAME, "complete");
threeway_dtmf = ast_channel_get_role_option(bridge_channel->chan, AST_TRANSFERER_ROLE_NAME, "threeway");
swap_dtmf = ast_channel_get_role_option(bridge_channel->chan, AST_TRANSFERER_ROLE_NAME, "swap");
if (!ast_strlen_zero(abort_dtmf) && ast_bridge_dtmf_hook(bridge_channel->features,
abort_dtmf, atxfer_abort, personality->details[personality->current].pvt, NULL,
@ -2838,11 +2837,11 @@ static int add_transferer_role(struct ast_channel *chan, struct ast_bridge_featu
atxfer_swap = ast_strdupa(xfer_cfg->atxferswap);
}
return ast_channel_add_bridge_role(chan, TRANSFERER_ROLE_NAME) ||
ast_channel_set_bridge_role_option(chan, TRANSFERER_ROLE_NAME, "abort", atxfer_abort) ||
ast_channel_set_bridge_role_option(chan, TRANSFERER_ROLE_NAME, "complete", atxfer_complete) ||
ast_channel_set_bridge_role_option(chan, TRANSFERER_ROLE_NAME, "threeway", atxfer_threeway) ||
ast_channel_set_bridge_role_option(chan, TRANSFERER_ROLE_NAME, "swap", atxfer_swap);
return ast_channel_add_bridge_role(chan, AST_TRANSFERER_ROLE_NAME) ||
ast_channel_set_bridge_role_option(chan, AST_TRANSFERER_ROLE_NAME, "abort", atxfer_abort) ||
ast_channel_set_bridge_role_option(chan, AST_TRANSFERER_ROLE_NAME, "complete", atxfer_complete) ||
ast_channel_set_bridge_role_option(chan, AST_TRANSFERER_ROLE_NAME, "threeway", atxfer_threeway) ||
ast_channel_set_bridge_role_option(chan, AST_TRANSFERER_ROLE_NAME, "swap", atxfer_swap);
}
/*!
@ -3243,6 +3242,15 @@ struct ast_bridge *ast_bridge_basic_new(void)
return bridge;
}
void ast_bridge_basic_set_flags(struct ast_bridge *bridge, unsigned int flags)
{
SCOPED_LOCK(lock, bridge, ast_bridge_lock, ast_bridge_unlock);
struct bridge_basic_personality *personality = bridge->personality;
personality->details[personality->current].bridge_flags |= flags;
ast_set_flag(&bridge->feature_flags, flags);
}
void ast_bridging_init_basic(void)
{
/* Setup bridge basic subclass v_table. */

@ -96,6 +96,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<syntax>
<channel_snapshot prefix="LocalOne"/>
<channel_snapshot prefix="LocalTwo"/>
<channel_snapshot prefix="Source"/>
<parameter name="DestUniqueId">
<para>The unique ID of the bridge into which the local channel is optimizing.</para>
</parameter>
<parameter name="Id">
<para>Identification for the optimization operation.</para>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">LocalOptimizationEnd</ref>
@ -110,6 +117,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<syntax>
<channel_snapshot prefix="LocalOne"/>
<channel_snapshot prefix="LocalTwo"/>
<parameter name="Success">
<para>Indicates whether the local optimization succeeded.</para>
</parameter>
<parameter name="Id">
<para>Identification for the optimization operation. Matches the <replaceable>Id</replaceable>
from a previous <literal>LocalOptimizationBegin</literal></para>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">LocalOptimizationBegin</ref>
@ -127,8 +141,9 @@ static struct ast_channel *local_request(const char *type, struct ast_format_cap
static int local_call(struct ast_channel *ast, const char *dest, int timeout);
static int local_hangup(struct ast_channel *ast);
static int local_devicestate(const char *data);
static void local_optimization_started_cb(struct ast_unreal_pvt *base);
static void local_optimization_finished_cb(struct ast_unreal_pvt *base);
static void local_optimization_started_cb(struct ast_unreal_pvt *base, struct ast_channel *source,
enum ast_unreal_channel_indicator dest, unsigned int id);
static void local_optimization_finished_cb(struct ast_unreal_pvt *base, int success, unsigned int id);
static struct ast_manager_event_blob *local_message_to_ami(struct stasis_message *msg);
@ -306,58 +321,97 @@ static int local_devicestate(const char *data)
return res;
}
static void publish_local_optimization(struct local_pvt *p, int complete)
static struct ast_multi_channel_blob *local_channel_optimization_blob(struct local_pvt *p,
struct ast_json *json_object)
{
RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
RAII_VAR(struct ast_json *, blob, ast_json_null(), ast_json_unref);
struct ast_multi_channel_blob *payload;
RAII_VAR(struct ast_channel_snapshot *, local_one_snapshot, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel_snapshot *, local_two_snapshot, NULL, ao2_cleanup);
if (!blob) {
return;
}
local_one_snapshot = ast_channel_snapshot_create(p->base.owner);
if (!local_one_snapshot) {
return;
return NULL;
}
local_two_snapshot = ast_channel_snapshot_create(p->base.chan);
if (!local_two_snapshot) {
return;
return NULL;
}
payload = ast_multi_channel_blob_create(blob);
payload = ast_multi_channel_blob_create(json_object);
if (!payload) {
return;
return NULL;
}
ast_multi_channel_blob_add_channel(payload, "1", local_one_snapshot);
ast_multi_channel_blob_add_channel(payload, "2", local_two_snapshot);
msg = stasis_message_create(
complete ? ast_local_optimization_end_type() : ast_local_optimization_begin_type(),
payload);
if (!msg) {
return;
}
stasis_publish(ast_channel_topic(p->base.owner), msg);
return payload;
}
/*! \brief Callback for \ref ast_unreal_pvt_callbacks \ref optimization_started_cb */
static void local_optimization_started_cb(struct ast_unreal_pvt *base)
static void local_optimization_started_cb(struct ast_unreal_pvt *base, struct ast_channel *source,
enum ast_unreal_channel_indicator dest, unsigned int id)
{
RAII_VAR(struct ast_json *, json_object, ast_json_null(), ast_json_unref);
RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
struct local_pvt *p = (struct local_pvt *)base;
publish_local_optimization(p, 0);
json_object = ast_json_pack("{s: i, s: i}",
"dest", dest, "id", id);
if (!json_object) {
return;
}
payload = local_channel_optimization_blob(p, json_object);
if (!payload) {
return;
}
if (source) {
RAII_VAR(struct ast_channel_snapshot *, source_snapshot, NULL, ao2_cleanup);
source_snapshot = ast_channel_snapshot_create(source);
if (!source_snapshot) {
return;
}
ast_multi_channel_blob_add_channel(payload, "source", source_snapshot);
}
msg = stasis_message_create(ast_local_optimization_begin_type(), payload);
if (!msg) {
return;
}
stasis_publish(ast_channel_topic(p->base.owner), msg);
}
/*! \brief Callback for \ref ast_unreal_pvt_callbacks \ref optimization_finished_cb */
static void local_optimization_finished_cb(struct ast_unreal_pvt *base)
static void local_optimization_finished_cb(struct ast_unreal_pvt *base, int success, unsigned int id)
{
RAII_VAR(struct ast_json *, json_object, ast_json_null(), ast_json_unref);
RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
struct local_pvt *p = (struct local_pvt *)base;
publish_local_optimization(p, 1);
json_object = ast_json_pack("{s: i, s: i}", "success", success, "id", id);
if (!json_object) {
return;
}
payload = local_channel_optimization_blob(p, json_object);
if (!payload) {
return;
}
msg = stasis_message_create(ast_local_optimization_end_type(), payload);
if (!msg) {
return;
}
stasis_publish(ast_channel_topic(p->base.owner), msg);
}
static struct ast_manager_event_blob *local_message_to_ami(struct stasis_message *message)
@ -384,9 +438,31 @@ static struct ast_manager_event_blob *local_message_to_ami(struct stasis_message
}
if (stasis_message_type(message) == ast_local_optimization_begin_type()) {
struct ast_channel_snapshot *source_snapshot;
RAII_VAR(struct ast_str *, source_str, NULL, ast_free);
const char *dest_uniqueid;
source_snapshot = ast_multi_channel_blob_get_channel(obj, "source");
if (source_snapshot) {
source_str = ast_manager_build_channel_state_string_prefix(source_snapshot, "Source");
if (!source_str) {
return NULL;
}
}
dest_uniqueid = ast_json_object_get(blob, "dest") == AST_UNREAL_OWNER ?
local_snapshot_one->uniqueid : local_snapshot_two->uniqueid;
event = "LocalOptimizationBegin";
if (source_str) {
ast_str_append(&event_buffer, 0, "%s", ast_str_buffer(source_str));
}
ast_str_append(&event_buffer, 0, "DestUniqueId: %s\r\n", dest_uniqueid);
ast_str_append(&event_buffer, 0, "Id: %u\r\n", (unsigned int) ast_json_integer_get(ast_json_object_get(blob, "id")));
} else if (stasis_message_type(message) == ast_local_optimization_end_type()) {
event = "LocalOptimizationEnd";
ast_str_append(&event_buffer, 0, "Success: %s\r\n", ast_json_integer_get(ast_json_object_get(blob, "success")) ? "Yes" : "No");
ast_str_append(&event_buffer, 0, "Id: %u\r\n", (unsigned int) ast_json_integer_get(ast_json_object_get(blob, "id")));
} else if (stasis_message_type(message) == ast_local_bridge_type()) {
event = "LocalBridge";
ast_str_append(&event_buffer, 0, "Context: %s\r\n", ast_json_string_get(ast_json_object_get(blob, "context")));

@ -639,19 +639,7 @@ static int pre_bridge_setup(struct ast_channel *chan, struct ast_channel *peer,
return 0;
}
/*!
* \brief bridge the call and set CDR
*
* \param chan The bridge considers this channel the caller.
* \param peer The bridge considers this channel the callee.
* \param config Configuration for this bridge.
*
* Set start time, check for two channels,check if monitor on
* check for feature activation, create new CDR
* \retval res on success.
* \retval -1 on failure to bridge.
*/
int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
int ast_bridge_call_with_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, unsigned int flags)
{
int res;
struct ast_bridge *bridge;
@ -684,6 +672,8 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a
return -1;
}
ast_bridge_basic_set_flags(bridge, flags);
/* Put peer into the bridge */
if (ast_bridge_impart(bridge, peer, NULL, peer_features, 1)) {
ast_bridge_destroy(bridge);
@ -717,6 +707,23 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a
return res;
}
/*!
* \brief bridge the call and set CDR
*
* \param chan The bridge considers this channel the caller.
* \param peer The bridge considers this channel the callee.
* \param config Configuration for this bridge.
*
* Set start time, check for two channels,check if monitor on
* check for feature activation, create new CDR
* \retval res on success.
* \retval -1 on failure to bridge.
*/
int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
{
return ast_bridge_call_with_flags(chan, peer, config, 0);
}
enum play_tone_action {
PLAYTONE_NONE = 0,
PLAYTONE_CHANNEL1 = (1 << 0),

Loading…
Cancel
Save