Adding support for the "automixmonitor" dial and queue options.
authorMark Michelson <mmichelson@digium.com>
Fri, 30 Nov 2007 21:19:57 +0000 (21:19 +0000)
committerMark Michelson <mmichelson@digium.com>
Fri, 30 Nov 2007 21:19:57 +0000 (21:19 +0000)
This works in much the same way as the automonitor, except that instead of using the monitor
app, it uses the mixmonitor app. By providing an 'x' or 'X' as a dial or queue option, a DTMF
sequence may be entered (as defined in features.conf) to start the one-touch mixmonitor.

This patch also introduces some new API calls to the audiohooks code for searching for an audiohook
by type and for searching for a running audiohook by type.

Big thanks to joetester for writing the initial patch, testing it and patiently waiting for it to
be committed.

(closes issue #10185, reported and patched by xmarksthespot)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90388 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_dial.c
apps/app_queue.c
configs/features.conf.sample
include/asterisk/app.h
include/asterisk/audiohook.h
include/asterisk/channel.h
main/audiohook.c
res/res_features.c

index a7f5fff..265b49b 100644 (file)
@@ -203,8 +203,12 @@ static char *descrip =
 "    w    - Allow the called party to enable recording of the call by sending\n"
 "           the DTMF sequence defined for one-touch recording in features.conf.\n"
 "    W    - Allow the calling party to enable recording of the call by sending\n"
-"           the DTMF sequence defined for one-touch recording in features.conf.\n";
-
+"           the DTMF sequence defined for one-touch recording in features.conf.\n"
+"    x    - Allow the called party to enable recording of the call by sending\n"
+"           the DTMF sequence defined for one-touch automixmonitor in features.conf\n"
+"    X    - Allow the calling party to enable recording of the call by sending\n"
+"           the DTMF sequence defined for one-touch automixmonitor in features.conf\n";
 /* RetryDial App by Anthony Minessale II <anthmct@yahoo.com> Jan/2005 */
 static char *rapp = "RetryDial";
 static char *rsynopsis = "Place a call, retrying on failure allowing optional exit extension.";
@@ -250,12 +254,14 @@ enum {
        OPT_CALLER_PARK =       (1 << 26),
        OPT_IGNORE_FORWARDING = (1 << 27),
        OPT_CALLEE_GOSUB =      (1 << 28),
-       OPT_CANCEL_ELSEWHERE =  (1 << 29),
-       OPT_PEER_H =            (1 << 30),
+       OPT_CALLEE_MIXMONITOR = (1 << 29),
+       OPT_CALLER_MIXMONITOR = (1 << 30),
 };
 
 #define DIAL_STILLGOING                        (1 << 31)
 #define DIAL_NOFORWARDHTML             ((uint64_t)1 << 32) /* flags are now 64 bits, so keep it up! */
+#define OPT_CANCEL_ELSEWHERE   ((uint64_t)1 << 33)
+#define OPT_PEER_H                     ((uint64_t)1 << 34)
 
 enum {
        OPT_ARG_ANNOUNCE = 0,
@@ -305,6 +311,8 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
        AST_APP_OPTION_ARG('U', OPT_CALLEE_GOSUB, OPT_ARG_CALLEE_GOSUB),
        AST_APP_OPTION('w', OPT_CALLEE_MONITOR),
        AST_APP_OPTION('W', OPT_CALLER_MONITOR),
+       AST_APP_OPTION('x', OPT_CALLEE_MIXMONITOR),
+       AST_APP_OPTION('X', OPT_CALLER_MIXMONITOR),
 END_OPTIONS );
 
 #define CAN_EARLY_BRIDGE(flags) (!ast_test_flag64(flags, OPT_CALLEE_HANGUP | \
@@ -621,6 +629,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                                       OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
                                                       OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
                                                       OPT_CALLEE_PARK | OPT_CALLER_PARK |
+                                                      OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
                                                       DIAL_NOFORWARDHTML);
                                        ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext));
                                        ast_copy_string(c->exten, "", sizeof(c->exten));
