Add admin toggle mute all and participant count menu options to app_confbridge

This patch adds two new menu features to app_confbridge, admin_toggle_menu_
participants and participant_count.  The admin action will globally mute /
unmute all non-admin participants on a converence, while the participant
count simply exposes the existing participant count function to the
conference bridge menu.

This also adds configuration options to change the sound played when the
conference is globally muted / unmuted, as well as the necessary config
hooks to place these functions in the DTMF menus.

(closes issue ASTERISK-18204)
Reported by: Kevin Reeves
Tested by: Matt Jordan
Patches:
  app_confbridge.c.patch.txt, conf_config_parser.c.patch.txt, 
  confbridge.h.patch.txt uploaded by Kevin Reeves (license 6281)

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



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@345560 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Matthew Jordan 2011-11-17 18:09:13 +00:00
parent 4a125f45a0
commit 279873e8eb
6 changed files with 192 additions and 4 deletions

View File

@ -12,6 +12,15 @@
--- Functionality changes from Asterisk 10 to Asterisk 11 --------------------
------------------------------------------------------------------------------
ConfBridge
-------------------
* Added menu action admin_toggle_mute_participants. This will mute / unmute
all non-admin participants on a conference. The confbridge configuration file
also allows for the default sounds played to all conference users when this
occurs to be overriden using sound_participants_unmuted and sound_participants_muted.
* Added menu action participant_count. This will playback the number of current
participants in a conference.
SIP Changes
-----------
* Asterisk will no longer substitute CID number for CID name into display

View File

