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
This commit is contained in:
Richard Mudgett
2013-05-21 18:00:22 +00:00
parent e1e1cc2dee
commit 3d63833bd6
99 changed files with 19717 additions and 7682 deletions

View File

@@ -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)
static void send_conf_stasis(struct confbridge_conference *conference, struct ast_channel *chan, const char *type, struct ast_json *extras, int channel_topic)
{
/*** 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);
}
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
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);
}
json_object = ast_json_pack("{s: s, s: s}",
"type", type,
"conference", conference->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_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_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_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 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);
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;
if (!json_object) {
return;
}
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;
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_conf_start_event(struct confbridge_conference *conference)
{
send_conf_stasis(conference, NULL, "confbridge_start", NULL, 0);
}
static void send_conf_end_event(struct confbridge_conference *conference)
{
send_conf_stasis(conference, NULL, "confbridge_end", NULL, 0);
}
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 void send_start_record_event(struct confbridge_conference *conference)
{
send_conf_stasis(conference, NULL, "confbridge_record", NULL, 0);
}
static void send_stop_record_event(struct confbridge_conference *conference)
{
send_conf_stasis(conference, NULL, "confbridge_stop_record", NULL, 0);
}
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));
if (!(conference->record_chan = ast_request("ConfBridgeRec", cap, NULL, conference->name, &cause))) {
cap = ast_format_cap_destroy(cap);
return -1;
}
ast_format_cap_add(cap, ast_format_set(&format, AST_FORMAT_SLINEAR, 0));
conference->record_chan = ast_request("CBRec", cap, NULL,
conference->name, NULL);
cap = ast_format_cap_destroy(cap);
if (!conference->record_chan) {
return -1;
}
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. */
if (!conference) {
return;
}
/* 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");
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);