@@ -655,6 +664,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                                               OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
                                                               OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
                                                               OPT_CALLEE_PARK | OPT_CALLER_PARK |
+                                                              OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
                                                               DIAL_NOFORWARDHTML);
                                                ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext));
                                                ast_copy_string(c->exten, "", sizeof(c->exten));
@@ -1332,6 +1342,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                                       OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
                                       OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
                                       OPT_CALLEE_PARK | OPT_CALLER_PARK |
+                                      OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
                                       OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID);
                        ast_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML);     
                }
@@ -1758,6 +1769,10 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                                ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
                        if (ast_test_flag64(peerflags, OPT_CALLER_PARK))
                                ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
+                       if (ast_test_flag64(peerflags, OPT_CALLEE_MIXMONITOR))
+                               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 (moh) {
                                moh = 0;
index d78420e..8be6a30 100644 (file)
@@ -152,6 +152,9 @@ static char *descrip =
 "             the DTMF sequence defined for call parking in features.conf.\n"
 "      'K' -- Allow the calling party to enable parking of the call by sending\n"
 "             the DTMF sequence defined for call parking in features.conf.\n"
+"      'x' -- allow the called user to write the conversation to disk via MixMonitor\n"
+"      'X' -- allow the calling user to write the conversation to disk via MixMonitor\n"
 "  In addition to transferring the call, a call may be parked and then picked\n"
 "up by another user.\n"
 "  The optional URL will be sent to the called party if the channel supports\n"
@@ -2775,6 +2778,13 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                case 'i':
                        forwardsallowed = 0;
                        break;
+               case 'x':
+                       ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
+                       break;
+               case 'X':
+                       ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
+                       break;
+
                }
 
        /* Hold the lock while we setup the outgoing calls */
index 30a61b5..c3026ad 100644 (file)
@@ -52,6 +52,7 @@ context => parkedcalls                ; Which context parked calls are in
 ;automon => *1                 ; One Touch Record a.k.a. Touch Monitor
 ;atxfer => *2                  ; Attended transfer
 ;parkcall => #72                ; Park call (one step parking)
+;automixmon => *3              ; One Touch Record a.k.a. Touch MixMonitor
 
 [applicationmap]
 ; Note that the DYNAMIC_FEATURES channel variable must be set to use the features
index 656e839..f76b0d9 100644 (file)
@@ -344,7 +344,7 @@ unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arra
  */
 struct ast_app_option {
        /*! \brief The flag bit that represents this option. */
-       unsigned int flag;
+       uint64_t flag;
        /*! \brief The index of the entry in the arguments array
          that should be used for this option's argument. */
        unsigned int arg_index;
index 3fd2eb1..5ef0ac2 100644 (file)
@@ -171,6 +171,28 @@ struct ast_frame *ast_audiohook_write_list(struct ast_channel *chan, struct ast_
  */
 void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook);
 
+/*!
+  \brief Find out how many audiohooks from  a certain source exist on a given channel, regardless of status.
+  \param chan The channel on which to find the spies 
+  \param source The audiohook's source
+  \param type The type of audiohook 
+  \return Return the number of audiohooks which are from the source specified
+
+  Note: Function performs nlocking.
+*/
+int ast_channel_audiohook_count_by_source(struct ast_channel *chan, const char *source, enum ast_audiohook_type type);
+
+/*!
+  \brief Find out how many spies of a certain type exist on a given channel, and are in state running.
+  \param chan The channel on which to find the spies
+  \param source The source of the audiohook
+  \param type The type of spy to look for
+  \return Return the number of running audiohooks which are from the source specified
+
+  Note: Function performs no locking.
+*/
+int ast_channel_audiohook_count_by_source_running(struct ast_channel *chan, const char *source, enum ast_audiohook_type type);
+
 /*! \brief Lock an audiohook
  * \param ah Audiohook structure
  */
index 4bf33d7..aea8f4d 100644 (file)
@@ -565,6 +565,7 @@ enum {
        AST_FEATURE_ATXFER =       (1 << 3),
        AST_FEATURE_AUTOMON =      (1 << 4),
        AST_FEATURE_PARKCALL =     (1 << 5),
+       AST_FEATURE_AUTOMIXMON =   (1 << 6),
 };
 
 /*! \brief bridge configuration */
index f1e5f58..9b97930 100644 (file)
@@ -612,3 +612,83 @@ void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
        
        return;
 }
