From 22eaabbc4e86b68812efebd2db4e08518d38f8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Thu, 2 Jun 2016 09:22:03 +0300 Subject: [PATCH] Implement deafened mode for confbridge ASTERISK-19109 --- CHANGES | 4 + apps/app_confbridge.c | 252 +++++++++++++++++++++++++++++---- apps/confbridge/conf_config_parser.c | 14 ++ apps/confbridge/confbridge_manager.c | 78 ++++++++++ apps/confbridge/include/confbridge.h | 8 ++ bridges/bridge_softmix.c | 24 ++-- configs/samples/confbridge.conf.sample | 5 + include/asterisk/bridge_features.h | 2 + main/bridge_channel.c | 17 ++- 9 files changed, 369 insertions(+), 35 deletions(-) diff --git a/CHANGES b/CHANGES index 382c4a4..435c69b 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,10 @@ === ============================================================================== + + * Added support for deaf participants with CLI commands, manager actions + and ConfBridge DTMF actions to toggle the deaf state. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13.8.0 to Asterisk 13.9.0 ------------ ------------------------------------------------------------------------------ diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c index 55b7b12..363d88c 100644 --- a/apps/app_confbridge.c +++ b/apps/app_confbridge.c @@ -272,6 +272,30 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + + + Deafen a Confbridge user. + + + + + + + + + + + + Undeafen a Confbridge user. + + + + + + + + + Kick a Confbridge user. @@ -368,6 +392,13 @@ static const char app[] = "ConfBridge"; /*! Initial recording filename space. */ #define RECORD_FILENAME_INITIAL_SPACE 128 +enum confbridge_feature_action { + CONFBRIDGE_FEATURE_MUTE, + CONFBRIDGE_FEATURE_UNMUTE, + CONFBRIDGE_FEATURE_DEAFEN, + CONFBRIDGE_FEATURE_UNDEAFEN +}; + /*! \brief Container to hold all conference bridges in progress */ struct ao2_container *conference_bridges; @@ -439,6 +470,10 @@ const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds return S_OR(custom_sounds->muted, "conf-muted"); case CONF_SOUND_UNMUTED: return S_OR(custom_sounds->unmuted, "conf-unmuted"); + case CONF_SOUND_DEAFENED: + return S_OR(custom_sounds->deafened, "conf-deafened"); + case CONF_SOUND_UNDEAFENED: + return S_OR(custom_sounds->undeafened, "conf-undeafened"); case CONF_SOUND_ONLY_ONE: return S_OR(custom_sounds->onlyone, "conf-onlyone"); case CONF_SOUND_THERE_ARE: @@ -589,6 +624,16 @@ static void send_unmute_event(struct confbridge_user *user, struct confbridge_co ast_json_unref(json_object); } +static void send_deafen_event(struct confbridge_user *user, struct confbridge_conference *conference) +{ + send_conf_stasis(conference, user->chan, confbridge_deafen_type(), NULL, 1); +} + +static void send_undeafen_event(struct confbridge_user *user, struct confbridge_conference *conference) +{ + send_conf_stasis(conference, user->chan, confbridge_undeafen_type(), NULL, 1); +} + static void set_rec_filename(struct confbridge_conference *conference, struct ast_str **filename, int is_new) { char *rec_file = conference->b_profile.rec_file; @@ -1088,26 +1133,54 @@ void conf_update_user_mute(struct confbridge_user *user) /* * \internal - * \brief Mute/unmute a single user. + * \brief Send feature change test event. */ -static void generic_mute_unmute_user(struct confbridge_conference *conference, struct confbridge_user *user, int mute) +static void test_suite_feature_action_event_notify( + struct confbridge_conference *conference, + struct confbridge_user *user, + const char *state, const char *verb) { - /* Set user level mute request. */ - user->muted = mute ? 1 : 0; - - conf_update_user_mute(user); - ast_test_suite_event_notify("CONF_MUTE", + ast_test_suite_event_notify(state, "Message: participant %s %s\r\n" "Conference: %s\r\n" "Channel: %s", ast_channel_name(user->chan), - mute ? "muted" : "unmuted", + verb, conference->b_profile.name, ast_channel_name(user->chan)); - if (mute) { +} + +/* \internal + * \brief Change a feature for one conference user. + */ +static void generic_feature_action_helper_user( + struct confbridge_conference *conference, struct confbridge_user *user, + enum confbridge_feature_action action) +{ + switch (action) { + case CONFBRIDGE_FEATURE_DEAFEN: + user->features.deaf = 1; + test_suite_feature_action_event_notify(conference, user, "CONF_DEAF", "deafened"); + send_deafen_event(user, conference); + break; + case CONFBRIDGE_FEATURE_UNDEAFEN: + user->features.deaf = 0; + test_suite_feature_action_event_notify(conference, user, "CONF_DEAF", "undeafened"); + send_undeafen_event(user, conference); + break; + case CONFBRIDGE_FEATURE_MUTE: + user->muted = 1; + conf_update_user_mute(user); + test_suite_feature_action_event_notify(conference, user, "CONF_MUTE", "muted"); send_mute_event(user, conference); - } else { + break; + case CONFBRIDGE_FEATURE_UNMUTE: + default: + user->muted = 0; + conf_update_user_mute(user); + test_suite_feature_action_event_notify(conference, user, "CONF_MUTE", "unmuted"); send_unmute_event(user, conference); + break; } } @@ -1766,6 +1839,11 @@ static int confbridge_exec(struct ast_channel *chan, const char *data) if (args.argc > 3 && !ast_strlen_zero(args.menu_profile_name)) { menu_profile_name = args.menu_profile_name; } + /* If the caller should be joined already deafened, set the flag before we join. */ + if (ast_test_flag(&user.u_profile, USER_OPT_STARTDEAF)) { + /* Set user level deaf request */ + user.features.deaf = 1; + } if (conf_set_menu_to_user(chan, &user, menu_profile_name)) { pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED"); @@ -1995,6 +2073,30 @@ static int action_toggle_mute_participants(struct confbridge_conference *confere return 0; } +static int action_toggle_deaf(struct confbridge_conference *conference, + struct confbridge_user *user, struct ast_channel *chan) +{ + /* Deafen or undeafen yourself */ + user->features.deaf = !user->features.deaf; + ast_test_suite_event_notify("CONF_DEAF", + "Message: participant %s %s\r\n" + "Conference: %s\r\n" + "Channel: %s", + ast_channel_name(chan), + user->features.deaf ? "deafened" : "undeafened", + user->b_profile.name, + ast_channel_name(chan)); + if (user->features.deaf) { + send_deafen_event(user, conference); + } else { + send_undeafen_event(user, conference); + } + return ast_stream_and_wait(chan, (user->features.deaf ? + conf_get_sound(CONF_SOUND_DEAFENED, user->b_profile.sounds) : + conf_get_sound(CONF_SOUND_UNDEAFENED, user->b_profile.sounds)), + ""); +} + static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file) { char *file_copy = ast_strdupa(playback_file); @@ -2180,6 +2282,11 @@ static int execute_menu_entry(struct confbridge_conference *conference, case MENU_ACTION_PARTICIPANT_COUNT: announce_user_count(conference, user, bridge_channel); break; + case MENU_ACTION_TOGGLE_DEAF: + res |= action_toggle_deaf(conference, + user, + bridge_channel->chan); + break; case MENU_ACTION_PLAYBACK: if (!stop_prompts) { res |= action_playback(bridge_channel, menu_action->data.playback_file); @@ -2567,14 +2674,14 @@ static int generic_lock_unlock_helper(int lock, const char *conference_name) } /* \internal - * \brief finds a conference user by channel name and mutes/unmutes them. + * \brief finds a conference user by channel name and changes feature bits on it. * * \retval 0 success * \retval -1 conference not found * \retval -2 user not found */ -static int generic_mute_unmute_helper(int mute, const char *conference_name, - const char *chan_name) +static int generic_feature_action_helper(enum confbridge_feature_action action, + const char *conference_name, const char *chan_name) { RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup); struct confbridge_user *user; @@ -2594,7 +2701,7 @@ static int generic_mute_unmute_helper(int mute, const char *conference_name, strlen(chan_name)); if (match || all || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) { - generic_mute_unmute_user(conference, user, mute); + generic_feature_action_helper_user(conference, user, action); res = 0; if (match) { return res; @@ -2607,7 +2714,7 @@ static int generic_mute_unmute_helper(int mute, const char *conference_name, strlen(chan_name)); if (match || all || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) { - generic_mute_unmute_user(conference, user, mute); + generic_feature_action_helper_user(conference, user, action); res = 0; if (match) { return res; @@ -2619,9 +2726,10 @@ static int generic_mute_unmute_helper(int mute, const char *conference_name, return res; } -static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a) +static int cli_feature_action_helper(enum confbridge_feature_action action, struct ast_cli_args *a) { - int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]); + const char *verb; + int res = generic_feature_action_helper(action, a->argv[2], a->argv[3]); if (res == -1) { ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]); @@ -2634,7 +2742,24 @@ static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a) } return -1; } - ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]); + + switch (action) { + case CONFBRIDGE_FEATURE_DEAFEN: + verb = "Deafening"; + break; + case CONFBRIDGE_FEATURE_UNDEAFEN: + verb = "Undeafening"; + break; + case CONFBRIDGE_FEATURE_MUTE: + verb = "Muting"; + break; + case CONFBRIDGE_FEATURE_UNMUTE: + default: + verb = "Unmuting"; + break; + } + + ast_cli(a->fd, "%s %s from confbridge %s\n", verb, a->argv[3], a->argv[2]); return 0; } @@ -2664,7 +2789,7 @@ static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct return CLI_SHOWUSAGE; } - cli_mute_unmute_helper(1, a); + cli_feature_action_helper(CONFBRIDGE_FEATURE_MUTE, a); return CLI_SUCCESS; } @@ -2695,7 +2820,53 @@ static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, stru return CLI_SHOWUSAGE; } - cli_mute_unmute_helper(0, a); + cli_feature_action_helper(CONFBRIDGE_FEATURE_UNMUTE, a); + + return CLI_SUCCESS; +} + +static char *handle_cli_confbridge_deafen(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "confbridge deafen"; + e->usage = + "Usage: confbridge deafen \n"; + return NULL; + case CLI_GENERATE: + if (a->pos == 2) { + return complete_confbridge_name(a->line, a->word, a->pos, a->n); + } + return NULL; + } + if (a->argc != 4) { + return CLI_SHOWUSAGE; + } + + cli_feature_action_helper(CONFBRIDGE_FEATURE_DEAFEN, a); + + return CLI_SUCCESS; +} + +static char *handle_cli_confbridge_undeafen(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "confbridge undeafen"; + e->usage = + "Usage: confbridge undeafen \n"; + return NULL; + case CLI_GENERATE: + if (a->pos == 2) { + return complete_confbridge_name(a->line, a->word, a->pos, a->n); + } + return NULL; + } + if (a->argc != 4) { + return CLI_SHOWUSAGE; + } + + cli_feature_action_helper(CONFBRIDGE_FEATURE_UNDEAFEN, a); return CLI_SUCCESS; } @@ -2850,6 +3021,8 @@ static struct ast_cli_entry cli_confbridge[] = { AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."), AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute participants."), AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute participants."), + AST_CLI_DEFINE(handle_cli_confbridge_deafen, "Deafen a participant."), + AST_CLI_DEFINE(handle_cli_confbridge_undeafen, "Undeafen a participant."), AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."), AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."), AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"), @@ -2996,10 +3169,11 @@ static int action_confbridgelistrooms(struct mansession *s, const struct message return 0; } -static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute) +static int action_feature_action_helper(struct mansession *s, const struct message *m, enum confbridge_feature_action action) { const char *conference_name = astman_get_header(m, "Conference"); const char *channel_name = astman_get_header(m, "Channel"); + char *verb; int res = 0; if (ast_strlen_zero(conference_name)) { @@ -3015,7 +3189,7 @@ static int action_mute_unmute_helper(struct mansession *s, const struct message return 0; } - res = generic_mute_unmute_helper(mute, conference_name, channel_name); + res = generic_feature_action_helper(action, conference_name, channel_name); if (res == -1) { astman_send_error(s, m, "No Conference by that name found."); @@ -3025,17 +3199,41 @@ static int action_mute_unmute_helper(struct mansession *s, const struct message return 0; } - astman_send_ack(s, m, mute ? "User muted" : "User unmuted"); + switch (action) { + case CONFBRIDGE_FEATURE_DEAFEN: + verb = "User deafened"; + break; + case CONFBRIDGE_FEATURE_UNDEAFEN: + verb = "User undeafened"; + break; + case CONFBRIDGE_FEATURE_MUTE: + verb = "User muted"; + break; + case CONFBRIDGE_FEATURE_UNMUTE: + default: + verb = "User unmuted"; + break; + } + + astman_send_ack(s, m, verb); return 0; } static int action_confbridgeunmute(struct mansession *s, const struct message *m) { - return action_mute_unmute_helper(s, m, 0); + return action_feature_action_helper(s, m, CONFBRIDGE_FEATURE_UNMUTE); } static int action_confbridgemute(struct mansession *s, const struct message *m) { - return action_mute_unmute_helper(s, m, 1); + return action_feature_action_helper(s, m, CONFBRIDGE_FEATURE_MUTE); +} +static int action_confbridgeundeafen(struct mansession *s, const struct message *m) +{ + return action_feature_action_helper(s, m, CONFBRIDGE_FEATURE_UNDEAFEN); +} +static int action_confbridgedeafen(struct mansession *s, const struct message *m) +{ + return action_feature_action_helper(s, m, CONFBRIDGE_FEATURE_DEAFEN); } static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock) @@ -3389,6 +3587,8 @@ static int unload_module(void) ast_manager_unregister("ConfbridgeListRooms"); ast_manager_unregister("ConfbridgeMute"); ast_manager_unregister("ConfbridgeUnmute"); + ast_manager_unregister("ConfbridgeDeafen"); + ast_manager_unregister("ConfbridgeUndeafen"); ast_manager_unregister("ConfbridgeKick"); ast_manager_unregister("ConfbridgeUnlock"); ast_manager_unregister("ConfbridgeLock"); @@ -3458,6 +3658,8 @@ static int load_module(void) res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms); res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute); res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute); + res |= ast_manager_register_xml("ConfbridgeDeafen", EVENT_FLAG_CALL, action_confbridgedeafen); + res |= ast_manager_register_xml("ConfbridgeUndeafen", EVENT_FLAG_CALL, action_confbridgeundeafen); res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick); res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock); res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock); diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c index b8b1e2a..5bf3269 100644 --- a/apps/confbridge/conf_config_parser.c +++ b/apps/confbridge/conf_config_parser.c @@ -872,6 +872,10 @@ static int set_sound(const char *sound_name, const char *sound_file, struct brid ast_string_field_set(sounds, muted, sound_file); } else if (!strcasecmp(sound_name, "sound_unmuted")) { ast_string_field_set(sounds, unmuted, sound_file); + } else if (!strcasecmp(sound_name, "sound_deafened")) { + ast_string_field_set(sounds, deafened, sound_file); + } else if (!strcasecmp(sound_name, "sound_undeafened")) { + ast_string_field_set(sounds, undeafened, sound_file); } else if (!strcasecmp(sound_name, "sound_there_are")) { ast_string_field_set(sounds, thereare, sound_file); } else if (!strcasecmp(sound_name, "sound_other_in_party")) { @@ -1097,6 +1101,7 @@ static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum con switch (id) { case MENU_ACTION_NOOP: case MENU_ACTION_TOGGLE_MUTE: + case MENU_ACTION_TOGGLE_DEAF: case MENU_ACTION_INCREASE_LISTENING: case MENU_ACTION_DECREASE_LISTENING: case MENU_ACTION_INCREASE_TALKING: @@ -1387,6 +1392,9 @@ static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, in ast_cli(a->fd,"Start Muted: %s\n", u_profile.flags & USER_OPT_STARTMUTED? "true" : "false"); + ast_cli(a->fd,"Start Deaf: %s\n", + u_profile.flags & USER_OPT_STARTDEAF? + "true" : "false"); ast_cli(a->fd,"MOH When Empty: %s\n", u_profile.flags & USER_OPT_MUSICONHOLD ? "enabled" : "disabled"); @@ -1591,6 +1599,8 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e, ast_cli(a->fd,"sound_kicked: %s\n", conf_get_sound(CONF_SOUND_KICKED, b_profile.sounds)); ast_cli(a->fd,"sound_muted: %s\n", conf_get_sound(CONF_SOUND_MUTED, b_profile.sounds)); ast_cli(a->fd,"sound_unmuted: %s\n", conf_get_sound(CONF_SOUND_UNMUTED, b_profile.sounds)); + ast_cli(a->fd,"sound_deafened: %s\n", conf_get_sound(CONF_SOUND_DEAFENED, b_profile.sounds)); + ast_cli(a->fd,"sound_undeafened: %s\n", conf_get_sound(CONF_SOUND_UNDEAFENED, b_profile.sounds)); ast_cli(a->fd,"sound_there_are: %s\n", conf_get_sound(CONF_SOUND_THERE_ARE, b_profile.sounds)); ast_cli(a->fd,"sound_other_in_party: %s\n", conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, b_profile.sounds)); ast_cli(a->fd,"sound_place_into_conference: %s\n", conf_get_sound(CONF_SOUND_PLACE_IN_CONF, b_profile.sounds)); @@ -1719,6 +1729,9 @@ static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, s case MENU_ACTION_TOGGLE_MUTE: ast_cli(a->fd, "toggle_mute"); break; + case MENU_ACTION_TOGGLE_DEAF: + ast_cli(a->fd, "toggle_deaf"); + break; case MENU_ACTION_NOOP: ast_cli(a->fd, "no_op"); break; @@ -2092,6 +2105,7 @@ int conf_load_config(void) aco_option_register(&cfg_info, "admin", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ADMIN); aco_option_register(&cfg_info, "marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MARKEDUSER); aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED); + aco_option_register(&cfg_info, "startdeaf", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTDEAF); aco_option_register(&cfg_info, "music_on_hold_when_empty", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MUSICONHOLD); aco_option_register(&cfg_info, "quiet", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_QUIET); aco_option_register_custom(&cfg_info, "announce_user_count_all", ACO_EXACT, user_types, "no", announce_user_count_all_handler, 0); diff --git a/apps/confbridge/confbridge_manager.c b/apps/confbridge/confbridge_manager.c index eb1b58e..e33c73f 100644 --- a/apps/confbridge/confbridge_manager.c +++ b/apps/confbridge/confbridge_manager.c @@ -189,6 +189,38 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + + + Raised when a Confbridge participant deafens. + + + The name of the Confbridge conference. + + + + + + ConfbridgeUndeafen + ConfBridge + + + + + + Raised when a confbridge participant undeafens. + + + The name of the Confbridge conference. + + + + + + ConfbridgeMute + ConfBridge + + + Raised when a confbridge participant unmutes. @@ -338,6 +370,18 @@ static void confbridge_unmute_cb(void *data, struct stasis_subscription *sub, ast_free(extra_text); } +static void confbridge_deafen_cb(void *data, struct stasis_subscription *sub, + struct stasis_message *message) +{ + confbridge_publish_manager_event(message, "ConfbridgeDeafen", NULL); +} + +static void confbridge_undeafen_cb(void *data, struct stasis_subscription *sub, + struct stasis_message *message) +{ + confbridge_publish_manager_event(message, "ConfbridgeUndeafen", NULL); +} + static void confbridge_talking_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message) { @@ -366,6 +410,8 @@ STASIS_MESSAGE_TYPE_DEFN(confbridge_start_record_type); STASIS_MESSAGE_TYPE_DEFN(confbridge_stop_record_type); STASIS_MESSAGE_TYPE_DEFN(confbridge_mute_type); STASIS_MESSAGE_TYPE_DEFN(confbridge_unmute_type); +STASIS_MESSAGE_TYPE_DEFN(confbridge_deafen_type); +STASIS_MESSAGE_TYPE_DEFN(confbridge_undeafen_type); STASIS_MESSAGE_TYPE_DEFN(confbridge_talking_type); void manager_confbridge_shutdown(void) { @@ -377,6 +423,8 @@ void manager_confbridge_shutdown(void) { STASIS_MESSAGE_TYPE_CLEANUP(confbridge_stop_record_type); STASIS_MESSAGE_TYPE_CLEANUP(confbridge_mute_type); STASIS_MESSAGE_TYPE_CLEANUP(confbridge_unmute_type); + STASIS_MESSAGE_TYPE_CLEANUP(confbridge_deafen_type); + STASIS_MESSAGE_TYPE_CLEANUP(confbridge_undeafen_type); STASIS_MESSAGE_TYPE_CLEANUP(confbridge_talking_type); if (bridge_state_router) { @@ -400,6 +448,8 @@ int manager_confbridge_init(void) STASIS_MESSAGE_TYPE_INIT(confbridge_stop_record_type); STASIS_MESSAGE_TYPE_INIT(confbridge_mute_type); STASIS_MESSAGE_TYPE_INIT(confbridge_unmute_type); + STASIS_MESSAGE_TYPE_INIT(confbridge_deafen_type); + STASIS_MESSAGE_TYPE_INIT(confbridge_undeafen_type); STASIS_MESSAGE_TYPE_INIT(confbridge_talking_type); bridge_state_router = stasis_message_router_create( @@ -466,6 +516,20 @@ int manager_confbridge_init(void) return -1; } if (stasis_message_router_add(bridge_state_router, + confbridge_deafen_type(), + confbridge_deafen_cb, + NULL)) { + manager_confbridge_shutdown(); + return -1; + } + if (stasis_message_router_add(bridge_state_router, + confbridge_undeafen_type(), + confbridge_undeafen_cb, + NULL)) { + manager_confbridge_shutdown(); + return -1; + } + if (stasis_message_router_add(bridge_state_router, confbridge_talking_type(), confbridge_talking_cb, NULL)) { @@ -538,6 +602,20 @@ int manager_confbridge_init(void) return -1; } if (stasis_message_router_add(channel_state_router, + confbridge_deafen_type(), + confbridge_deafen_cb, + NULL)) { + manager_confbridge_shutdown(); + return -1; + } + if (stasis_message_router_add(channel_state_router, + confbridge_undeafen_type(), + confbridge_undeafen_cb, + NULL)) { + manager_confbridge_shutdown(); + return -1; + } + if (stasis_message_router_add(channel_state_router, confbridge_talking_type(), confbridge_talking_cb, NULL)) { diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h index 8d2dffb..ffa60d8 100644 --- a/apps/confbridge/include/confbridge.h +++ b/apps/confbridge/include/confbridge.h @@ -62,6 +62,7 @@ enum user_profile_flags { USER_OPT_ANNOUNCEUSERCOUNTALL = (1 << 14), /*!< Sets if the number of users should be announced to everyone. */ USER_OPT_JITTERBUFFER = (1 << 15), /*!< Places a jitterbuffer on the user. */ USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW = (1 << 16), /*!< modifies ANNOUNCE_JOIN_LEAVE - user reviews the recording before continuing */ + USER_OPT_STARTDEAF = (1 << 17), /*!< Set if the caller should be initially set deaf */ }; enum bridge_profile_flags { @@ -74,6 +75,7 @@ enum bridge_profile_flags { enum conf_menu_action_id { MENU_ACTION_TOGGLE_MUTE = 1, + MENU_ACTION_TOGGLE_DEAF, MENU_ACTION_PLAYBACK, MENU_ACTION_PLAYBACK_AND_CONTINUE, MENU_ACTION_INCREASE_LISTENING, @@ -150,6 +152,8 @@ enum conf_sounds { CONF_SOUND_KICKED, CONF_SOUND_MUTED, CONF_SOUND_UNMUTED, + CONF_SOUND_DEAFENED, + CONF_SOUND_UNDEAFENED, CONF_SOUND_ONLY_ONE, CONF_SOUND_THERE_ARE, CONF_SOUND_OTHER_IN_PARTY, @@ -177,6 +181,8 @@ struct bridge_profile_sounds { AST_STRING_FIELD(kicked); AST_STRING_FIELD(muted); AST_STRING_FIELD(unmuted); + AST_STRING_FIELD(deafened); + AST_STRING_FIELD(undeafened); AST_STRING_FIELD(onlyone); AST_STRING_FIELD(thereare); AST_STRING_FIELD(otherinparty); @@ -367,6 +373,8 @@ int conf_handle_dtmf( struct conf_menu_entry *menu_entry, struct conf_menu *menu); +struct stasis_message_type *confbridge_deafen_type(void); +struct stasis_message_type *confbridge_undeafen_type(void); /*! \brief Looks to see if sound file is stored in bridge profile sounds, if not * default sound is provided.*/ diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c index e3df18f..aeaf1dd 100644 --- a/bridges/bridge_softmix.c +++ b/bridges/bridge_softmix.c @@ -1025,15 +1025,21 @@ static int softmix_mixing_loop(struct ast_bridge *bridge) ast_mutex_lock(&sc->lock); - /* Make SLINEAR write frame from local buffer */ - ao2_t_replace(sc->write_frame.subclass.format, cur_slin, - "Replace softmix channel slin format"); - sc->write_frame.datalen = softmix_datalen; - sc->write_frame.samples = softmix_samples; - memcpy(sc->final_buf, buf, softmix_datalen); - - /* process the softmix channel's new write audio */ - softmix_process_write_audio(&trans_helper, ast_channel_rawwriteformat(bridge_channel->chan), sc); + if (bridge_channel->features && bridge_channel->features->deaf) { + /* For deaf channels post a null frame */ + sc->write_frame.frametype = AST_FRAME_NULL; + } else { + /* Make SLINEAR write frame from local buffer */ + sc->write_frame.frametype = AST_FRAME_VOICE; + ao2_t_replace(sc->write_frame.subclass.format, cur_slin, + "Replace softmix channel slin format"); + sc->write_frame.datalen = softmix_datalen; + sc->write_frame.samples = softmix_samples; + memcpy(sc->final_buf, buf, softmix_datalen); + + /* process the softmix channel's new write audio */ + softmix_process_write_audio(&trans_helper, ast_channel_rawwriteformat(bridge_channel->chan), sc); + } ast_mutex_unlock(&sc->lock); diff --git a/configs/samples/confbridge.conf.sample b/configs/samples/confbridge.conf.sample index d0bdd6f..8104fc2 100644 --- a/configs/samples/confbridge.conf.sample +++ b/configs/samples/confbridge.conf.sample @@ -20,6 +20,7 @@ type=user ;admin=yes ; Sets if the user is an admin or not. Off by default. ;marked=yes ; Sets if this is a marked user or not. Off by default. ;startmuted=yes; Sets if all users should start out muted. Off by default +;startdeaf=yes ; Sets if all users should start out deaf. Off by default. ;music_on_hold_when_empty=yes ; Sets whether MOH should be played when only ; one person is in the conference or when the ; the user is waiting on a marked user to enter @@ -228,6 +229,8 @@ type=bridge ;sound_kicked ; The sound played to a user who has been kicked from the conference. ;sound_muted ; The sound played when the mute option it toggled on. ;sound_unmuted ; The sound played when the mute option it toggled off. +;sound_deafened ; The sound played when the deaf option is toggled on. +;sound_undeafened ; The sound played when the deaf option is toggled off. ;sound_only_person ; The sound played when the user is the only person in the conference. ;sound_only_one ; The sound played to a user when there is only one other ; person is in the conference. @@ -289,6 +292,8 @@ type=bridge ; using the '&' character as a delimiter. ; toggle_mute ; Toggle turning on and off mute. Mute will make the user silent ; to everyone else, but the user will still be able to listen in. +; toggle_deaf ; Toggle turning on and off deaf. Deaf will make the user to hear + ; only silence, but the user will still be able to talk. ; no_op ; This action does nothing (No Operation). Its only real purpose exists for ; being able to reserve a sequence in the config as a menu exit sequence. diff --git a/include/asterisk/bridge_features.h b/include/asterisk/bridge_features.h index df01a0d..1bbce9f 100644 --- a/include/asterisk/bridge_features.h +++ b/include/asterisk/bridge_features.h @@ -273,6 +273,8 @@ struct ast_bridge_features { unsigned int usable:1; /*! TRUE if the channel/bridge is muted. */ unsigned int mute:1; + /*! TRUE if the channel/bridge is deaf. */ + unsigned int deaf:1; /*! TRUE if DTMF should be passed into the bridge tech. */ unsigned int dtmf_passthrough:1; }; diff --git a/main/bridge_channel.c b/main/bridge_channel.c index 4baae3c..d164ac2 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -42,6 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/pbx.h" #include "asterisk/channel.h" +#include "asterisk/format_cache.h" #include "asterisk/timing.h" #include "asterisk/bridge.h" #include "asterisk/bridge_channel.h" @@ -971,7 +972,21 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st return 0; } - dup = ast_frdup(fr); + if (fr->frametype == AST_FRAME_VOICE && + (bridge_channel->features && bridge_channel->features->deaf)) { + short buf[fr->samples]; + struct ast_frame sframe = { + .frametype = AST_FRAME_VOICE, + .subclass.format = ast_format_slin, + .data.ptr = buf, + .samples = fr->samples, + .datalen = sizeof(buf), + }; + memset(buf, 0, sizeof(buf)); + dup = ast_frdup(&sframe); + } else { + dup = ast_frdup(fr); + } if (!dup) { return -1; } -- 2.8.3