summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2013-05-21 18:00:22 +0000
committerRichard Mudgett <rmudgett@digium.com>2013-05-21 18:00:22 +0000
commit3d63833bd6c869b7efa383e8dea14be1a6eff998 (patch)
tree34957dd051b8f67c7cc58a510e24ee3873a61ad4 /apps
parente1e1cc2deefb92f8b43825f1f34e619354737842 (diff)
Merge in the bridge_construction branch to make the system use the Bridging API.
Breaks many things until they can be reworked. A partial list: chan_agent chan_dahdi, chan_misdn, chan_iax2 native bridging app_queue COLP updates DTMF attended transfers Protocol attended transfers git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@389378 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'apps')
-rw-r--r--apps/app_bridgewait.c245
-rw-r--r--apps/app_channelredirect.c4
-rw-r--r--apps/app_chanspy.c9
-rw-r--r--apps/app_confbridge.c501
-rw-r--r--apps/app_dial.c78
-rw-r--r--apps/app_dumpchan.c12
-rw-r--r--apps/app_followme.c2
-rw-r--r--apps/app_mixmonitor.c10
-rw-r--r--apps/app_parkandannounce.c249
-rw-r--r--apps/app_queue.c71
-rw-r--r--apps/confbridge/conf_chan_announce.c207
-rw-r--r--apps/confbridge/conf_chan_record.c94
-rw-r--r--apps/confbridge/conf_config_parser.c4
-rw-r--r--apps/confbridge/confbridge_manager.c339
-rw-r--r--apps/confbridge/include/confbridge.h63
15 files changed, 1248 insertions, 640 deletions
diff --git a/apps/app_bridgewait.c b/apps/app_bridgewait.c
new file mode 100644
index 000000000..ca12f0d30
--- /dev/null
+++ b/apps/app_bridgewait.c
@@ -0,0 +1,245 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Author: Jonathan Rose <jrose@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Application to place the channel into a holding Bridge
+ *
+ * \author Jonathan Rose <jrose@digium.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>bridge_holding</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/features.h"
+#include "asterisk/say.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/bridging.h"
+#include "asterisk/musiconhold.h"
+
+/*** DOCUMENTATION
+ <application name="BridgeWait" language="en_US">
+ <synopsis>
+ Put a call into the holding bridge.
+ </synopsis>
+ <syntax>
+ <parameter name="options">
+ <optionlist>
+ <option name="A">
+ <para>The channel will join the holding bridge as an
+ announcer</para>
+ </option>
+ <option name="m">
+ <argument name="class" required="false" />
+ <para>Play music on hold to the entering channel while it is
+ on hold. If the <emphasis>class</emphasis> is included, then
+ that class of music on hold will take priority over the
+ channel default.</para>
+ </option>
+ <option name="r">
+ <para>Play a ringing tone to the entering channel while it is
+ on hold.</para>
+ </option>
+ <option name="S">
+ <argument name="duration" required="true" />
+ <para>Automatically end the hold and return to the PBX after
+ <emphasis>duration</emphasis> seconds.</para>
+ </option>
+ </optionlist>
+ </parameter>
+ </syntax>
+ <description>
+ <para>This application places the incoming channel into a holding bridge.
+ The channel will then wait in the holding bridge until some
+ event occurs which removes it from the holding bridge.</para>
+ </description>
+ </application>
+ ***/
+/* BUGBUG Add bridge name/id parameter to specify which holding bridge to join (required) */
+/* BUGBUG Add h(moh-class) option to put channel on hold using AST_CONTROL_HOLD/AST_CONTROL_UNHOLD while in bridge */
+/* BUGBUG Add s option to send silence media frames to channel while in bridge (uses a silence generator) */
+/* BUGBUG Add n option to send no media to channel while in bridge (Channel should not be answered yet) */
+/* BUGBUG The channel may or may not be answered with the r option. */
+/* BUGBUG You should not place an announcer into a holding bridge with unanswered channels. */
+/* BUGBUG Not supplying any option flags will assume the m option with the default music class. */
+
+static char *app = "BridgeWait";
+static struct ast_bridge *holding_bridge;
+
+AST_MUTEX_DEFINE_STATIC(bridgewait_lock);
+
+enum bridgewait_flags {
+ MUXFLAG_PLAYMOH = (1 << 0),
+ MUXFLAG_RINGING = (1 << 1),
+ MUXFLAG_TIMEOUT = (1 << 2),
+ MUXFLAG_ANNOUNCER = (1 << 3),
+};
+
+enum bridgewait_args {
+ OPT_ARG_MOHCLASS,
+ OPT_ARG_TIMEOUT,
+ OPT_ARG_ARRAY_SIZE, /* Always the last element of the enum */
+};
+
+AST_APP_OPTIONS(bridgewait_opts, {
+ AST_APP_OPTION('A', MUXFLAG_ANNOUNCER),
+ AST_APP_OPTION('r', MUXFLAG_RINGING),
+ AST_APP_OPTION_ARG('m', MUXFLAG_PLAYMOH, OPT_ARG_MOHCLASS),
+ AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT),
+});
+
+static int apply_option_timeout(struct ast_bridge_features *features, char *duration_arg)
+{
+ struct ast_bridge_features_limits hold_limits;
+
+ if (ast_strlen_zero(duration_arg)) {
+ ast_log(LOG_ERROR, "No duration value provided for the timeout ('S') option.\n");
+ return -1;
+ }
+
+ if (ast_bridge_features_limits_construct(&hold_limits)) {
+ ast_log(LOG_ERROR, "Could not construct duration limits. Bridge canceled.\n");
+ return -1;
+ }
+
+ if (sscanf(duration_arg, "%u", &(hold_limits.duration)) != 1 || hold_limits.duration == 0) {
+ ast_log(LOG_ERROR, "Duration value provided for the timeout ('S') option must be greater than 0\n");
+ ast_bridge_features_limits_destroy(&hold_limits);
+ return -1;
+ }
+
+ /* Limits struct holds time as milliseconds, so muliply 1000x */
+ hold_limits.duration *= 1000;
+ ast_bridge_features_set_limits(features, &hold_limits, 1 /* remove_on_pull */);
+ ast_bridge_features_limits_destroy(&hold_limits);
+
+ return 0;
+}
+
+static void apply_option_moh(struct ast_channel *chan, char *class_arg)
+{
+ ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
+ ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", class_arg);
+}
+
+static void apply_option_ringing(struct ast_channel *chan)
+{
+ ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "ringing");
+}
+
+static int process_options(struct ast_channel *chan, struct ast_flags *flags, char **opts, struct ast_bridge_features *features)
+{
+ if (ast_test_flag(flags, MUXFLAG_TIMEOUT)) {
+ if (apply_option_timeout(features, opts[OPT_ARG_TIMEOUT])) {
+ return -1;
+ }
+ }
+
+ if (ast_test_flag(flags, MUXFLAG_ANNOUNCER)) {
+ /* Announcer specific stuff */
+ ast_channel_add_bridge_role(chan, "announcer");
+ } else {
+ /* Non Announcer specific stuff */
+ ast_channel_add_bridge_role(chan, "holding_participant");
+
+ if (ast_test_flag(flags, MUXFLAG_PLAYMOH)) {
+ apply_option_moh(chan, opts[OPT_ARG_MOHCLASS]);
+ } else if (ast_test_flag(flags, MUXFLAG_RINGING)) {
+ apply_option_ringing(chan);
+ }
+ }
+
+ return 0;
+}
+
+static int bridgewait_exec(struct ast_channel *chan, const char *data)
+{
+ struct ast_bridge_features chan_features;
+ struct ast_flags flags = { 0 };
+ char *parse;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(options);
+ AST_APP_ARG(other); /* Any remaining unused arguments */
+ );
+
+ ast_mutex_lock(&bridgewait_lock);
+ if (!holding_bridge) {
+ holding_bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_HOLDING,
+ AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
+ | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
+ }
+ ast_mutex_unlock(&bridgewait_lock);
+ if (!holding_bridge) {
+ ast_log(LOG_ERROR, "Could not create holding bridge for '%s'.\n", ast_channel_name(chan));
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_bridge_features_init(&chan_features)) {
+ ast_bridge_features_cleanup(&chan_features);
+ return -1;
+ }
+
+ if (args.options) {
+ char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
+ ast_app_parse_options(bridgewait_opts, &flags, opts, args.options);
+ if (process_options(chan, &flags, opts, &chan_features)) {
+ ast_bridge_features_cleanup(&chan_features);
+ return -1;
+ }
+ }
+
+ ast_bridge_join(holding_bridge, chan, NULL, &chan_features, NULL, 0);
+
+ ast_bridge_features_cleanup(&chan_features);
+ return ast_check_hangup_locked(chan) ? -1 : 0;
+}
+
+static int unload_module(void)
+{
+ ao2_cleanup(holding_bridge);
+ holding_bridge = NULL;
+
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application_xml(app, bridgewait_exec);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Place the channel into a holding bridge application");
diff --git a/apps/app_channelredirect.c b/apps/app_channelredirect.c
index 8c98ed7b7..f636e0248 100644
--- a/apps/app_channelredirect.c
+++ b/apps/app_channelredirect.c
@@ -96,10 +96,6 @@ static int asyncgoto_exec(struct ast_channel *chan, const char *data)
return 0;
}
- if (ast_channel_pbx(chan2)) {
- ast_set_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
- }
-
res = ast_async_parseable_goto(chan2, args.label);
chan2 = ast_channel_unref(chan2);
diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c
index c5adb78ad..8e4590781 100644
--- a/apps/app_chanspy.c
+++ b/apps/app_chanspy.c
@@ -482,15 +482,18 @@ static struct ast_generator spygen = {
static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook)
{
int res = 0;
- struct ast_channel *peer = NULL;
ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, ast_channel_name(autochan->chan));
ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
res = ast_audiohook_attach(autochan->chan, audiohook);
- if (!res && ast_test_flag(ast_channel_flags(autochan->chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(autochan->chan))) {
- ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
+ if (!res) {
+ ast_channel_lock(autochan->chan);
+ if (ast_channel_is_bridged(autochan->chan)) {
+ ast_softhangup_nolock(autochan->chan, AST_SOFTHANGUP_UNBRIDGE);
+ }
+ ast_channel_unlock(autochan->chan);
}
return res;
}
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index d4cce4b46..5107bcd5c 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -67,6 +67,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/paths.h"
#include "asterisk/manager.h"
#include "asterisk/test.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_bridging.h"
+#include "asterisk/json.h"
/*** DOCUMENTATION
<application name="ConfBridge" language="en_US">
@@ -303,7 +306,7 @@ enum {
};
/*! \brief Container to hold all conference bridges in progress */
-static struct ao2_container *conference_bridges;
+struct ao2_container *conference_bridges;
static void leave_conference(struct confbridge_user *user);
static int play_sound_number(struct confbridge_conference *conference, int say_number);
@@ -412,221 +415,77 @@ const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds
return "";
}
-static void send_conf_start_event(const char *conf_name)
-{
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when a conference starts.</synopsis>
- <syntax>
- <parameter name="Conference">
- <para>The name of the Confbridge conference.</para>
- </parameter>
- </syntax>
- <see-also>
- <ref type="managerEvent">ConfbridgeEnd</ref>
- <ref type="application">ConfBridge</ref>
- </see-also>
- </managerEventInstance>
- ***/
- manager_event(EVENT_FLAG_CALL, "ConfbridgeStart", "Conference: %s\r\n", conf_name);
-}
-
-static void send_conf_end_event(const char *conf_name)
-{
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when a conference ends.</synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
- </syntax>
- <see-also>
- <ref type="managerEvent">ConfbridgeStart</ref>
- </see-also>
- </managerEventInstance>
- ***/
- manager_event(EVENT_FLAG_CALL, "ConfbridgeEnd", "Conference: %s\r\n", conf_name);
-}
-
-static void send_join_event(struct ast_channel *chan, const char *conf_name)
-{
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when a channel joins a Confbridge conference.</synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
- </syntax>
- <see-also>
- <ref type="managerEvent">ConfbridgeLeave</ref>
- <ref type="application">ConfBridge</ref>
- </see-also>
- </managerEventInstance>
- ***/
- ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeJoin",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Conference: %s\r\n"
- "CallerIDnum: %s\r\n"
- "CallerIDname: %s\r\n",
- ast_channel_name(chan),
- ast_channel_uniqueid(chan),
- conf_name,
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
- S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
- );
+static void send_conf_stasis(struct confbridge_conference *conference, struct ast_channel *chan, const char *type, struct ast_json *extras, int channel_topic)
+{
+ RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+
+ json_object = ast_json_pack("{s: s, s: s}",
+ "type", type,
+ "conference", conference->name);
+
+ if (!json_object) {
+ return;
+ }
+
+ if (extras) {
+ ast_json_object_update(json_object, extras);
+ }
+
+ msg = ast_bridge_blob_create(confbridge_message_type(),
+ conference->bridge,
+ chan,
+ json_object);
+ if (!msg) {
+ return;
+ }
+
+ if (channel_topic) {
+ stasis_publish(ast_channel_topic(chan), msg);
+ } else {
+ stasis_publish(ast_bridge_topic(conference->bridge), msg);
+ }
+
}
-static void send_leave_event(struct ast_channel *chan, const char *conf_name)
-{
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when a channel leaves a Confbridge conference.</synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
- </syntax>
- <see-also>
- <ref type="managerEvent">ConfbridgeJoin</ref>
- </see-also>
- </managerEventInstance>
- ***/
- ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeLeave",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Conference: %s\r\n"
- "CallerIDnum: %s\r\n"
- "CallerIDname: %s\r\n",
- ast_channel_name(chan),
- ast_channel_uniqueid(chan),
- conf_name,
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
- S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
- );
+static void send_conf_start_event(struct confbridge_conference *conference)
+{
+ send_conf_stasis(conference, NULL, "confbridge_start", NULL, 0);
}
-static void send_start_record_event(const char *conf_name)
-{
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when a conference recording starts.</synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
- </syntax>
- <see-also>
- <ref type="managerEvent">ConfbridgeStopRecord</ref>
- <ref type="application">ConfBridge</ref>
- </see-also>
- </managerEventInstance>
- ***/
-
- manager_event(EVENT_FLAG_CALL, "ConfbridgeStartRecord", "Conference: %s\r\n", conf_name);
-}
-
-static void send_stop_record_event(const char *conf_name)
-{
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when a conference recording stops.</synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
- </syntax>
- <see-also>
- <ref type="managerEvent">ConfbridgeStartRecord</ref>
- </see-also>
- </managerEventInstance>
- ***/
- manager_event(EVENT_FLAG_CALL, "ConfbridgeStopRecord", "Conference: %s\r\n", conf_name);
-}
-
-static void send_mute_event(struct ast_channel *chan, const char *conf_name)
-{
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when a Confbridge participant mutes.</synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
- </syntax>
- <see-also>
- <ref type="managerEvent">ConfbridgeUnmute</ref>
- <ref type="application">ConfBridge</ref>
- </see-also>
- </managerEventInstance>
- ***/
- ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeMute",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Conference: %s\r\n"
- "CallerIDnum: %s\r\n"
- "CallerIDname: %s\r\n",
- ast_channel_name(chan),
- ast_channel_uniqueid(chan),
- conf_name,
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
- S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
- );
+static void send_conf_end_event(struct confbridge_conference *conference)
+{
+ send_conf_stasis(conference, NULL, "confbridge_end", NULL, 0);
}
-static void send_unmute_event(struct ast_channel *chan, const char *conf_name)
-{
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when a Confbridge participant unmutes.</synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
- </syntax>
- <see-also>
- <ref type="managerEvent">ConfbridgeMute</ref>
- </see-also>
- </managerEventInstance>
- ***/
- ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeUnmute",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Conference: %s\r\n"
- "CallerIDnum: %s\r\n"
- "CallerIDname: %s\r\n",
- ast_channel_name(chan),
- ast_channel_uniqueid(chan),
- conf_name,
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
- S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
- );
+static void send_join_event(struct ast_channel *chan, struct confbridge_conference *conference)
+{
+ send_conf_stasis(conference, chan, "confbridge_join", NULL, 0);
}
+static void send_leave_event(struct ast_channel *chan, struct confbridge_conference *conference)
+{
+ send_conf_stasis(conference, chan, "confbridge_leave", NULL, 0);
+}
-static struct ast_frame *rec_read(struct ast_channel *ast)
+static void send_start_record_event(struct confbridge_conference *conference)
{
- return &ast_null_frame;
+ send_conf_stasis(conference, NULL, "confbridge_record", NULL, 0);
}
-static int rec_write(struct ast_channel *ast, struct ast_frame *f)
+
+static void send_stop_record_event(struct confbridge_conference *conference)
{
- return 0;
+ send_conf_stasis(conference, NULL, "confbridge_stop_record", NULL, 0);
}
-static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
-static struct ast_channel_tech record_tech = {
- .type = "ConfBridgeRec",
- .description = "Conference Bridge Recording Channel",
- .requester = rec_request,
- .read = rec_read,
- .write = rec_write,
-};
-static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
-{
- struct ast_channel *tmp;
- struct ast_format fmt;
- const char *conf_name = data;
- if (!(tmp = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", NULL, 0,
- "ConfBridgeRecorder/conf-%s-uid-%d",
- conf_name,
- (int) ast_random()))) {
- return NULL;
- }
- ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0);
- ast_channel_tech_set(tmp, &record_tech);
- ast_format_cap_add_all(ast_channel_nativeformats(tmp));
- ast_format_copy(ast_channel_writeformat(tmp), &fmt);
- ast_format_copy(ast_channel_rawwriteformat(tmp), &fmt);
- ast_format_copy(ast_channel_readformat(tmp), &fmt);
- ast_format_copy(ast_channel_rawreadformat(tmp), &fmt);
- return tmp;
+
+static void send_mute_event(struct ast_channel *chan, struct confbridge_conference *conference)
+{
+ send_conf_stasis(conference, chan, "confbridge_mute", NULL, 1);
+}
+
+static void send_unmute_event(struct ast_channel *chan, struct confbridge_conference *conference)
+{
+ send_conf_stasis(conference, chan, "confbridge_unmute", NULL, 1);
}
static void set_rec_filename(struct confbridge_conference *conference, struct ast_str **filename, int is_new)
@@ -682,6 +541,7 @@ static void *record_thread(void *obj)
struct ast_channel *chan;
struct ast_str *filename = ast_str_alloca(PATH_MAX);
struct ast_str *orig_rec_file = NULL;
+ struct ast_bridge_features features;
ast_mutex_lock(&conference->record_lock);
if (!mixmonapp) {
@@ -691,20 +551,29 @@ static void *record_thread(void *obj)
ao2_ref(conference, -1);
return NULL;
}
+ if (ast_bridge_features_init(&features)) {
+ ast_bridge_features_cleanup(&features);
+ conference->record_thread = AST_PTHREADT_NULL;
+ ast_mutex_unlock(&conference->record_lock);
+ ao2_ref(conference, -1);
+ return NULL;
+ }
+ ast_set_flag(&features.feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
/* XXX If we get an EXIT right here, START will essentially be a no-op */
while (conference->record_state != CONF_RECORD_EXIT) {
set_rec_filename(conference, &filename,
- is_new_rec_file(conference->b_profile.rec_file, &orig_rec_file));
+ is_new_rec_file(conference->b_profile.rec_file, &orig_rec_file));
chan = ast_channel_ref(conference->record_chan);
ast_answer(chan);
pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
- ast_bridge_join(conference->bridge, chan, NULL, NULL, NULL);
+ ast_bridge_join(conference->bridge, chan, NULL, &features, NULL, 0);
ast_hangup(chan); /* This will eat this thread's reference to the channel as well */
/* STOP has been called. Wait for either a START or an EXIT */
ast_cond_wait(&conference->record_cond, &conference->record_lock);
}
+ ast_bridge_features_cleanup(&features);
ast_free(orig_rec_file);
ast_mutex_unlock(&conference->record_lock);
ao2_ref(conference, -1);
@@ -739,7 +608,7 @@ static int conf_stop_record(struct confbridge_conference *conference)
ast_queue_frame(chan, &ast_null_frame);
chan = ast_channel_unref(chan);
ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference->b_profile.name);
- send_stop_record_event(conference->name);
+ send_stop_record_event(conference);
return 0;
}
@@ -783,8 +652,7 @@ static int conf_stop_record_thread(struct confbridge_conference *conference)
static int conf_start_record(struct confbridge_conference *conference)
{
struct ast_format_cap *cap;
- struct ast_format tmpfmt;
- int cause;
+ struct ast_format format;
if (conference->record_state != CONF_RECORD_STOP) {
return -1;
@@ -795,25 +663,26 @@ static int conf_start_record(struct confbridge_conference *conference)
return -1;
}
- if (!(cap = ast_format_cap_alloc_nolock())) {
+ cap = ast_format_cap_alloc_nolock();
+ if (!cap) {
return -1;
}
- ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
+ ast_format_cap_add(cap, ast_format_set(&format, AST_FORMAT_SLINEAR, 0));
- if (!(conference->record_chan = ast_request("ConfBridgeRec", cap, NULL, conference->name, &cause))) {
- cap = ast_format_cap_destroy(cap);
+ conference->record_chan = ast_request("CBRec", cap, NULL,
+ conference->name, NULL);
+ cap = ast_format_cap_destroy(cap);
+ if (!conference->record_chan) {
return -1;
}
- cap = ast_format_cap_destroy(cap);
-
conference->record_state = CONF_RECORD_START;
ast_mutex_lock(&conference->record_lock);
ast_cond_signal(&conference->record_cond);
ast_mutex_unlock(&conference->record_lock);
ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference->b_profile.name);
- send_start_record_event(conference->name);
+ send_start_record_event(conference);
return 0;
}
@@ -1017,10 +886,7 @@ static void destroy_conference_bridge(void *obj)
ast_debug(1, "Destroying conference bridge '%s'\n", conference->name);
if (conference->playback_chan) {
- struct ast_channel *underlying_channel = ast_channel_tech(conference->playback_chan)->bridged_channel(conference->playback_chan, NULL);
- if (underlying_channel) {
- ast_hangup(underlying_channel);
- }
+ conf_announce_channel_depart(conference->playback_chan);
ast_hangup(conference->playback_chan);
conference->playback_chan = NULL;
}
@@ -1252,7 +1118,7 @@ void conf_ended(struct confbridge_conference *conference)
{
/* Called with a reference to conference */
ao2_unlink(conference_bridges, conference);
- send_conf_end_event(conference->name);
+ send_conf_end_event(conference);
conf_stop_record_thread(conference);
}
@@ -1314,7 +1180,9 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
conf_bridge_profile_copy(&conference->b_profile, &user->b_profile);
/* Create an actual bridge that will do the audio mixing */
- if (!(conference->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0))) {
+ conference->bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_MULTIMIX,
+ AST_BRIDGE_FLAG_MASQUERADE_ONLY | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY);
+ if (!conference->bridge) {
ao2_ref(conference, -1);
conference = NULL;
ao2_unlock(conference_bridges);
@@ -1351,7 +1219,7 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
ao2_unlock(conference);
}
- send_conf_start_event(conference->name);
+ send_conf_start_event(conference);
ast_debug(1, "Created conference '%s' and linked to container.\n", conference_name);
}
@@ -1452,74 +1320,59 @@ static void leave_conference(struct confbridge_user *user)
/*!
* \internal
- * \brief allocates playback chan on a channel
+ * \brief Allocate playback channel for a conference.
* \pre expects conference to be locked before calling this function
*/
static int alloc_playback_chan(struct confbridge_conference *conference)
{
- int cause;
struct ast_format_cap *cap;
- struct ast_format tmpfmt;
+ struct ast_format format;
- if (conference->playback_chan) {
- return 0;
- }
- if (!(cap = ast_format_cap_alloc_nolock())) {
- return -1;
- }
- ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
- if (!(conference->playback_chan = ast_request("Bridge", cap, NULL, "", &cause))) {
- cap = ast_format_cap_destroy(cap);
+ cap = ast_format_cap_alloc_nolock();
+ if (!cap) {
return -1;
}
+ ast_format_cap_add(cap, ast_format_set(&format, AST_FORMAT_SLINEAR, 0));
+ conference->playback_chan = ast_request("CBAnn", cap, NULL,
+ conference->name, NULL);
cap = ast_format_cap_destroy(cap);
-
- ast_channel_internal_bridge_set(conference->playback_chan, conference->bridge);
-
- if (ast_call(conference->playback_chan, "", 0)) {
- ast_hangup(conference->playback_chan);
- conference->playback_chan = NULL;
+ if (!conference->playback_chan) {
return -1;
}
- ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference->name);
+ ast_debug(1, "Created announcer channel '%s' to conference bridge '%s'\n",
+ ast_channel_name(conference->playback_chan), conference->name);
return 0;
}
static int play_sound_helper(struct confbridge_conference *conference, const char *filename, int say_number)
{
- struct ast_channel *underlying_channel;
-
/* Do not waste resources trying to play files that do not exist */
if (!ast_strlen_zero(filename) && !sound_file_exists(filename)) {
return 0;
}
ast_mutex_lock(&conference->playback_lock);
- if (!(conference->playback_chan)) {
- if (alloc_playback_chan(conference)) {
- ast_mutex_unlock(&conference->playback_lock);
- return -1;
- }
- underlying_channel = ast_channel_tech(conference->playback_chan)->bridged_channel(conference->playback_chan, NULL);
- } else {
- /* Channel was already available so we just need to add it back into the bridge */
- underlying_channel = ast_channel_tech(conference->playback_chan)->bridged_channel(conference->playback_chan, NULL);
- if (ast_bridge_impart(conference->bridge, underlying_channel, NULL, NULL, 0)) {
- ast_mutex_unlock(&conference->playback_lock);
- return -1;
- }
+ if (!conference->playback_chan && alloc_playback_chan(conference)) {
+ ast_mutex_unlock(&conference->playback_lock);
+ return -1;
+ }
+ if (conf_announce_channel_push(conference->playback_chan)) {
+ ast_mutex_unlock(&conference->playback_lock);
+ return -1;
}
/* The channel is all under our control, in goes the prompt */
if (!ast_strlen_zero(filename)) {
ast_stream_and_wait(conference->playback_chan, filename, "");
} else if (say_number >= 0) {
- ast_say_number(conference->playback_chan, say_number, "", ast_channel_language(conference->playback_chan), NULL);
+ ast_say_number(conference->playback_chan, say_number, "",
+ ast_channel_language(conference->playback_chan), NULL);
}
- ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", ast_channel_name(underlying_channel), conference->bridge);
- ast_bridge_depart(conference->bridge, underlying_channel);
+ ast_debug(1, "Departing announcer channel '%s' from conference bridge '%s'\n",
+ ast_channel_name(conference->playback_chan), conference->name);
+ conf_announce_channel_depart(conference->playback_chan);
ast_mutex_unlock(&conference->playback_lock);
@@ -1550,43 +1403,25 @@ static void conf_handle_talker_destructor(void *pvt_data)
ast_free(pvt_data);
}
-static void conf_handle_talker_cb(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *pvt_data)
+static void conf_handle_talker_cb(struct ast_bridge_channel *bridge_channel, void *pvt_data, int talking)
{
- char *conf_name = pvt_data;
- int talking;
+ const char *conf_name = pvt_data;
+ struct confbridge_conference *conference = ao2_find(conference_bridges, conf_name, OBJ_KEY);
+ struct ast_json *talking_extras;
- switch (bridge_channel->state) {
- case AST_BRIDGE_CHANNEL_STATE_START_TALKING:
- talking = 1;
- break;
- case AST_BRIDGE_CHANNEL_STATE_STOP_TALKING:
- talking = 0;
- break;
- default:
- return; /* uhh this shouldn't happen, but bail if it does. */
- }
-
- /* notify AMI someone is has either started or stopped talking */
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when a conference participant has started or stopped talking.</synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
- <parameter name="TalkingStatus">
- <enumlist>
- <enum name="on"/>
- <enum name="off"/>
- </enumlist>
- </parameter>
- </syntax>
- </managerEventInstance>
- ***/
- ast_manager_event(bridge_channel->chan, EVENT_FLAG_CALL, "ConfbridgeTalking",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Conference: %s\r\n"
- "TalkingStatus: %s\r\n",
- ast_channel_name(bridge_channel->chan), ast_channel_uniqueid(bridge_channel->chan), conf_name, talking ? "on" : "off");
+ if (!conference) {
+ return;
+ }
+
+ talking_extras = ast_json_pack("{s: s}",
+ "talking_status", talking ? "on" : "off");
+
+ if (!talking_extras) {
+ return;
+ }
+
+ send_conf_stasis(conference, bridge_channel->chan, "confbridge_talking", talking_extras, 0);
+ ast_json_unref(talking_extras);
}
static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
@@ -1681,12 +1516,16 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
AST_APP_ARG(u_profile_name);
AST_APP_ARG(menu_name);
);
- ast_bridge_features_init(&user.features);
if (ast_channel_state(chan) != AST_STATE_UP) {
ast_answer(chan);
}
+ if (ast_bridge_features_init(&user.features)) {
+ res = -1;
+ goto confbridge_cleanup;
+ }
+
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
res = -1; /* invalid PIN */
@@ -1832,13 +1671,14 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
conf_moh_unsuspend(&user);
/* Join our conference bridge for real */
- send_join_event(user.chan, conference->name);
+ send_join_event(user.chan, conference);
ast_bridge_join(conference->bridge,
chan,
NULL,
&user.features,
- &user.tech_args);
- send_leave_event(user.chan, conference->name);
+ &user.tech_args,
+ 0);
+ send_leave_event(user.chan, conference);
/* if we're shutting down, don't attempt to do further processing */
if (ast_shutting_down()) {
@@ -1905,9 +1745,9 @@ static int action_toggle_mute(struct confbridge_conference *conference,
user->features.mute = (!user->features.mute ? 1 : 0);
ast_test_suite_event_notify("CONF_MUTE", "Message: participant %s %s\r\nConference: %s\r\nChannel: %s", ast_channel_name(chan), user->features.mute ? "muted" : "unmuted", user->b_profile.name, ast_channel_name(chan));
if (user->features.mute) {
- send_mute_event(chan, conference->name);
- } else {
- send_unmute_event(chan, conference->name);
+ send_mute_event(chan, conference);
+ } else {
+ send_unmute_event(chan, conference);
}
}
return ast_stream_and_wait(chan, (user->features.mute ?
@@ -3207,6 +3047,46 @@ void conf_remove_user_waiting(struct confbridge_conference *conference, struct c
conference->waitingusers--;
}
+/*!
+ * \internal
+ * \brief Unregister a ConfBridge channel technology.
+ * \since 12.0.0
+ *
+ * \param tech What to unregister.
+ *
+ * \return Nothing
+ */
+static void unregister_channel_tech(struct ast_channel_tech *tech)
+{
+ ast_channel_unregister(tech);
+ tech->capabilities = ast_format_cap_destroy(tech->capabilities);
+}
+
+/*!
+ * \internal
+ * \brief Register a ConfBridge channel technology.
+ * \since 12.0.0
+ *
+ * \param tech What to register.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int register_channel_tech(struct ast_channel_tech *tech)
+{
+ tech->capabilities = ast_format_cap_alloc();
+ if (!tech->capabilities) {
+ return -1;
+ }
+ ast_format_cap_add_all(tech->capabilities);
+ if (ast_channel_register(tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel technology %s(%s).\n",
+ tech->type, tech->description);
+ return -1;
+ }
+ return 0;
+}
+
/*! \brief Called when module is being unloaded */
static int unload_module(void)
{
@@ -3228,14 +3108,17 @@ static int unload_module(void)
ast_manager_unregister("ConfbridgeStopRecord");
ast_manager_unregister("ConfbridgeSetSingleVideoSrc");
+ /* Unsubscribe from stasis confbridge message type and clean it up. */
+ manager_confbridge_shutdown();
+
/* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
ao2_cleanup(conference_bridges);
conference_bridges = NULL;
conf_destroy_config();
- ast_channel_unregister(&record_tech);
- record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
+ unregister_channel_tech(conf_announce_get_tech());
+ unregister_channel_tech(conf_record_get_tech());
return 0;
}
@@ -3259,13 +3142,8 @@ static int load_module(void)
return AST_MODULE_LOAD_DECLINE;
}
- if (!(record_tech.capabilities = ast_format_cap_alloc())) {
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- ast_format_cap_add_all(record_tech.capabilities);
- if (ast_channel_register(&record_tech)) {
- ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
+ if (register_channel_tech(conf_record_get_tech())
+ || register_channel_tech(conf_announce_get_tech())) {
unload_module();
return AST_MODULE_LOAD_FAILURE;
}
@@ -3278,6 +3156,9 @@ static int load_module(void)
return AST_MODULE_LOAD_FAILURE;
}
+ /* Setup manager stasis subscriptions */
+ res |= manager_confbridge_init();
+
res |= ast_register_application_xml(app, confbridge_exec);
res |= ast_custom_function_register(&confbridge_function);
diff --git a/apps/app_dial.c b/apps/app_dial.c
index d3d37216a..35c9ad8bf 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -26,7 +26,6 @@
*/
/*** MODULEINFO
- <depend>chan_local</depend>
<support_level>core</support_level>
***/
@@ -67,6 +66,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/ccss.h"
#include "asterisk/indications.h"
#include "asterisk/framehook.h"
+#include "asterisk/bridging.h"
#include "asterisk/stasis_channels.h"
/*** DOCUMENTATION
@@ -2037,6 +2037,40 @@ static int dial_handle_playtones(struct ast_channel *chan, const char *data)
return res;
}
+/*!
+ * \internal
+ * \brief Setup the after bridge goto location on the peer.
+ * \since 12.0.0
+ *
+ * \param chan Calling channel for bridge.
+ * \param peer Peer channel for bridge.
+ * \param opts Dialing option flags.
+ * \param opt_args Dialing option argument strings.
+ *
+ * \return Nothing
+ */
+static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags64 *opts, char *opt_args[])
+{
+ const char *context;
+ const char *extension;
+ int priority;
+
+ if (ast_test_flag64(opts, OPT_PEER_H)) {
+ ast_channel_lock(chan);
+ context = ast_strdupa(ast_channel_context(chan));
+ ast_channel_unlock(chan);
+ ast_after_bridge_set_h(peer, context);
+ } else if (ast_test_flag64(opts, OPT_CALLEE_GO_ON)) {
+ ast_channel_lock(chan);
+ context = ast_strdupa(ast_channel_context(chan));
+ extension = ast_strdupa(ast_channel_exten(chan));
+ priority = ast_channel_priority(chan);
+ ast_channel_unlock(chan);
+ ast_after_bridge_set_go_on(peer, context, extension, priority,
+ opt_args[OPT_ARG_CALLEE_GO_ON]);
+ }
+}
+
static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast_flags64 *peerflags, int *continue_exec)
{
int res = -1; /* default: error */
@@ -2974,6 +3008,14 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
}
if (res) { /* some error */
+ if (!ast_check_hangup(chan) && ast_check_hangup(peer)) {
+ ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer));
+ }
+ setup_peer_after_bridge_goto(chan, peer, &opts, opt_args);
+ if (ast_after_bridge_goto_setup(peer)
+ || ast_pbx_start(peer)) {
+ ast_autoservice_chan_hangup_peer(chan, peer);
+ }
res = -1;
} else {
if (ast_test_flag64(peerflags, OPT_CALLEE_TRANSFER))
@@ -2996,8 +3038,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMIXMON);
if (ast_test_flag64(peerflags, OPT_CALLER_MIXMONITOR))
ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMIXMON);
- if (ast_test_flag64(peerflags, OPT_GO_ON))
- ast_set_flag(&(config.features_caller), AST_FEATURE_NO_H_EXTEN);
config.end_bridge_callback = end_bridge_callback;
config.end_bridge_callback_data = chan;
@@ -3029,38 +3069,10 @@ 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);
}
+/* BUGBUG bridge needs to set hangup cause on chan when peer breaks the bridge. */
+ setup_peer_after_bridge_goto(chan, peer, &opts, opt_args);
res = ast_bridge_call(chan, peer, &config);
}
-
- ast_channel_context_set(peer, ast_channel_context(chan));
-
- if (ast_test_flag64(&opts, OPT_PEER_H)
- && ast_exists_extension(peer, ast_channel_context(peer), "h", 1,
- S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
- ast_autoservice_start(chan);
- ast_pbx_h_exten_run(peer, ast_channel_context(peer));
- ast_autoservice_stop(chan);
- }
- if (!ast_check_hangup(peer)) {
- if (ast_test_flag64(&opts, OPT_CALLEE_GO_ON)) {
- int goto_res;
-
- if (!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
- ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
- goto_res = ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
- } else { /* F() */
- goto_res = ast_goto_if_exists(peer, ast_channel_context(chan),
- ast_channel_exten(chan), ast_channel_priority(chan) + 1);
- }
- if (!goto_res && !ast_pbx_start(peer)) {
- /* The peer is now running its own PBX. */
- goto out;
- }
- }
- } else if (!ast_check_hangup(chan)) {
- ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer));
- }
- ast_autoservice_chan_hangup_peer(chan, peer);
}
out:
if (moh) {
diff --git a/apps/app_dumpchan.c b/apps/app_dumpchan.c
index 4a80a3d13..722f15541 100644
--- a/apps/app_dumpchan.c
+++ b/apps/app_dumpchan.c
@@ -41,6 +41,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/channel.h"
#include "asterisk/app.h"
#include "asterisk/translate.h"
+#include "asterisk/bridging.h"
/*** DOCUMENTATION
<application name="DumpChan" language="en_US">
@@ -77,6 +78,7 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
char pgrp[256];
struct ast_str *write_transpath = ast_str_alloca(256);
struct ast_str *read_transpath = ast_str_alloca(256);
+ struct ast_bridge *bridge;
now = ast_tvnow();
memset(buf, 0, size);
@@ -90,6 +92,9 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
sec = elapsed_seconds % 60;
}
+ ast_channel_lock(c);
+ bridge = ast_channel_get_bridge(c);
+ ast_channel_unlock(c);
snprintf(buf,size,
"Name= %s\n"
"Type= %s\n"
@@ -117,8 +122,7 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
"Framesout= %d %s\n"
"TimetoHangup= %ld\n"
"ElapsedTime= %dh%dm%ds\n"
- "DirectBridge= %s\n"
- "IndirectBridge= %s\n"
+ "BridgeID= %s\n"
"Context= %s\n"
"Extension= %s\n"
"Priority= %d\n"
@@ -158,8 +162,7 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
hour,
min,
sec,
- ast_channel_internal_bridged_channel(c) ? ast_channel_name(ast_channel_internal_bridged_channel(c)) : "<none>",
- ast_bridged_channel(c) ? ast_channel_name(ast_bridged_channel(c)) : "<none>",
+ bridge ? bridge->uniqueid : "(Not bridged)",
ast_channel_context(c),
ast_channel_exten(c),
ast_channel_priority(c),
@@ -169,6 +172,7 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
ast_channel_data(c) ? S_OR(ast_channel_data(c), "(Empty)") : "(None)",
(ast_test_flag(ast_channel_flags(c), AST_FLAG_BLOCKING) ? ast_channel_blockproc(c) : "(Not Blocking)"));
+ ao2_cleanup(bridge);
return 0;
}
diff --git a/apps/app_followme.c b/apps/app_followme.c
index 83f583bb3..43f196708 100644
--- a/apps/app_followme.c
+++ b/apps/app_followme.c
@@ -36,7 +36,6 @@
*/
/*** MODULEINFO
- <depend>chan_local</depend>
<support_level>core</support_level>
***/
@@ -1520,7 +1519,6 @@ static int app_exec(struct ast_channel *chan, const char *data)
}
res = ast_bridge_call(caller, outbound, &config);
- ast_autoservice_chan_hangup_peer(caller, outbound);
}
outrun:
diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c
index 6e7976ec9..e8d4903f0 100644
--- a/apps/app_mixmonitor.c
+++ b/apps/app_mixmonitor.c
@@ -411,7 +411,6 @@ static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
{
- struct ast_channel *peer = NULL;
int res = 0;
if (!chan)
@@ -419,8 +418,13 @@ static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
ast_audiohook_attach(chan, audiohook);
- if (!res && ast_test_flag(ast_channel_flags(chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
- ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
+ if (!res) {
+ ast_channel_lock(chan);
+ if (ast_channel_is_bridged(chan)) {
+ ast_softhangup_nolock(chan, AST_SOFTHANGUP_UNBRIDGE);
+ }
+ ast_channel_unlock(chan);
+ }
return res;
}
diff --git a/apps/app_parkandannounce.c b/apps/app_parkandannounce.c
deleted file mode 100644
index 6d6ccae26..000000000
--- a/apps/app_parkandannounce.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2006, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * Author: Ben Miller <bgmiller@dccinc.com>
- * With TONS of help from Mark!
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \brief ParkAndAnnounce application for Asterisk
- *
- * \author Ben Miller <bgmiller@dccinc.com>
- * \arg With TONS of help from Mark!
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/features.h"
-#include "asterisk/say.h"
-#include "asterisk/lock.h"
-#include "asterisk/utils.h"
-#include "asterisk/app.h"
-
-/*** DOCUMENTATION
- <application name="ParkAndAnnounce" language="en_US">
- <synopsis>
- Park and Announce.
- </synopsis>
- <syntax>
- <parameter name="announce_template" required="true" argsep=":">
- <argument name="announce" required="true">
- <para>Colon-separated list of files to announce. The word
- <literal>PARKED</literal> will be replaced by a say_digits of the extension in which
- the call is parked.</para>
- </argument>
- <argument name="announce1" multiple="true" />
- </parameter>
- <parameter name="timeout" required="true">
- <para>Time in seconds before the call returns into the return
- context.</para>
- </parameter>
- <parameter name="dial" required="true">
- <para>The app_dial style resource to call to make the
- announcement. Console/dsp calls the console.</para>
- </parameter>
- <parameter name="return_context">
- <para>The goto-style label to jump the call back into after
- timeout. Default <literal>priority+1</literal>.</para>
- </parameter>
- </syntax>
- <description>
- <para>Park a call into the parkinglot and announce the call to another channel.</para>
- <para>The variable <variable>PARKEDAT</variable> will contain the parking extension
- into which the call was placed. Use with the Local channel to allow the dialplan to make
- use of this information.</para>
- </description>
- <see-also>
- <ref type="application">Park</ref>
- <ref type="application">ParkedCall</ref>
- </see-also>
- </application>
- ***/
-
-static char *app = "ParkAndAnnounce";
-
-static int parkandannounce_exec(struct ast_channel *chan, const char *data)
-{
- int res = -1;
- int lot, timeout = 0, dres;
- char *dialtech, *tmp[100], buf[13];
- int looptemp, i;
- char *s;
- struct ast_party_id caller_id;
-
- struct ast_channel *dchan;
- struct outgoing_helper oh = { 0, };
- int outstate;
- struct ast_format tmpfmt;
- struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
-
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(template);
- AST_APP_ARG(timeout);
- AST_APP_ARG(dial);
- AST_APP_ARG(return_context);
- );
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce_template,timeout,dial,[return_context])\n");
- res = -1;
- goto parkcleanup;
- }
- if (!cap_slin) {
- res = -1;
- goto parkcleanup;
- }
- ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
-
- s = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, s);
-
- if (args.timeout)
- timeout = atoi(args.timeout) * 1000;
-
- if (ast_strlen_zero(args.dial)) {
- ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or DAHDI/g1/5551212\n");
- res = -1;
- goto parkcleanup;
- }
-
- dialtech = strsep(&args.dial, "/");
- ast_verb(3, "Dial Tech,String: (%s,%s)\n", dialtech, args.dial);
-
- if (!ast_strlen_zero(args.return_context)) {
- ast_clear_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
- ast_parseable_goto(chan, args.return_context);
- } else {
- ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
- }
-
- ast_verb(3, "Return Context: (%s,%s,%d) ID: %s\n", ast_channel_context(chan), ast_channel_exten(chan),
- ast_channel_priority(chan),
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""));
- if (!ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
- ast_verb(3, "Warning: Return Context Invalid, call will return to default|s\n");
- }
-
- /* Save the CallerID because the masquerade turns chan into a ZOMBIE. */
- ast_party_id_init(&caller_id);
- ast_channel_lock(chan);
- ast_party_id_copy(&caller_id, &ast_channel_caller(chan)->id);
- ast_channel_unlock(chan);
-
- /* we are using masq_park here to protect * from touching the channel once we park it. If the channel comes out of timeout
- before we are done announcing and the channel is messed with, Kablooeee. So we use Masq to prevent this. */
-
- res = ast_masq_park_call(chan, NULL, timeout, &lot);
- if (res) {
- /* Parking failed. */
- ast_party_id_free(&caller_id);
- res = -1;
- goto parkcleanup;
- }
-
- ast_verb(3, "Call parked in space: %d, timeout: %d, return-context: %s\n",
- lot, timeout, args.return_context ? args.return_context : "");
-
- /* Now place the call to the extension */
-
- snprintf(buf, sizeof(buf), "%d", lot);
- oh.parent_channel = chan;
- oh.vars = ast_variable_new("_PARKEDAT", buf, "");
- dchan = __ast_request_and_dial(dialtech, cap_slin, chan, args.dial, 30000,
- &outstate,
- S_COR(caller_id.number.valid, caller_id.number.str, NULL),
- S_COR(caller_id.name.valid, caller_id.name.str, NULL),
- &oh);
- ast_variables_destroy(oh.vars);
- ast_party_id_free(&caller_id);
- if (dchan) {
- if (ast_channel_state(dchan) == AST_STATE_UP) {
- ast_verb(4, "Channel %s was answered.\n", ast_channel_name(dchan));
- } else {
- ast_verb(4, "Channel %s was never answered.\n", ast_channel_name(dchan));
- ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", ast_channel_name(dchan));
- ast_hangup(dchan);
- res = -1;
- goto parkcleanup;
- }
- } else {
- ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
- res = -1;
- goto parkcleanup;
- }
-
- ast_stopstream(dchan);
-
- /* now we have the call placed and are ready to play stuff to it */
-
- ast_verb(4, "Announce Template:%s\n", args.template);
-
- for (looptemp = 0; looptemp < ARRAY_LEN(tmp); looptemp++) {
- if ((tmp[looptemp] = strsep(&args.template, ":")) != NULL)
- continue;
- else
- break;
- }
-
- for (i = 0; i < looptemp; i++) {
- ast_verb(4, "Announce:%s\n", tmp[i]);
- if (!strcmp(tmp[i], "PARKED")) {
- ast_say_digits(dchan, lot, "", ast_channel_language(dchan));
- } else {
- dres = ast_streamfile(dchan, tmp[i], ast_channel_language(dchan));
- if (!dres) {
- dres = ast_waitstream(dchan, "");
- } else {
- ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], ast_channel_name(dchan));
- }
- }
- }
-
- ast_stopstream(dchan);
- ast_hangup(dchan);
-
-parkcleanup:
- cap_slin = ast_format_cap_destroy(cap_slin);
-
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- /* return ast_register_application(app, park_exec); */
- return ast_register_application_xml(app, parkandannounce_exec);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Parking and Announce Application");
diff --git a/apps/app_queue.c b/apps/app_queue.c
index c63cd071e..8a96b64b2 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -106,6 +106,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cel.h"
#include "asterisk/data.h"
#include "asterisk/term.h"
+#include "asterisk/bridging.h"
/* Define, to debug reference counts on queues, without debugging reference counts on queue members */
/* #define REF_DEBUG_ONLY_QUEUES */
@@ -4912,6 +4913,7 @@ enum agent_complete_reason {
TRANSFER
};
+#if 0 // BUGBUG
/*! \brief Send out AMI message with member call completion status information */
static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
const struct ast_channel *peer, const struct member *member, time_t callstart,
@@ -4975,6 +4977,7 @@ static void send_agent_complete(const struct queue_ent *qe, const char *queuenam
(long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
}
+#endif // BUGBUG
struct queue_transfer_ds {
struct queue_ent *qe;
@@ -5029,6 +5032,7 @@ static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struc
}
}
+#if 0 // BUGBUG
/*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
*
* When a caller is atxferred, then the queue_transfer_info datastore
@@ -5041,6 +5045,7 @@ static int attended_transfer_occurred(struct ast_channel *chan)
{
return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
}
+#endif // BUGBUG
/*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
*/
@@ -5100,6 +5105,35 @@ static void end_bridge_callback(void *data)
/*!
* \internal
+ * \brief Setup the after bridge goto location on the peer.
+ * \since 12.0.0
+ *
+ * \param chan Calling channel for bridge.
+ * \param peer Peer channel for bridge.
+ * \param opts Dialing option flags.
+ * \param opt_args Dialing option argument strings.
+ *
+ * \return Nothing
+ */
+static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags *opts, char *opt_args[])
+{
+ const char *context;
+ const char *extension;
+ int priority;
+
+ if (ast_test_flag(opts, OPT_CALLEE_GO_ON)) {
+ ast_channel_lock(chan);
+ context = ast_strdupa(ast_channel_context(chan));
+ extension = ast_strdupa(ast_channel_exten(chan));
+ priority = ast_channel_priority(chan);
+ ast_channel_unlock(chan);
+ ast_after_bridge_set_go_on(peer, context, extension, priority,
+ opt_args[OPT_ARG_CALLEE_GO_ON]);
+ }
+}
+
+/*!
+ * \internal
* \brief A large function which calls members, updates statistics, and bridges the caller and a member
*
* Here is the process of this function
@@ -5128,7 +5162,7 @@ static void end_bridge_callback(void *data)
* \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
* \param[in] ringing 1 if the 'r' option is set, otherwise 0
*/
-static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
+static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
{
struct member *cur;
struct callattempt *outgoing = NULL; /* the list of calls we are building */
@@ -5200,9 +5234,6 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
if (ast_test_flag(&opts, OPT_CALLER_AUTOMON)) {
ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
}
- if (ast_test_flag(&opts, OPT_GO_ON)) {
- ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
- }
if (ast_test_flag(&opts, OPT_DATA_QUALITY)) {
nondataquality = 0;
}
@@ -5244,7 +5275,7 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
}
/* if the calling channel has AST_CAUSE_ANSWERED_ELSEWHERE set, make sure this is inherited.
- (this is mainly to support chan_local)
+ (this is mainly to support unreal/local channels)
*/
if (ast_channel_hangupcause(qe->chan) == AST_CAUSE_ANSWERED_ELSEWHERE) {
qe->cancel_answered_elsewhere = 1;
@@ -5437,11 +5468,6 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
}
}
} else { /* peer is valid */
- /* These variables are used with the F option without arguments (callee jumps to next priority after queue) */
- char *caller_context;
- char *caller_extension;
- int caller_priority;
-
/* Ah ha! Someone answered within the desired timeframe. Of course after this
we will always return with -1 so that it is hung up properly after the
conversation. */
@@ -5595,11 +5621,8 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
set_queue_variables(qe->parent, qe->chan);
set_queue_variables(qe->parent, peer);
+ setup_peer_after_bridge_goto(qe->chan, peer, &opts, opt_args);
ast_channel_lock(qe->chan);
- /* Copy next destination data for 'F' option (no args) */
- caller_context = ast_strdupa(ast_channel_context(qe->chan));
- caller_extension = ast_strdupa(ast_channel_exten(qe->chan));
- caller_priority = ast_channel_priority(qe->chan);
if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
monitorfilename = ast_strdupa(monitorfilename);
}
@@ -5883,6 +5906,8 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
bridge = ast_bridge_call(qe->chan, peer, &bridge_config);
+/* BUGBUG need to do this queue logging a different way because we cannot reference peer anymore. Likely needs to be made a subscriber of stasis transfer events. */
+#if 0 // BUGBUG
/* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
* when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for
* the AgentComplete manager event
@@ -5917,28 +5942,12 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
/* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */
send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
}
+#endif // BUGBUG
if (transfer_ds) {
ast_datastore_free(transfer_ds);
}
- if (!ast_check_hangup(peer) && ast_test_flag(&opts, OPT_CALLEE_GO_ON)) {
- int goto_res;
-
- if (!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
- ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
- goto_res = ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
- } else { /* F() */
- goto_res = ast_goto_if_exists(peer, caller_context, caller_extension,
- caller_priority + 1);
- }
- if (goto_res || ast_pbx_start(peer)) {
- ast_autoservice_chan_hangup_peer(qe->chan, peer);
- }
- } else {
- ast_autoservice_chan_hangup_peer(qe->chan, peer);
- }
-
res = bridge ? bridge : 1;
ao2_ref(member, -1);
}
diff --git a/apps/confbridge/conf_chan_announce.c b/apps/confbridge/conf_chan_announce.c
new file mode 100644
index 000000000..46e074b20
--- /dev/null
+++ b/apps/confbridge/conf_chan_announce.c
@@ -0,0 +1,207 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013 Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief ConfBridge announcer channel driver
+ *
+ * \author Richard Mudgett <rmudgett@digium.com>
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
+ */
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/bridging.h"
+#include "asterisk/core_unreal.h"
+#include "include/confbridge.h"
+
+/* ------------------------------------------------------------------- */
+
+/*! ConfBridge announcer channel private. */
+struct announce_pvt {
+ /*! Unreal channel driver base class values. */
+ struct ast_unreal_pvt base;
+ /*! Conference bridge associated with this announcer. */
+ struct ast_bridge *bridge;
+};
+
+static int announce_call(struct ast_channel *chan, const char *addr, int timeout)
+{
+ /* Make sure anyone calling ast_call() for this channel driver is going to fail. */
+ return -1;
+}
+
+static int announce_hangup(struct ast_channel *ast)
+{
+ struct announce_pvt *p = ast_channel_tech_pvt(ast);
+ int res;
+
+ if (!p) {
+ return -1;
+ }
+
+ /* give the pvt a ref to fulfill calling requirements. */
+ ao2_ref(p, +1);
+ res = ast_unreal_hangup(&p->base, ast);
+ ao2_ref(p, -1);
+
+ return res;
+}
+
+static void announce_pvt_destructor(void *vdoomed)
+{
+ struct announce_pvt *doomed = vdoomed;
+
+ ao2_cleanup(doomed->bridge);
+ doomed->bridge = NULL;
+}
+
+static struct ast_channel *announce_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
+{
+ struct ast_channel *chan;
+ const char *conf_name = data;
+ RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
+ RAII_VAR(struct announce_pvt *, pvt, NULL, ao2_cleanup);
+
+ conference = ao2_find(conference_bridges, conf_name, OBJ_KEY);
+ if (!conference) {
+ return NULL;
+ }
+ ast_assert(conference->bridge != NULL);
+
+ /* Allocate a new private structure and then Asterisk channels */
+ pvt = (struct announce_pvt *) ast_unreal_alloc(sizeof(*pvt), announce_pvt_destructor,
+ cap);
+ if (!pvt) {
+ return NULL;
+ }
+ ast_set_flag(&pvt->base, AST_UNREAL_NO_OPTIMIZATION);
+ ast_copy_string(pvt->base.name, conf_name, sizeof(pvt->base.name));
+ pvt->bridge = conference->bridge;
+ ao2_ref(pvt->bridge, +1);
+
+ chan = ast_unreal_new_channels(&pvt->base, conf_announce_get_tech(),
+ AST_STATE_UP, AST_STATE_UP, NULL, NULL, requestor, NULL);
+ if (chan) {
+ ast_answer(pvt->base.owner);
+ ast_answer(pvt->base.chan);
+ if (ast_channel_add_bridge_role(pvt->base.chan, "announcer")) {
+ ast_hangup(chan);
+ chan = NULL;
+ }
+ }
+
+ return chan;
+}
+
+static struct ast_channel_tech announce_tech = {
+ .type = "CBAnn",
+ .description = "Conference Bridge Announcing Channel",
+ .requester = announce_request,
+ .call = announce_call,
+ .hangup = announce_hangup,
+
+ .send_digit_begin = ast_unreal_digit_begin,
+ .send_digit_end = ast_unreal_digit_end,
+ .read = ast_unreal_read,
+ .write = ast_unreal_write,
+ .write_video = ast_unreal_write,
+ .exception = ast_unreal_read,
+ .indicate = ast_unreal_indicate,
+ .fixup = ast_unreal_fixup,
+ .send_html = ast_unreal_sendhtml,
+ .send_text = ast_unreal_sendtext,
+ .queryoption = ast_unreal_queryoption,
+ .setoption = ast_unreal_setoption,
+};
+
+struct ast_channel_tech *conf_announce_get_tech(void)
+{
+ return &announce_tech;
+}
+
+void conf_announce_channel_depart(struct ast_channel *chan)
+{
+ struct announce_pvt *p = ast_channel_tech_pvt(chan);
+
+ if (!p) {
+ return;
+ }
+
+ ao2_ref(p, +1);
+ ao2_lock(p);
+ if (!ast_test_flag(&p->base, AST_UNREAL_CARETAKER_THREAD)) {
+ ao2_unlock(p);
+ ao2_ref(p, -1);
+ return;
+ }
+ ast_clear_flag(&p->base, AST_UNREAL_CARETAKER_THREAD);
+ chan = p->base.chan;
+ if (chan) {
+ ast_channel_ref(chan);
+ }
+ ao2_unlock(p);
+ ao2_ref(p, -1);
+ if (chan) {
+ ast_bridge_depart(chan);
+ ast_channel_unref(chan);
+ }
+}
+
+int conf_announce_channel_push(struct ast_channel *ast)
+{
+ struct ast_bridge_features *features;
+ RAII_VAR(struct announce_pvt *, p, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_channel *, chan, NULL, ast_channel_unref);
+
+ {
+ SCOPED_CHANNELLOCK(lock, ast);
+
+ p = ast_channel_tech_pvt(ast);
+ if (!p) {
+ return -1;
+ }
+ ao2_ref(p, +1);
+ chan = p->base.chan;
+ if (!chan) {
+ return -1;
+ }
+ ast_channel_ref(chan);
+ }
+
+ features = ast_bridge_features_new();
+ if (!features) {
+ return -1;
+ }
+ ast_set_flag(&features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
+
+ /* Impart the output channel into the bridge */
+ if (ast_bridge_impart(p->bridge, chan, NULL, features, 0)) {
+ return -1;
+ }
+ ao2_lock(p);
+ ast_set_flag(&p->base, AST_UNREAL_CARETAKER_THREAD);
+ ao2_unlock(p);
+ return 0;
+}
diff --git a/apps/confbridge/conf_chan_record.c b/apps/confbridge/conf_chan_record.c
new file mode 100644
index 000000000..18f971f35
--- /dev/null
+++ b/apps/confbridge/conf_chan_record.c
@@ -0,0 +1,94 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013 Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief ConfBridge recorder channel driver
+ *
+ * \author Richard Mudgett <rmudgett@digium.com>
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
+ */
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/bridging.h"
+#include "include/confbridge.h"
+
+/* ------------------------------------------------------------------- */
+
+static int rec_call(struct ast_channel *chan, const char *addr, int timeout)
+{
+ /* Make sure anyone calling ast_call() for this channel driver is going to fail. */
+ return -1;
+}
+
+static struct ast_frame *rec_read(struct ast_channel *ast)
+{
+ return &ast_null_frame;
+}
+
+static int rec_write(struct ast_channel *ast, struct ast_frame *f)
+{
+ return 0;
+}
+
+static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
+{
+ struct ast_channel *chan;
+ struct ast_format format;
+ const char *conf_name = data;
+
+ chan = ast_channel_alloc(1, AST_STATE_UP, NULL, NULL, NULL, NULL, NULL, NULL, 0,
+ "CBRec/conf-%s-uid-%d",
+ conf_name, (int) ast_random());
+ if (!chan) {
+ return NULL;
+ }
+ if (ast_channel_add_bridge_role(chan, "recorder")) {
+ ast_channel_release(chan);
+ return NULL;
+ }
+ ast_format_set(&format, AST_FORMAT_SLINEAR, 0);
+ ast_channel_tech_set(chan, conf_record_get_tech());
+ ast_format_cap_add_all(ast_channel_nativeformats(chan));
+ ast_format_copy(ast_channel_writeformat(chan), &format);
+ ast_format_copy(ast_channel_rawwriteformat(chan), &format);
+ ast_format_copy(ast_channel_readformat(chan), &format);
+ ast_format_copy(ast_channel_rawreadformat(chan), &format);
+ return chan;
+}
+
+static struct ast_channel_tech record_tech = {
+ .type = "CBRec",
+ .description = "Conference Bridge Recording Channel",
+ .requester = rec_request,
+ .call = rec_call,
+ .read = rec_read,
+ .write = rec_write,
+};
+
+struct ast_channel_tech *conf_record_get_tech(void)
+{
+ return &record_tech;
+}
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index 1bca2d5c2..6cec25522 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -1924,6 +1924,7 @@ int conf_load_config(int reload)
/* This option should only be used with the CONFBRIDGE dialplan function */
aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
+/* BUGBUG need a user supplied bridge merge_priority to merge ConfBridges (default = 1, range 1-INT_MAX) */
/* Bridge options */
aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
@@ -2156,7 +2157,8 @@ int conf_set_menu_to_user(const char *menu_name, struct confbridge_user *user)
ao2_ref(menu, +1);
pvt->menu = menu;
- ast_bridge_features_hook(&user->features, pvt->menu_entry.dtmf, menu_hook_callback, pvt, menu_hook_destroy);
+ ast_bridge_dtmf_hook(&user->features, pvt->menu_entry.dtmf, menu_hook_callback,
+ pvt, menu_hook_destroy, 0);
}
ao2_unlock(menu);
diff --git a/apps/confbridge/confbridge_manager.c b/apps/confbridge/confbridge_manager.c
new file mode 100644
index 000000000..56fedb98e
--- /dev/null
+++ b/apps/confbridge/confbridge_manager.c
@@ -0,0 +1,339 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Jonathan Rose <jrose@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Confbridge manager events for stasis messages
+ *
+ * \author Jonathan Rose <jrose@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/bridging.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/stasis_bridging.h"
+#include "asterisk/manager.h"
+#include "asterisk/stasis_message_router.h"
+#include "include/confbridge.h"
+
+/*** DOCUMENTATION
+ <managerEvent language="en_US" name="ConfbridgeStart">
+ <managerEventInstance class="EVENT_FLAG_CALL">
+ <synopsis>Raised when a conference starts.</synopsis>
+ <syntax>
+ <parameter name="Conference">
+ <para>The name of the Confbridge conference.</para>
+ </parameter>
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+ </syntax>
+ <see-also>
+ <ref type="managerEvent">ConfbridgeEnd</ref>
+ <ref type="application">ConfBridge</ref>
+ </see-also>
+ </managerEventInstance>
+ </managerEvent>
+ <managerEvent language="en_US" name="ConfbridgeEnd">
+ <managerEventInstance class="EVENT_FLAG_CALL">
+ <synopsis>Raised when a conference ends.</synopsis>
+ <syntax>
+ <parameter name="Conference">
+ <para>The name of the Confbridge conference.</para>
+ </parameter>
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+ </syntax>
+ <see-also>
+ <ref type="managerEvent">ConfbridgeStart</ref>
+ <ref type="application">ConfBridge</ref>
+ </see-also>
+ </managerEventInstance>
+ </managerEvent>
+ <managerEvent language="en_US" name="ConfbridgeJoin">
+ <managerEventInstance class="EVENT_FLAG_CALL">
+ <synopsis>Raised when a channel joins a Confbridge conference.</synopsis>
+ <syntax>
+ <parameter name="Conference">
+ <para>The name of the Confbridge conference.</para>
+ </parameter>
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+ </syntax>
+ <see-also>
+ <ref type="managerEvent">ConfbridgeLeave</ref>
+ <ref type="application">ConfBridge</ref>
+ </see-also>
+ </managerEventInstance>
+ </managerEvent>
+ <managerEvent language="en_US" name="ConfbridgeLeave">
+ <managerEventInstance class="EVENT_FLAG_CALL">
+ <synopsis>Raised when a channel leaves a Confbridge conference.</synopsis>
+ <syntax>
+ <parameter name="Conference">
+ <para>The name of the Confbridge conference.</para>
+ </parameter>
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+ </syntax>
+ <see-also>
+ <ref type="managerEvent">ConfbridgeJoin</ref>
+ <ref type="application">ConfBridge</ref>
+ </see-also>
+ </managerEventInstance>
+ </managerEvent>
+ <managerEvent language="en_US" name="ConfbridgeRecord">
+ <managerEventInstance class="EVENT_FLAG_CALL">
+ <synopsis>Raised when a conference starts recording.</synopsis>
+ <syntax>
+ <parameter name="Conference">
+ <para>The name of the Confbridge conference.</para>
+ </parameter>
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+ </syntax>
+ <see-also>
+ <ref type="managerEvent">ConfbridgeStopRecord</ref>
+ <ref type="application">ConfBridge</ref>
+ </see-also>
+ </managerEventInstance>
+ </managerEvent>
+ <managerEvent language="en_US" name="ConfbridgeStopRecord">
+ <managerEventInstance class="EVENT_FLAG_CALL">
+ <synopsis>Raised when a conference that was recording stops recording.</synopsis>
+ <syntax>
+ <parameter name="Conference">
+ <para>The name of the Confbridge conference.</para>
+ </parameter>
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+ </syntax>
+ <see-also>
+ <ref type="managerEvent">ConfbridgeRecord</ref>
+ <ref type="application">ConfBridge</ref>
+ </see-also>
+ </managerEventInstance>
+ </managerEvent>
+ <managerEvent language="en_US" name="ConfbridgeMute">
+ <managerEventInstance class="EVENT_FLAG_CALL">
+ <synopsis>Raised when a Confbridge participant mutes.</synopsis>
+ <syntax>
+ <parameter name="Conference">
+ <para>The name of the Confbridge conference.</para>
+ </parameter>
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+ </syntax>
+ <see-also>
+ <ref type="managerEvent">ConfbridgeUnmute</ref>
+ <ref type="application">ConfBridge</ref>
+ </see-also>
+ </managerEventInstance>
+ </managerEvent>
+ <managerEvent language="en_US" name="ConfbridgeUnmute">
+ <managerEventInstance class="EVENT_FLAG_CALL">
+ <synopsis>Raised when a confbridge participant unmutes.</synopsis>
+ <syntax>
+ <parameter name="Conference">
+ <para>The name of the Confbridge conference.</para>
+ </parameter>
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+ </syntax>
+ <see-also>
+ <ref type="managerEvent">ConfbridgeMute</ref>
+ <ref type="application">ConfBridge</ref>
+ </see-also>
+ </managerEventInstance>
+ </managerEvent>
+
+ <managerEvent language="en_US" name="ConfbridgeTalking">
+ <managerEventInstance class="EVENT_FLAG_CALL">
+ <synopsis>Raised when a confbridge participant unmutes.</synopsis>
+ <syntax>
+ <parameter name="Conference">
+ <para>The name of the Confbridge conference.</para>
+ </parameter>
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+ <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+ <parameter name="TalkingStatus">
+ <enumlist>
+ <enum name="on"/>
+ <enum name="off"/>
+ </enumlist>
+ </parameter>
+ </syntax>
+ <see-also>
+ <ref type="application">ConfBridge</ref>
+ </see-also>
+ </managerEventInstance>
+ </managerEvent>
+***/
+
+static struct stasis_message_router *bridge_state_router;
+static struct stasis_message_router *channel_state_router;
+
+static void append_event_header(struct ast_str **fields_string,
+ const char *header, const char *value)
+{
+ struct ast_str *working_str = *fields_string;
+
+ if (!working_str) {
+ working_str = ast_str_create(128);
+ if (!working_str) {
+ return;
+ }
+ *fields_string = working_str;
+ }
+
+ ast_str_append(&working_str, 0,
+ "%s: %s\r\n",
+ header, value);
+}
+
+static void stasis_confbridge_cb(void *data, struct stasis_subscription *sub,
+ struct stasis_topic *topic,
+ struct stasis_message *message)
+{
+ struct ast_bridge_blob *blob = stasis_message_data(message);
+ const char *type = ast_bridge_blob_json_type(blob);
+ const char *conference_name;
+ RAII_VAR(struct ast_str *, bridge_text, NULL, ast_free);
+ RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
+ RAII_VAR(struct ast_str *, extra_text, NULL, ast_free);
+ char *event;
+
+ if (!blob || !type) {
+ ast_assert(0);
+ return;
+ }
+
+ if (!strcmp("confbridge_start", type)) {
+ event = "ConfbridgeStart";
+ } else if (!strcmp("confbridge_end", type)) {
+ event = "ConfbridgeEnd";
+ } else if (!strcmp("confbridge_leave", type)) {
+ event = "ConfbridgeLeave";
+ } else if (!strcmp("confbridge_join", type)) {
+ event = "ConfbridgeJoin";
+ } else if (!strcmp("confbridge_record", type)) {
+ event = "ConfbridgeRecord";
+ } else if (!strcmp("confbridge_stop_record", type)) {
+ event = "ConfbridgeStopRecord";
+ } else if (!strcmp("confbridge_mute", type)) {
+ event = "ConfbridgeMute";
+ } else if (!strcmp("confbridge_unmute", type)) {
+ event = "ConfbridgeUnmute";
+ } else if (!strcmp("confbridge_talking", type)) {
+ const char *talking_status = ast_json_string_get(ast_json_object_get(blob->blob, "talking_status"));
+ event = "ConfbridgeTalking";
+
+ if (!talking_status) {
+ return;
+ }
+
+ append_event_header(&extra_text, "TalkingStatus", talking_status);
+
+ } else {
+ return;
+ }
+
+ conference_name = ast_json_string_get(ast_json_object_get(blob->blob, "conference"));
+
+ if (!conference_name) {
+ ast_assert(0);
+ return;
+ }
+
+ bridge_text = ast_manager_build_bridge_state_string(blob->bridge, "");
+ if (blob->channel) {
+ channel_text = ast_manager_build_channel_state_string(blob->channel);
+ }
+
+ manager_event(EVENT_FLAG_CALL, event,
+ "Conference: %s\r\n"
+ "%s"
+ "%s"
+ "%s",
+ conference_name,
+ ast_str_buffer(bridge_text),
+ channel_text ? ast_str_buffer(channel_text) : "",
+ extra_text ? ast_str_buffer(extra_text) : "");
+}
+
+static struct stasis_message_type *confbridge_msg_type;
+
+struct stasis_message_type *confbridge_message_type(void)
+{
+ return confbridge_msg_type;
+}
+
+void manager_confbridge_shutdown(void) {
+ ao2_cleanup(confbridge_msg_type);
+ confbridge_msg_type = NULL;
+
+ if (bridge_state_router) {
+ stasis_message_router_unsubscribe(bridge_state_router);
+ bridge_state_router = NULL;
+ }
+
+ if (channel_state_router) {
+ stasis_message_router_unsubscribe(channel_state_router);
+ channel_state_router = NULL;
+ }
+}
+
+int manager_confbridge_init(void)
+{
+ if (!(confbridge_msg_type = stasis_message_type_create("confbridge"))) {
+ return -1;
+ }
+
+ bridge_state_router = stasis_message_router_create(
+ stasis_caching_get_topic(ast_bridge_topic_all_cached()));
+
+ if (!bridge_state_router) {
+ return -1;
+ }
+
+ if (stasis_message_router_add(bridge_state_router,
+ confbridge_message_type(),
+ stasis_confbridge_cb,
+ NULL)) {
+ manager_confbridge_shutdown();
+ return -1;
+ }
+
+ channel_state_router = stasis_message_router_create(
+ stasis_caching_get_topic(ast_channel_topic_all_cached()));
+
+ if (!channel_state_router) {
+ manager_confbridge_shutdown();
+ return -1;
+ }
+
+ if (stasis_message_router_add(channel_state_router,
+ confbridge_message_type(),
+ stasis_confbridge_cb,
+ NULL)) {
+ manager_confbridge_shutdown();
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index 245561376..f0620149a 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -222,6 +222,8 @@ struct confbridge_conference {
AST_LIST_HEAD_NOLOCK(, confbridge_user) waiting_list; /*!< List of users waiting to join the conference bridge */
};
+extern struct ao2_container *conference_bridges;
+
struct post_join_action {
int (*func)(struct confbridge_user *user);
AST_LIST_ENTRY(post_join_action) list;
@@ -460,4 +462,65 @@ void conf_remove_user_waiting(struct confbridge_conference *conference, struct c
* \retval non-zero failure
*/
int conf_add_post_join_action(struct confbridge_user *user, int (*func)(struct confbridge_user *user));
+
+/*!
+ * \since 12.0
+ * \brief get the confbridge stasis message type
+ *
+ * \retval stasis message type for confbridge messages if it's available
+ * \retval NULL if it isn't
+ */
+struct stasis_message_type *confbridge_message_type(void);
+
+/*!
+ * \since 12.0
+ * \brief register stasis message routers to handle manager events for confbridge messages
+ *
+ * \retval 0 success
+ * \retval non-zero failure
+ */
+int manager_confbridge_init(void);
+
+/*!
+ * \since 12.0
+ * \brief unregister stasis message routers to handle manager events for confbridge messages
+ */
+void manager_confbridge_shutdown(void);
+
+/*!
+ * \brief Get ConfBridge record channel technology struct.
+ * \since 12.0.0
+ *
+ * \return ConfBridge record channel technology.
+ */
+struct ast_channel_tech *conf_record_get_tech(void);
+
+/*!
+ * \brief Get ConfBridge announce channel technology struct.
+ * \since 12.0.0
+ *
+ * \return ConfBridge announce channel technology.
+ */
+struct ast_channel_tech *conf_announce_get_tech(void);
+
+/*!
+ * \brief Remove the announcer channel from the conference.
+ * \since 12.0.0
+ *
+ * \param chan Either channel in the announcer channel pair.
+ *
+ * \return Nothing
+ */
+void conf_announce_channel_depart(struct ast_channel *chan);
+
+/*!
+ * \brief Push the announcer channel into the conference.
+ * \since 12.0.0
+ *
+ * \param ast Either channel in the announcer channel pair.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int conf_announce_channel_push(struct ast_channel *ast);
#endif