+
+/* Count number of channel audiohooks by type, regardless of type */
+int ast_channel_audiohook_count_by_source(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
+{
+       int count = 0;
+       struct ast_audiohook *ah = NULL;
+
+       if (!chan->audiohooks)
+               return -1;
+
+       switch (type) {
+               case AST_AUDIOHOOK_TYPE_SPY:
+                       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->spy_list, ah, list) {
+                               if (!strcmp(ah->source, source)) {
+                                       count++;
+                               }
+                       }
+                       AST_LIST_TRAVERSE_SAFE_END;
+                       break;
+               case AST_AUDIOHOOK_TYPE_WHISPER:
+                       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->whisper_list, ah, list) {
+                               if (!strcmp(ah->source, source)) {
+                                       count++;
+                               }
+                       }
+                       AST_LIST_TRAVERSE_SAFE_END;
+                       break;
+               case AST_AUDIOHOOK_TYPE_MANIPULATE:
+                       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->manipulate_list, ah, list) {
+                               if (!strcmp(ah->source, source)) {
+                                       count++;
+                               }
+                       }
+                       AST_LIST_TRAVERSE_SAFE_END;
+                       break;
+               default:
+                       ast_log(LOG_DEBUG, "Invalid audiohook type supplied, (%d)\n", type);
+                       return -1;
+       }
+
+       return count;
+}
+
+/* Count number of channel audiohooks by type that are running */
+int ast_channel_audiohook_count_by_source_running(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
+{
+       int count = 0;
+       struct ast_audiohook *ah = NULL;
+       if (!chan->audiohooks)
+               return -1;
+
+       switch (type) {
+               case AST_AUDIOHOOK_TYPE_SPY:
+                       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->spy_list, ah, list) {
+                               if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
+                                       count++;
+                       }
+                       AST_LIST_TRAVERSE_SAFE_END;
+                       break;
+               case AST_AUDIOHOOK_TYPE_WHISPER:
+                       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->whisper_list, ah, list) {
+                               if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
+                                       count++;
+                       }
+                       AST_LIST_TRAVERSE_SAFE_END;
+                       break;
+               case AST_AUDIOHOOK_TYPE_MANIPULATE:
+                       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->manipulate_list, ah, list) {
+                               if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
+                                       count++;
+                       }
+                       AST_LIST_TRAVERSE_SAFE_END;
+                       break;
+               default:
+                       ast_log(LOG_DEBUG, "Invalid audiohook type supplied, (%d)\n", type);
+                       return -1;
+       }
+       return count;
+}
+
index b483dbb..3deb1bc 100644 (file)
@@ -50,6 +50,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/adsi.h"
 #include "asterisk/devicestate.h"
 #include "asterisk/monitor.h"
+#include "asterisk/audiohook.h"
 
 #define DEFAULT_PARK_TIME 45000
 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
@@ -151,6 +152,12 @@ static char *descrip2 = "Park(): "
 static struct ast_app *monitor_app = NULL;
 static int monitor_ok = 1;
 
+static struct ast_app *mixmonitor_app = NULL;
+static int mixmonitor_ok = 1;
+
+static struct ast_app *stopmixmonitor_app = NULL;
+static int stopmixmonitor_ok = 1;
+
 struct parkeduser {
        struct ast_channel *chan;                   /*!< Parking channel */
        struct timeval start;                       /*!< Time the parking started */
@@ -717,6 +724,118 @@ static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *pee
        return -1;
 }
 
