Fixup hold/unhold with attended and blind transfers.

* DTMF attended and blind transfers have hold/unhold behavior restored.

* External attended and blind transfers unhold the transfered party when
the transfer is initiated.

* Made prohibit blind transferring a bridge marked as masquerade only.
(ConfBridge bridges)

* Made running an application or playing a file inside a bridge post the
hold/unhold messages if MOH is requested.

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


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@390289 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Richard Mudgett 12 years ago
parent a1494300c9
commit ccc8cc5346

@ -182,7 +182,8 @@ static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_c
const char *context; const char *context;
char *goto_on_blindxfr; char *goto_on_blindxfr;
/* BUGBUG the peer needs to be put on hold for the transfer. */ ast_bridge_channel_write_hold(bridge_channel, NULL);
ast_channel_lock(bridge_channel->chan); ast_channel_lock(bridge_channel->chan);
context = ast_strdupa(get_transfer_context(bridge_channel->chan, context = ast_strdupa(get_transfer_context(bridge_channel->chan,
blind_transfer ? blind_transfer->context : NULL)); blind_transfer ? blind_transfer->context : NULL));
@ -192,6 +193,7 @@ static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_c
/* Grab the extension to transfer to */ /* Grab the extension to transfer to */
if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) { if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
ast_bridge_channel_write_unhold(bridge_channel);
return 0; return 0;
} }
@ -264,9 +266,10 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
const char *context; const char *context;
enum atxfer_code transfer_code = ATXFER_INCOMPLETE; enum atxfer_code transfer_code = ATXFER_INCOMPLETE;
ast_bridge_channel_write_hold(bridge_channel, NULL);
bridge = ast_bridge_channel_merge_inhibit(bridge_channel, +1); bridge = ast_bridge_channel_merge_inhibit(bridge_channel, +1);
/* BUGBUG the peer needs to be put on hold for the transfer. */
ast_channel_lock(bridge_channel->chan); ast_channel_lock(bridge_channel->chan);
context = ast_strdupa(get_transfer_context(bridge_channel->chan, context = ast_strdupa(get_transfer_context(bridge_channel->chan,
attended_transfer ? attended_transfer->context : NULL)); attended_transfer ? attended_transfer->context : NULL));
@ -276,6 +279,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) { if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
ast_bridge_merge_inhibit(bridge, -1); ast_bridge_merge_inhibit(bridge, -1);
ao2_ref(bridge, -1); ao2_ref(bridge, -1);
ast_bridge_channel_write_unhold(bridge_channel);
return 0; return 0;
} }
@ -286,6 +290,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
ao2_ref(bridge, -1); ao2_ref(bridge, -1);
/* BUGBUG beeperr needs to be configurable from features.conf */ /* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE); ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
ast_bridge_channel_write_unhold(bridge_channel);
return 0; return 0;
} }
@ -313,6 +318,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
ao2_ref(bridge, -1); ao2_ref(bridge, -1);
/* BUGBUG beeperr needs to be configurable from features.conf */ /* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE); ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
ast_bridge_channel_write_unhold(bridge_channel);
return 0; return 0;
} }
@ -326,6 +332,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
ao2_ref(bridge, -1); ao2_ref(bridge, -1);
/* BUGBUG beeperr needs to be configurable from features.conf */ /* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE); ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
ast_bridge_channel_write_unhold(bridge_channel);
return 0; return 0;
} }
ast_bridge_merge_inhibit(attended_bridge, +1); ast_bridge_merge_inhibit(attended_bridge, +1);
@ -340,6 +347,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
ao2_ref(bridge, -1); ao2_ref(bridge, -1);
/* BUGBUG beeperr needs to be configurable from features.conf */ /* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE); ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
ast_bridge_channel_write_unhold(bridge_channel);
return 0; return 0;
} }
@ -384,6 +392,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
break; break;
case ATXFER_COMPLETE: case ATXFER_COMPLETE:
/* The peer takes our place in the bridge. */ /* The peer takes our place in the bridge. */
ast_bridge_channel_write_unhold(bridge_channel);
ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, bridge_channel->chan, NULL, 1); xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, bridge_channel->chan, NULL, 1);
break; break;
@ -394,6 +403,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
* Just impart the peer onto the bridge and have us return to it * Just impart the peer onto the bridge and have us return to it
* as normal. * as normal.
*/ */
ast_bridge_channel_write_unhold(bridge_channel);
xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, NULL, NULL, 1); xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, NULL, NULL, 1);
break; break;
case ATXFER_ABORT: case ATXFER_ABORT:
@ -407,6 +417,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
if (!ast_check_hangup_locked(bridge_channel->chan)) { if (!ast_check_hangup_locked(bridge_channel->chan)) {
ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE); ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
} }
ast_bridge_channel_write_unhold(bridge_channel);
} }
return 0; return 0;

