Index: apps/app_meetme.c =================================================================== --- apps/app_meetme.c (revision 66066) +++ apps/app_meetme.c (working copy) @@ -79,7 +79,8 @@ enum { ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */ ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */ - ADMINFLAG_KICKME = (1 << 3) /*!< User has been kicked */ + ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */ + ADMINFLAG_WHISPER = (1 << 4) /*!< User is to be whispered */ }; #define MEETME_DELAYDETECTTALK 300 @@ -158,7 +159,9 @@ CONFFLAG_SLA_STATION = (1 << 26), CONFFLAG_SLA_TRUNK = (1 << 27), /*! If set, the user should continue in the dialplan if kicked out */ - CONFFLAG_KICK_CONTINUE = (1 << 28) + CONFFLAG_KICK_CONTINUE = (1 << 28), + /*! If set all dtmf key events will be sent to the manager */ + CONFFLAG_DTMF = (1 << 29) }; enum { @@ -193,6 +196,7 @@ AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ), AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ), AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ), + AST_APP_OPTION('z', CONFFLAG_DTMF ), END_OPTIONS ); static const char *app = "MeetMe"; @@ -256,7 +260,8 @@ " 'X' -- allow user to exit the conference by entering a valid single\n" " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n" " if that variable is not defined.\n" -" '1' -- do not play message when first person enters\n"; +" '1' -- do not play message when first person enters\n" +" 'z' -- all dtmf key events will be sent to the manager\n"; static const char *descrip2 = " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n" @@ -358,6 +363,7 @@ time_t jointime; /*!< Time the user joined the conference */ struct volume talk; struct volume listen; + char *whisperfile; /*!< Message file name to be played to user */ AST_LIST_ENTRY(ast_conf_user) list; }; @@ -1868,6 +1874,36 @@ chan->name, chan->uniqueid, conf->confno, user->user_no); } + /* If whisper mode */ + if (user->adminflags & ADMINFLAG_WHISPER) { + user->adminflags ^= ADMINFLAG_WHISPER; + /* Separate user from conference audio stream temporarily */ + struct zt_confinfo ztc_curr; + memset(&ztc_curr, 0, sizeof(ztc_curr)); + if (!ioctl(fd, ZT_GETCONF, &ztc_curr)) { + ztc.confmode |= ZT_CONF_TALKER; + ztc.confmode ^= ZT_CONF_LISTENER; + if (ioctl(fd, ZT_SETCONF, &ztc)) + ast_log(LOG_WARNING, "Error setting conference\n"); + + /* whisper the message */ + if (!ast_streamfile(user->chan, user->whisperfile, user->chan->language)) + ast_waitstream(user->chan, AST_DIGIT_ANY); + + /* Restore user's audio stream from the conference */ + if (ioctl(fd, ZT_SETCONF, &ztc_curr)) + ast_log(LOG_WARNING, "Error setting conference\n"); + + /* Restore musiconhold */ + if (((musiconhold) && (confflags & CONFFLAG_MOH)) && + (((confflags & CONFFLAG_WAITMARKED) && (currentmarked == 0)) + || (!(confflags & CONFFLAG_WAITMARKED) && (conf->users == 1)))) + ast_moh_start(user->chan, NULL, NULL); + } else { + ast_log(LOG_WARNING, "Error getting conference\n"); + } + } + /* If I have been kicked, exit the conference */ if (user->adminflags & ADMINFLAG_KICKME) { //You have been kicked. @@ -1898,6 +1934,19 @@ f = ast_read(c); if (!f) break; + + /* Send UserEvent to manager if CONFFLAG_DTMF is turned on, can be used + to customize key functionality through manager interface */ + if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_DTMF)) { + manager_event(EVENT_FLAG_CALL, "DTMFEvent", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Meetme: %s\r\n" + "Usernum: %d\r\n" + "Status: %c\r\n", + chan->name, chan->uniqueid, conf->confno, user->user_no, f->subclass); + } + if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) { if (user->talk.actual) ast_frame_adjust_volume(f, user->talk.actual); @@ -3042,6 +3091,75 @@ return meetmemute(s, m, 0); } +static int action_meetmewhisper(struct mansession *s, const struct message *m) +{ + struct ast_conference *conf; + struct ast_conf_user *user; + const char *confid = astman_get_header(m, "Meetme"); + char *userid = ast_strdupa(astman_get_header(m, "Usernum")); + const char *message = astman_get_header(m, "Message"); + int userno; + + if (!confid || ast_strlen_zero(confid)) { + astman_send_error(s, m, "Meetme conference not specified"); + return 0; + } + + if (!userid || ast_strlen_zero(userid)) { + astman_send_error(s, m, "Meetme user number not specified"); + return 0; + } + + if (!message || ast_strlen_zero(message)) { + astman_send_error(s, m, "Meetme whisper message not specified"); + return 0; + } + + userno = strtoul(userid, &userid, 10); + + if (*userid) { + astman_send_error(s, m, "Invalid user number"); + return 0; + } + + /* Look in the conference list */ + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, conf, list) { + if (!strcmp(confid, conf->confno)) + break; + } + + if (!conf) { + AST_LIST_UNLOCK(&confs); + astman_send_error(s, m, "Meetme conference does not exist"); + return 0; + } + + AST_LIST_TRAVERSE(&conf->userlist, user, list) { + if (user->user_no == userno) + break; + } + + if (!user) { + AST_LIST_UNLOCK(&confs); + astman_send_error(s, m, "User number not found"); + return 0; + } + + user->whisperfile = realloc(user->whisperfile, strlen(message)); + strcpy(user->whisperfile, message); + + /* request user whisper, disconnect from rest of conference stream to prevent mixing */ + user->adminflags |= ADMINFLAG_WHISPER; + + AST_LIST_UNLOCK(&confs); + + ast_log(LOG_NOTICE, "Requested to whisper conf %s user %d userchan %s uniqueid %s\n", conf->confno, user->user_no, user->chan->name, user->chan->uniqueid); + + astman_send_ack(s, m, "Whispered user"); + return 0; +} + static void *recordthread(void *args) { struct ast_conference *cnf = args; @@ -4827,6 +4945,7 @@ ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme)); res = ast_manager_unregister("MeetmeMute"); res |= ast_manager_unregister("MeetmeUnmute"); + res |= ast_manager_unregister("MeetmeWhisper"); res |= ast_unregister_application(app4); res |= ast_unregister_application(app3); res |= ast_unregister_application(app2); @@ -4855,6 +4974,8 @@ action_meetmemute, "Mute a Meetme user"); res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute, "Unmute a Meetme user"); + res |= ast_manager_register("MeetmeWhisper", EVENT_FLAG_CALL, + action_meetmewhisper, "Whisper a specific channel"); res |= ast_register_application(app4, channel_admin_exec, synopsis4, descrip4); res |= ast_register_application(app3, admin_exec, synopsis3, descrip3); res |= ast_register_application(app2, count_exec, synopsis2, descrip2);