+static int builtin_automixmonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
+{
+       char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
+       int x = 0;
+       size_t len;
+       struct ast_channel *caller_chan, *callee_chan;
+       const char *mixmonitor_spy_type = "MixMonitor";
+       int count = 0;
+
+       if (!mixmonitor_ok) {
+               ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
+               return -1;
+       }
+
+       if (!(mixmonitor_app = pbx_findapp("MixMonitor"))) {
+               mixmonitor_ok = 0;
+               ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
+               return -1;
+       }
+
+       set_peers(&caller_chan, &callee_chan, peer, chan, sense);
+
+       if (!ast_strlen_zero(courtesytone)) {
+               if (ast_autoservice_start(callee_chan))
+                       return -1;
+               if (ast_stream_and_wait(caller_chan, courtesytone, "")) {
+                       ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
+                       ast_autoservice_stop(callee_chan);
+                       return -1;
+               }
+               if (ast_autoservice_stop(callee_chan))
+                       return -1;
+       }
+
+       ast_channel_lock(callee_chan);
+       count = ast_channel_audiohook_count_by_source(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
+       ast_channel_unlock(callee_chan);
+
+       // This means a mixmonitor is attached to the channel, running or not is unknown.
+       if (count > 0) {
+               
+               if (option_verbose > 3)
+                       ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code);
+
+               //Make sure they are running
+               ast_channel_lock(callee_chan);
+               count = ast_channel_audiohook_count_by_source_running(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
+               ast_channel_unlock(callee_chan);
+               if (count > 0) {
+                       if (!stopmixmonitor_ok) {
+                               ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
+                               return -1;
+                       }
+                       if (!(stopmixmonitor_app = pbx_findapp("StopMixMonitor"))) {
+                               stopmixmonitor_ok = 0;
+                               ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
+                               return -1;
+                       } else {
+                               pbx_exec(callee_chan, stopmixmonitor_app, "");
+                               return FEATURE_RETURN_SUCCESS;
+                       }
+               }
+               
+               ast_log(LOG_WARNING,"Stopped MixMonitors are attached to the channel.\n");      
+       }                       
+
+       if (caller_chan && callee_chan) {
+               const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR_FORMAT");
+               const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR");
+
+               if (!touch_format)
+                       touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR_FORMAT");
+
+               if (!touch_monitor)
+                       touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR");
+
+               if (touch_monitor) {
+                       len = strlen(touch_monitor) + 50;
+                       args = alloca(len);
+                       touch_filename = alloca(len);
+                       snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
+                       snprintf(args, len, "%s.%s,b", touch_filename, (touch_format) ? touch_format : "wav");
+               } else {
+                       caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name));
+                       callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name));
+                       len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
+                       args = alloca(len);
+                       touch_filename = alloca(len);
+                       snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
+                       snprintf(args, len, "%s.%s,b", touch_filename, S_OR(touch_format, "wav"));
+               }
+
+               for( x = 0; x < strlen(args); x++) {
+                       if (args[x] == '/')
+                               args[x] = '-';
+               }
+
+               if (option_verbose > 3)
+                       ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, touch_filename);
+
+               pbx_exec(callee_chan, mixmonitor_app, args);
+               pbx_builtin_setvar_helper(callee_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
+               pbx_builtin_setvar_helper(caller_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
+               return FEATURE_RETURN_SUCCESS;
+       
+       }
+
+       ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");
+       return -1;
+
+}
+
 static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
 {
        ast_verb(4, "User hit '%s' to disconnect call.\n", code);
@@ -1136,6 +1255,7 @@ static struct ast_call_feature builtin_features[] =
        { AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF, "" },
        { AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF, "" },
        { AST_FEATURE_PARKCALL, "Park Call", "parkcall", "", "", builtin_parkcall, AST_FEATURE_FLAG_NEEDSDTMF, "" },
+       { AST_FEATURE_AUTOMIXMON, "One Touch MixMonitor", "automixmon", "", "", builtin_automixmonitor, AST_FEATURE_FLAG_NEEDSDTMF, "" },
 };