@ -1109,6 +1109,27 @@ void ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_cha
*/ */
void ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen); void ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen);
/*!
* \brief Write a hold frame into the bridge.
* \since 12.0.0
*
* \param bridge_channel Which channel is putting the hold into the bridge.
* \param moh_class The suggested music class for the other end to use.
*
* \return Nothing
*/
void ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class);
/*!
* \brief Write an unhold frame into the bridge.
* \since 12.0.0
*
* \param bridge_channel Which channel is putting the hold into the bridge.
*
* \return Nothing
*/
void ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel);
/*! /*!
* \brief Run an application on the bridge channel. * \brief Run an application on the bridge channel.
* \since 12.0.0 * \since 12.0.0

@ -272,6 +272,19 @@ struct ast_json *ast_multi_channel_blob_get_json(struct ast_multi_channel_blob *
void ast_multi_channel_blob_add_channel(struct ast_multi_channel_blob *obj, void ast_multi_channel_blob_add_channel(struct ast_multi_channel_blob *obj,
const char *role, struct ast_channel_snapshot *snapshot); const char *role, struct ast_channel_snapshot *snapshot);
/*!
* \brief Publish a channel blob message.
* \since 12.0.0
*
* \param chan Channel publishing the blob.
* \param type Type of stasis message.
* \param blob The blob being published. (NULL if no blob)
*
* \return Nothing
*/
void ast_channel_publish_blob(struct ast_channel *chan, struct stasis_message_type *type,
struct ast_json *blob);
/*! /*!
* \since 12 * \since 12
* \brief Publish a \ref ast_channel_snapshot for a channel. * \brief Publish a \ref ast_channel_snapshot for a channel.

@ -43,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/bridging_basic.h" #include "asterisk/bridging_basic.h"
#include "asterisk/bridging_technology.h" #include "asterisk/bridging_technology.h"
#include "asterisk/stasis_bridging.h" #include "asterisk/stasis_bridging.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/app.h" #include "asterisk/app.h"
#include "asterisk/file.h" #include "asterisk/file.h"
#include "asterisk/module.h" #include "asterisk/module.h"
@ -714,6 +715,32 @@ void ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_cha
bridge_channel_write_frame(bridge_channel, &frame); bridge_channel_write_frame(bridge_channel, &frame);
} }
void ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class)
{
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
size_t datalen;
if (!ast_strlen_zero(moh_class)) {
datalen = strlen(moh_class) + 1;
blob = ast_json_pack("{s: s}",
"musicclass", moh_class);
} else {
moh_class = NULL;
datalen = 0;
}
ast_channel_publish_blob(bridge_channel->chan, ast_channel_hold_type(), blob);
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD, moh_class,
datalen);
}
void ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel)
{
ast_channel_publish_blob(bridge_channel->chan, ast_channel_unhold_type(), NULL);
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, NULL, 0);
}
static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args) static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args)
{ {
int res = 0; int res = 0;
@ -738,21 +765,14 @@ static int run_app_helper(struct ast_channel *chan, const char *app_name, const
void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
{ {
if (moh_class) { if (moh_class) {
if (ast_strlen_zero(moh_class)) { ast_bridge_channel_write_hold(bridge_channel, moh_class);
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
NULL, 0);
} else {
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
moh_class, strlen(moh_class) + 1);
}
} }
if (run_app_helper(bridge_channel->chan, app_name, S_OR(app_args, ""))) { if (run_app_helper(bridge_channel->chan, app_name, S_OR(app_args, ""))) {
/* Break the bridge if the app returns non-zero. */ /* Break the bridge if the app returns non-zero. */
bridge_handle_hangup(bridge_channel); bridge_handle_hangup(bridge_channel);
} }
if (moh_class) { if (moh_class) {
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, ast_bridge_channel_write_unhold(bridge_channel);
NULL, 0);
} }
} }
@ -821,13 +841,7 @@ void ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, con
void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
{ {
if (moh_class) { if (moh_class) {
if (ast_strlen_zero(moh_class)) { ast_bridge_channel_write_hold(bridge_channel, moh_class);
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
NULL, 0);
} else {
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
moh_class, strlen(moh_class) + 1);
}
} }
if (custom_play) { if (custom_play) {
custom_play(bridge_channel, playfile); custom_play(bridge_channel, playfile);
@ -835,8 +849,7 @@ void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_
ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE); ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
} }
if (moh_class) { if (moh_class) {
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, ast_bridge_channel_write_unhold(bridge_channel);
NULL, 0);
} }
/* /*
@ -5428,25 +5441,49 @@ static void set_blind_transfer_variables(struct ast_channel *transferer, struct
ao2_iterator_destroy(&iter); ao2_iterator_destroy(&iter);
} }
static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
{
struct ast_bridge *bridge;
ast_channel_lock(chan);
bridge = ast_channel_get_bridge(chan);
ast_channel_unlock(chan);
if (bridge
&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)) {
ao2_ref(bridge, -1);
bridge = NULL;
}
return bridge;
}
enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transferer, enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transferer,
const char *exten, const char *context, const char *exten, const char *context,
transfer_channel_cb new_channel_cb, void *user_data) transfer_channel_cb new_channel_cb, void *user_data)
{ {
RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup);
int do_bridge_transfer; int do_bridge_transfer;
int transfer_prohibited; int transfer_prohibited;
enum try_parking_result parking_result; enum try_parking_result parking_result;
bridge = acquire_bridge(transferer);
if (!bridge) {
return AST_BRIDGE_TRANSFER_INVALID;
}
ast_channel_lock(transferer); ast_channel_lock(transferer);
bridge = ast_channel_get_bridge(transferer); bridge_channel = ast_channel_get_bridge_channel(transferer);
ast_channel_unlock(transferer); ast_channel_unlock(transferer);
if (!bridge_channel) {
if (!bridge) {
return AST_BRIDGE_TRANSFER_INVALID; return AST_BRIDGE_TRANSFER_INVALID;
} }
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold(bridge_channel);
parking_result = try_parking(bridge, transferer, exten, context); parking_result = try_parking(bridge, transferer, exten, context);
switch (parking_result) { switch (parking_result) {
case PARKING_SUCCESS: case PARKING_SUCCESS:
@ -5501,26 +5538,6 @@ enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transfere
return AST_BRIDGE_TRANSFER_SUCCESS; return AST_BRIDGE_TRANSFER_SUCCESS;
} }
static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
{
struct ast_bridge *bridge;
ast_channel_lock(chan);
bridge = ast_channel_get_bridge(chan);
ast_channel_unlock(chan);
if (!bridge) {
return NULL;
}
if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)) {
ao2_ref(bridge, -1);
bridge = NULL;
}
return bridge;
}
/*! /*!
* \internal * \internal
* \brief Performs an attended transfer by moving a channel from one bridge to another * \brief Performs an attended transfer by moving a channel from one bridge to another
@ -5613,6 +5630,8 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
{ {
RAII_VAR(struct ast_bridge *, to_transferee_bridge, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge *, to_transferee_bridge, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge *, to_target_bridge, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge *, to_target_bridge, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, to_transferee_bridge_channel, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, to_target_bridge_channel, NULL, ao2_cleanup);
RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup);
struct ast_bridge *the_bridge; struct ast_bridge *the_bridge;
@ -5629,19 +5648,31 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
return AST_BRIDGE_TRANSFER_INVALID; return AST_BRIDGE_TRANSFER_INVALID;
} }
ast_channel_lock(to_transferee);
to_transferee_bridge_channel = ast_channel_get_bridge_channel(to_transferee);
ast_channel_unlock(to_transferee);
ast_channel_lock(to_transfer_target);
to_target_bridge_channel = ast_channel_get_bridge_channel(to_transfer_target);
ast_channel_unlock(to_transfer_target);
if (to_transferee_bridge_channel) {
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold(to_transferee_bridge_channel);
}
if (to_target_bridge_channel) {
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold(to_target_bridge_channel);
}
/* Let's get the easy one out of the way first */ /* Let's get the easy one out of the way first */
if (to_transferee_bridge && to_target_bridge) { if (to_transferee_bridge && to_target_bridge) {
RAII_VAR(struct ast_bridge_channel *, to_transferee_bridge_channel, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, to_target_bridge_channel, NULL, ao2_cleanup);
enum ast_transfer_result res; enum ast_transfer_result res;
ast_channel_lock(to_transferee); if (!to_transferee_bridge_channel || !to_target_bridge_channel) {
to_transferee_bridge_channel = ast_channel_get_bridge_channel(to_transferee); return AST_BRIDGE_TRANSFER_INVALID;
ast_channel_unlock(to_transferee); }
ast_channel_lock(to_transfer_target);
to_target_bridge_channel = ast_channel_get_bridge_channel(to_transfer_target);
ast_channel_unlock(to_transfer_target);
ast_bridge_lock_both(to_transferee_bridge, to_target_bridge); ast_bridge_lock_both(to_transferee_bridge, to_target_bridge);
res = two_bridge_attended_transfer(to_transferee, to_transferee_bridge_channel, res = two_bridge_attended_transfer(to_transferee, to_transferee_bridge_channel,

@ -1348,21 +1348,6 @@ int ast_queue_frame_head(struct ast_channel *chan, struct ast_frame *fin)
return __ast_queue_frame(chan, fin, 1, NULL); return __ast_queue_frame(chan, fin, 1, NULL);
} }
/*! \internal \brief Publish a channel blob message */
static void publish_channel_blob(struct ast_channel *chan,
struct stasis_message_type *type, struct ast_json *blob)
{
RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
if (!blob) {
blob = ast_json_null();
}
message = ast_channel_blob_create(chan, type, blob);
if (message) {
stasis_publish(ast_channel_topic(chan), message);
}
}
/*! \brief Queue a hangup frame for channel */ /*! \brief Queue a hangup frame for channel */
int ast_queue_hangup(struct ast_channel *chan) int ast_queue_hangup(struct ast_channel *chan)
{ {
@ -1372,7 +1357,7 @@ int ast_queue_hangup(struct ast_channel *chan)
/* Yeah, let's not change a lock-critical value without locking */ /* Yeah, let's not change a lock-critical value without locking */
ast_channel_lock(chan); ast_channel_lock(chan);
ast_channel_softhangup_internal_flag_add(chan, AST_SOFTHANGUP_DEV); ast_channel_softhangup_internal_flag_add(chan, AST_SOFTHANGUP_DEV);
publish_channel_blob(chan, ast_channel_hangup_request_type(), NULL); ast_channel_publish_blob(chan, ast_channel_hangup_request_type(), NULL);
res = ast_queue_frame(chan, &f); res = ast_queue_frame(chan, &f);
ast_channel_unlock(chan); ast_channel_unlock(chan);
@ -1398,7 +1383,7 @@ int ast_queue_hangup_with_cause(struct ast_channel *chan, int cause)
} }
blob = ast_json_pack("{s: i}", blob = ast_json_pack("{s: i}",
"cause", cause); "cause", cause);
publish_channel_blob(chan, ast_channel_hangup_request_type(), blob); ast_channel_publish_blob(chan, ast_channel_hangup_request_type(), blob);
res = ast_queue_frame(chan, &f); res = ast_queue_frame(chan, &f);
ast_channel_unlock(chan); ast_channel_unlock(chan);
@ -1419,7 +1404,7 @@ int ast_queue_hold(struct ast_channel *chan, const char *musicclass)
"musicclass", musicclass); "musicclass", musicclass);
} }
publish_channel_blob(chan, ast_channel_hold_type(), blob); ast_channel_publish_blob(chan, ast_channel_hold_type(), blob);
res = ast_queue_frame(chan, &f); res = ast_queue_frame(chan, &f);
return res; return res;
@ -1430,7 +1415,7 @@ int ast_queue_unhold(struct ast_channel *chan)
struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_UNHOLD }; struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_UNHOLD };
int res; int res;
publish_channel_blob(chan, ast_channel_unhold_type(), NULL); ast_channel_publish_blob(chan, ast_channel_unhold_type(), NULL);
res = ast_queue_frame(chan, &f); res = ast_queue_frame(chan, &f);
return res; return res;
@ -2741,7 +2726,7 @@ int ast_softhangup(struct ast_channel *chan, int cause)
blob = ast_json_pack("{s: i, s: b}", blob = ast_json_pack("{s: i, s: b}",
"cause", cause, "cause", cause,
"soft", 1); "soft", 1);
publish_channel_blob(chan, ast_channel_hangup_request_type(), blob); ast_channel_publish_blob(chan, ast_channel_hangup_request_type(), blob);
ast_channel_unlock(chan); ast_channel_unlock(chan);
return res; return res;
@ -3755,7 +3740,7 @@ static void send_dtmf_begin_event(struct ast_channel *chan,
return; return;
} }
publish_channel_blob(chan, ast_channel_dtmf_begin_type(), blob); ast_channel_publish_blob(chan, ast_channel_dtmf_begin_type(), blob);
} }
static void send_dtmf_end_event(struct ast_channel *chan, static void send_dtmf_end_event(struct ast_channel *chan,
@ -3772,7 +3757,7 @@ static void send_dtmf_end_event(struct ast_channel *chan,
return; return;
} }
publish_channel_blob(chan, ast_channel_dtmf_end_type(), blob); ast_channel_publish_blob(chan, ast_channel_dtmf_end_type(), blob);
} }
static void ast_read_generator_actions(struct ast_channel *chan, struct ast_frame *f) static void ast_read_generator_actions(struct ast_channel *chan, struct ast_frame *f)

@ -484,6 +484,20 @@ struct ast_json *ast_multi_channel_blob_get_json(struct ast_multi_channel_blob *
return obj->blob; return obj->blob;
} }
void ast_channel_publish_blob(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
{
RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
if (!blob) {
blob = ast_json_null();
}
message = ast_channel_blob_create(chan, type, blob);
if (message) {
stasis_publish(ast_channel_topic(chan), message);
}
}
void ast_channel_publish_snapshot(struct ast_channel *chan) void ast_channel_publish_snapshot(struct ast_channel *chan)
{ {
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);

Loading…
Cancel
Save