@ -340,6 +340,10 @@ const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds
return S_OR(custom_sounds->join, "confbridge-join");
case CONF_SOUND_LEAVE:
return S_OR(custom_sounds->leave, "confbridge-leave");
case CONF_SOUND_PARTICIPANTS_MUTED:
return S_OR(custom_sounds->participantsmuted, "conf-now-muted");
case CONF_SOUND_PARTICIPANTS_UNMUTED:
return S_OR(custom_sounds->participantsunmuted, "conf-now-unmuted");
}
return "";
@ -1548,6 +1552,37 @@ static int action_toggle_mute(struct conference_bridge *conference_bridge,
"");
}
static int action_toggle_mute_participants(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
{
struct conference_bridge_user *participant = NULL;
const char *sound_to_play;
ao2_lock(conference_bridge);
/* If already muted, then unmute */
conference_bridge->muted = conference_bridge->muted ? 0 : 1;
sound_to_play = conf_get_sound((conference_bridge->muted ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
conference_bridge_user->b_profile.sounds);
AST_LIST_TRAVERSE(&conference_bridge->users_list, participant, list) {
if (!ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
participant->features.mute = conference_bridge->muted;
}
}
ao2_unlock(conference_bridge);
/* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
ast_stream_and_wait(conference_bridge_user->chan, sound_to_play, "");
/* Announce to the group that all participants are muted */
ast_autoservice_start(conference_bridge_user->chan);
play_sound_helper(conference_bridge, sound_to_play, 0);
ast_autoservice_stop(conference_bridge_user->chan);
return 0;
}
static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
{
char *file_copy = ast_strdupa(playback_file);
@ -1727,6 +1762,15 @@ static int execute_menu_entry(struct conference_bridge *conference_bridge,
conference_bridge_user,
bridge_channel->chan);
break;
case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
if (!isadmin) {
break;
}
action_toggle_mute_participants(conference_bridge, conference_bridge_user);
break;
case MENU_ACTION_PARTICIPANT_COUNT:
announce_user_count(conference_bridge, conference_bridge_user);
break;
case MENU_ACTION_PLAYBACK:
if (!stop_prompts) {
res |= action_playback(bridge_channel, menu_action->data.playback_file);
@ -1944,14 +1988,15 @@ static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct
ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
return CLI_SUCCESS;
}
ast_cli(a->fd, "Channel User Profile Bridge Profile Menu\n");
ast_cli(a->fd, "============================= ================ ================ ================\n");
ast_cli(a->fd, "Channel User Profile Bridge Profile Menu CallerID\n");
ast_cli(a->fd, "============================= ================ ================ ================ ================\n");
ao2_lock(bridge);
AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
ast_cli(a->fd, "%-29s ", participant->chan->name);
ast_cli(a->fd, "%-17s", participant->u_profile.name);
ast_cli(a->fd, "%-17s", participant->b_profile.name);
ast_cli(a->fd, "%-17s", participant->menu_name);
ast_cli(a->fd, "%-17s", S_COR(participant->chan->caller.id.number.valid, participant->chan->caller.id.number.str, "<unknown>"));
ast_cli(a->fd, "\n");
}
ao2_unlock(bridge);

View File

@ -253,6 +253,10 @@ static int set_sound(const char *sound_name, const char *sound_file, struct brid
ast_string_field_set(sounds, join, sound_file);
} else if (!strcasecmp(sound_name, "sound_leave")) {
ast_string_field_set(sounds, leave, sound_file);
} else if (!strcasecmp(sound_name, "sound_participants_muted")) {
ast_string_field_set(sounds, participantsmuted, sound_file);
} else if (!strcasecmp(sound_name, "sound_participants_unmuted")) {
ast_string_field_set(sounds, participantsunmuted, sound_file);
} else {
return -1;
}
@ -315,7 +319,7 @@ static int set_bridge_option(const char *name, const char *value, struct bridge_
}
/* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds
* structure of a dynamic profile will need to be altered, a completely new sounds structure must be
* create instead of simply holding a reference to the one built by the config file. */
* created instead of simply holding a reference to the one built by the config file. */
ast_string_field_set(sounds, onlyperson, tmp->sounds->onlyperson);
ast_string_field_set(sounds, hasjoin, tmp->sounds->hasjoin);
ast_string_field_set(sounds, hasleft, tmp->sounds->hasleft);
@ -332,6 +336,8 @@ static int set_bridge_option(const char *name, const char *value, struct bridge_
ast_string_field_set(sounds, unlockednow, tmp->sounds->unlockednow);
ast_string_field_set(sounds, lockednow, tmp->sounds->lockednow);
ast_string_field_set(sounds, errormenu, tmp->sounds->errormenu);
ast_string_field_set(sounds, participantsmuted, tmp->sounds->participantsmuted);
ast_string_field_set(sounds, participantsunmuted, tmp->sounds->participantsunmuted);
ao2_ref(tmp->sounds, -1); /* sounds struct copied over to it from the template by reference only. */
ao2_ref(oldsounds,-1); /* original sounds struct we don't need anymore */
@ -540,6 +546,8 @@ static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum con
case MENU_ACTION_RESET_LISTENING:
case MENU_ACTION_RESET_TALKING:
case MENU_ACTION_ADMIN_TOGGLE_LOCK:
case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
case MENU_ACTION_PARTICIPANT_COUNT:
case MENU_ACTION_ADMIN_KICK_LAST:
case MENU_ACTION_LEAVE:
case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
@ -655,6 +663,10 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_TALKING, NULL);
} else if (!strcasecmp(action, "admin_toggle_conference_lock")) {
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_LOCK, NULL);
} else if (!strcasecmp(action, "admin_toggle_mute_participants")) {
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS, NULL);
} else if (!strcasecmp(action, "participant_count")) {
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PARTICIPANT_COUNT, NULL);
} else if (!strcasecmp(action, "admin_kick_last")) {
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
} else if (!strcasecmp(action, "leave_conference")) {
@ -1025,6 +1037,8 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
ast_cli(a->fd,"sound_unlocked_now: %s\n", conf_get_sound(CONF_SOUND_UNLOCKED_NOW, b_profile.sounds));
ast_cli(a->fd,"sound_lockednow: %s\n", conf_get_sound(CONF_SOUND_LOCKED_NOW, b_profile.sounds));
ast_cli(a->fd,"sound_error_menu: %s\n", conf_get_sound(CONF_SOUND_ERROR_MENU, b_profile.sounds));
ast_cli(a->fd,"sound_participants_muted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_MUTED, b_profile.sounds));
ast_cli(a->fd,"sound_participants_unmuted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_UNMUTED, b_profile.sounds));
ast_cli(a->fd,"\n");
conf_bridge_profile_destroy(&b_profile);
@ -1160,6 +1174,12 @@ static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, s
case MENU_ACTION_ADMIN_TOGGLE_LOCK:
ast_cli(a->fd, "admin_toggle_conference_lock");
break;
case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
ast_cli(a->fd, "admin_toggle_mute_participants");
break;
case MENU_ACTION_PARTICIPANT_COUNT:
ast_cli(a->fd, "participant_count");
break;
case MENU_ACTION_ADMIN_KICK_LAST:
ast_cli(a->fd, "admin_kick_last");
break;
@ -1360,8 +1380,9 @@ const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan,
conf_bridge_profile_copy(result, &b_data->b_profile);
return result;
}
} else {
ast_channel_unlock(chan);
}
ast_channel_unlock(chan);
}
if (ast_strlen_zero(bridge_profile_name)) {
bridge_profile_name = DEFAULT_BRIDGE_PROFILE;

View File

@ -83,6 +83,8 @@ enum conf_menu_action_id {
MENU_ACTION_NOOP,
MENU_ACTION_SET_SINGLE_VIDEO_SRC,
MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC,
MENU_ACTION_PARTICIPANT_COUNT,
MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS,
};
/*! The conference menu action contains both
@ -156,6 +158,8 @@ enum conf_sounds {
CONF_SOUND_ERROR_MENU,
CONF_SOUND_JOIN,
CONF_SOUND_LEAVE,
CONF_SOUND_PARTICIPANTS_MUTED,
CONF_SOUND_PARTICIPANTS_UNMUTED,
};
struct bridge_profile_sounds {
@ -180,6 +184,8 @@ struct bridge_profile_sounds {
AST_STRING_FIELD(errormenu);
AST_STRING_FIELD(leave);
AST_STRING_FIELD(join);
AST_STRING_FIELD(participantsmuted);
AST_STRING_FIELD(participantsunmuted);
);
};
@ -202,6 +208,7 @@ struct conference_bridge {
unsigned int users; /*!< Number of users present */
unsigned int markedusers; /*!< Number of marked users present */
unsigned int locked:1; /*!< Is this conference bridge locked? */
unsigned int muted:1; /*!< Is this conference bridge muted? */
struct ast_channel *playback_chan; /*!< Channel used for playback into the conference bridge */
struct ast_channel *record_chan; /*!< Channel used for recording the conference */
pthread_t record_thread; /*!< The thread the recording chan lives in */

View File

@ -306,6 +306,15 @@ type=bridge
; to whatever operation the video_mode option is set to
; upon release of the video src.
; admin_toggle_mute_participants ; This action allows an administrator to toggle the mute
; state for all non-admins within a conference. All
; admin users are unaffected by this option. Note that all
; users, regardless of their admin status, are notified
; that the conference is muted.
; participant_count ; This action plays back the number of participants currently
; in a conference
[sample_user_menu]
type=menu
*=playback_and_continue(conf-usermenu)

View File

@ -0,0 +1,97 @@
#
# Table structure for table `sipfriends`
#
CREATE TABLE IF NOT EXISTS `sipfriends` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) NOT NULL,
`ipaddr` varchar(15) DEFAULT NULL,
`port` int(5) DEFAULT NULL,
`regseconds` int(11) DEFAULT NULL,
`defaultuser` varchar(10) DEFAULT NULL,
`fullcontact` varchar(35) DEFAULT NULL,
`regserver` varchar(20) DEFAULT NULL,
`useragent` varchar(20) DEFAULT NULL,
`lastms` int(11) DEFAULT NULL,
`host` varchar(40) DEFAULT NULL,
`type` enum('friend','user','peer') DEFAULT NULL,
`context` varchar(40) DEFAULT NULL,
`permit` varchar(40) DEFAULT NULL,
`deny` varchar(40) DEFAULT NULL,
`secret` varchar(40) DEFAULT NULL,
`md5secret` varchar(40) DEFAULT NULL,
`remotesecret` varchar(40) DEFAULT NULL,
`transport` enum('udp','tcp','udp,tcp','tcp,udp') DEFAULT NULL,
`dtmfmode` enum('rfc2833','info','shortinfo','inband','auto') DEFAULT NULL,
`directmedia` enum('yes','no','nonat','update') DEFAULT NULL,
`nat` enum('yes','no','never','route') DEFAULT NULL,
`callgroup` varchar(40) DEFAULT NULL,
`pickupgroup` varchar(40) DEFAULT NULL,
`language` varchar(40) DEFAULT NULL,
`allow` varchar(200) DEFAULT NULL,
`insecure` varchar(40) DEFAULT NULL,
`trustrpid` enum('yes','no') DEFAULT NULL,
`progressinband` enum('yes','no','never') DEFAULT NULL,
`promiscredir` enum('yes','no') DEFAULT NULL,
`useclientcode` enum('yes','no') DEFAULT NULL,
`accountcode` varchar(40) DEFAULT NULL,
`setvar` varchar(40) DEFAULT NULL,
`callerid` varchar(40) DEFAULT NULL,
`amaflags` varchar(40) DEFAULT NULL,
`callcounter` enum('yes','no') DEFAULT NULL,
`busylevel` int(11) DEFAULT NULL,
`allowoverlap` enum('yes','no') DEFAULT NULL,
`allowsubscribe` enum('yes','no') DEFAULT NULL,
`videosupport` enum('yes','no') DEFAULT NULL,
`maxcallbitrate` int(11) DEFAULT NULL,
`rfc2833compensate` enum('yes','no') DEFAULT NULL,
`mailbox` varchar(40) DEFAULT NULL,
`session-timers` enum('accept','refuse','originate') DEFAULT NULL,
`session-expires` int(11) DEFAULT NULL,
`session-minse` int(11) DEFAULT NULL,
`session-refresher` enum('uac','uas') DEFAULT NULL,
`t38pt_usertpsource` varchar(40) DEFAULT NULL,
`regexten` varchar(40) DEFAULT NULL,
`fromdomain` varchar(40) DEFAULT NULL,
`fromuser` varchar(40) DEFAULT NULL,
`qualify` varchar(40) DEFAULT NULL,
`defaultip` varchar(40) DEFAULT NULL,
`rtptimeout` int(11) DEFAULT NULL,
`rtpholdtimeout` int(11) DEFAULT NULL,
`sendrpid` enum('yes','no') DEFAULT NULL,
`outboundproxy` varchar(40) DEFAULT NULL,
`callbackextension` varchar(40) DEFAULT NULL,
`registertrying` enum('yes','no') DEFAULT NULL,
`timert1` int(11) DEFAULT NULL,
`timerb` int(11) DEFAULT NULL,
`qualifyfreq` int(11) DEFAULT NULL,
`constantssrc` enum('yes','no') DEFAULT NULL,
`contactpermit` varchar(40) DEFAULT NULL,
`contactdeny` varchar(40) DEFAULT NULL,
`usereqphone` enum('yes','no') DEFAULT NULL,
`textsupport` enum('yes','no') DEFAULT NULL,
`faxdetect` enum('yes','no') DEFAULT NULL,
`buggymwi` enum('yes','no') DEFAULT NULL,
`auth` varchar(40) DEFAULT NULL,
`fullname` varchar(40) DEFAULT NULL,
`trunkname` varchar(40) DEFAULT NULL,
`cid_number` varchar(40) DEFAULT NULL,
`callingpres` enum('allowed_not_screened','allowed_passed_screen','allowed_failed_screen','allowed','prohib_not_screened','prohib_passed_screen','prohib_failed_screen','prohib') DEFAULT NULL,
`mohinterpret` varchar(40) DEFAULT NULL,
`mohsuggest` varchar(40) DEFAULT NULL,
`parkinglot` varchar(40) DEFAULT NULL,
`hasvoicemail` enum('yes','no') DEFAULT NULL,
`subscribemwi` enum('yes','no') DEFAULT NULL,
`vmexten` varchar(40) DEFAULT NULL,
`autoframing` enum('yes','no') DEFAULT NULL,
`rtpkeepalive` int(11) DEFAULT NULL,
`call-limit` int(11) DEFAULT NULL,
`g726nonstandard` enum('yes','no') DEFAULT NULL,
`ignoresdpversion` enum('yes','no') DEFAULT NULL,
`allowtransfer` enum('yes','no') DEFAULT NULL,
`dynamic` enum('yes','no') DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
KEY `ipaddr` (`ipaddr`,`port`),
KEY `host` (`host`,`port`)
) ENGINE=MyISAM;