Added MixMonitorMute manager command
authorJulian Lyndon-Smith <julian@dotr.com>
Wed, 21 Apr 2010 11:27:27 +0000 (11:27 +0000)
committerJulian Lyndon-Smith <julian@dotr.com>
Wed, 21 Apr 2010 11:27:27 +0000 (11:27 +0000)
Added a new manager command to mute/unmute MixMonitor audio on a channel.
Added a new feature to audiohooks so that you can mute either read / write
(or both) types of frames - this allows for MixMonitor to mute either side
of the conversation without affecting the conversation itself.

(closes issue #16740)
Reported by: jmls

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

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

apps/app_mixmonitor.c
include/asterisk/audiohook.h
include/asterisk/frame.h
main/audiohook.c
main/frame.c
res/res_mutestream.c

index bb4b736..ca3a239 100644 (file)
@@ -46,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/app.h"
 #include "asterisk/channel.h"
 #include "asterisk/autochan.h"
+#include "asterisk/manager.h"
 
 /*** DOCUMENTATION
        <application name="MixMonitor" language="en_US">
@@ -124,7 +125,27 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <ref type="application">MixMonitor</ref>
                </see-also>
        </application>
-               
+       <manager name="MixMonitorMute" language="en_US">
+               <synopsis>
+                       Mute / unMute a Mixmonitor recording.
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+                       <parameter name="Channel" required="true">
+                               <para>Used to specify the channel to mute.</para>
+                       </parameter>
+                       <parameter name="Direction">
+                               <para>Which part of the recording to mute:  read, write or both (from channel, to channel or both channels).</para>
+                       </parameter>
+                       <parameter name="State">
+                               <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>This action may be used to mute a MixMonitor recording.</para>
+               </description>
+       </manager>
+
  ***/
 
 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
@@ -606,6 +627,73 @@ static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_
        return CLI_SUCCESS;
 }
 
+/*! \brief  Mute / unmute  a MixMonitor channel */
+static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
+{
+       struct ast_channel *c = NULL;
+
+       const char *name = astman_get_header(m, "Channel");
+       const char *id = astman_get_header(m, "ActionID");
+       const char *state = astman_get_header(m, "State");
+       const char *direction = astman_get_header(m,"Direction");
+
+       int clearmute = 1;
+
+       enum ast_audiohook_flags flag;
+
+       if (ast_strlen_zero(direction)) {
+               astman_send_error(s, m, "No direction specified. Must be read, write or both");
+               return AMI_SUCCESS;
+       }
+
+       if (!strcasecmp(direction, "read")) {
+               flag = AST_AUDIOHOOK_MUTE_READ;
+       } else  if (!strcasecmp(direction, "write")) {
+               flag = AST_AUDIOHOOK_MUTE_WRITE;
+       } else  if (!strcasecmp(direction, "both")) {
+               flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE;
+       } else {
+               astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
+               return AMI_SUCCESS;
+       }
+
+       if (ast_strlen_zero(name)) {
+               astman_send_error(s, m, "No channel specified");
+               return AMI_SUCCESS;
+       }
+
+       if (ast_strlen_zero(state)) {
+               astman_send_error(s, m, "No state specified");
+               return AMI_SUCCESS;
+       }
+
+       clearmute = ast_false(state);
+       c = ast_channel_get_by_name(name);
+
+       if (!c) {
+               astman_send_error(s, m, "No such channel");
+               return AMI_SUCCESS;
+       }
+
+       if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
+               c = ast_channel_unref(c);
+               astman_send_error(s, m, "Cannot set mute flag");
+               return AMI_SUCCESS;
+       }
+
+       astman_append(s, "Response: Success\r\n");
+
+       if (!ast_strlen_zero(id)) {
+               astman_append(s, "ActionID: %s\r\n", id);
+       }
+
+       astman_append(s, "\r\n");
+
+       c = ast_channel_unref(c);
+
+       return AMI_SUCCESS;
+}
+
 static struct ast_cli_entry cli_mixmonitor[] = {
        AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
 };
@@ -617,6 +705,7 @@ static int unload_module(void)
        ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
        res = ast_unregister_application(stop_app);
        res |= ast_unregister_application(app);
+       res |= ast_manager_unregister("MixMonitorMute");
        
        return res;
 }
@@ -628,6 +717,7 @@ static int load_module(void)
        ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
        res = ast_register_application_xml(app, mixmonitor_exec);
        res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
+       res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
 
        return res;
 }
index 91081ce..b1e77f8 100644 (file)
@@ -62,6 +62,8 @@ enum ast_audiohook_flags {
         * slinfactories. We will flush the factories if they contain too many samples.
         */
        AST_AUDIOHOOK_SMALL_QUEUE = (1 << 3),
+       AST_AUDIOHOOK_MUTE_READ = (1 << 4),     /*!< audiohook should be mute frames read */
+       AST_AUDIOHOOK_MUTE_WRITE = (1 << 5),    /*!< audiohook should be mute frames written */
 };
 
 #define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*< Tolerance in milliseconds for audiohooks synchronization */
@@ -277,6 +279,16 @@ int ast_audiohook_volume_get(struct ast_channel *chan, enum ast_audiohook_direct
  */
 int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume);
 
+/*! \brief Mute frames read from or written to a channel
+ * \param chan Channel to muck with
+ * \param source Type of audiohook
+ * \param flag which direction to set / clear
+ * \param clear set or clear muted frames on direction based on flag parameter
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_audiohook_set_mute(struct ast_channel *chan, const char *source, enum ast_audiohook_flags flag, int clear);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
index 68a0c7e..0d8d557 100644 (file)
@@ -728,6 +728,11 @@ static force_inline int ast_format_rate(format_t format)
        }
 }
 
+/*!
+ * \brief Clear all audio samples from an ast_frame. The frame must be AST_FRAME_VOICE and AST_FORMAT_SLINEAR 
+ */
+int ast_frame_clear(struct ast_frame *frame);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
index 4a24a2f..f586b09 100644 (file)
@@ -127,6 +127,7 @@ int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohoo
        int our_factory_ms;
        int other_factory_samples;
        int other_factory_ms;
+       int muteme = 0;
 
        /* Update last feeding time to be current */
        *rwtime = ast_tvnow();
@@ -151,6 +152,17 @@ int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohoo
                ast_slinfactory_flush(other_factory);
        }
 
+       /* swap frame data for zeros if mute is required */
+       if ((ast_test_flag(audiohook, AST_AUDIOHOOK_MUTE_READ) && (direction == AST_AUDIOHOOK_DIRECTION_READ)) ||
+               (ast_test_flag(audiohook, AST_AUDIOHOOK_MUTE_WRITE) && (direction == AST_AUDIOHOOK_DIRECTION_WRITE)) ||
+               (ast_test_flag(audiohook, AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE) == (AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE))) {
+                       muteme = 1;
+       }
+
+       if (muteme && frame->datalen > 0) {
+               ast_frame_clear(frame);
+       }
+
        /* Write frame out to respective factory */
        ast_slinfactory_feed(factory, frame);
 
@@ -1001,3 +1013,37 @@ int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_dir
 
        return 0;
 }
+
+/*! \brief Mute frames read from or written to a channel
+ * \param chan Channel to muck with
+ * \param source Type of audiohook
+ * \param flag which flag to set / clear
+ * \param clear set or clear
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_set_mute(struct ast_channel *chan, const char *source, enum ast_audiohook_flags flag, int clear)
+{
+       struct ast_audiohook *audiohook = NULL;
+
+       ast_channel_lock(chan);
+
+       /* Ensure the channel has audiohooks on it */
+       if (!chan->audiohooks) {
+               ast_channel_unlock(chan);
+               return -1;
+       }
+
+       audiohook = find_audiohook_by_source(chan->audiohooks, source);
+
+       if (audiohook) {
+               if (clear) {
+                       ast_clear_flag(audiohook, flag);
+               } else {
+                       ast_set_flag(audiohook, flag);
+               }
+       }
+
+       ast_channel_unlock(chan);
+
+       return (audiohook ? 0 : -1);
+}
index c90469b..af782ed 100644 (file)
@@ -1587,3 +1587,15 @@ int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2)
 
        return 0;
 }
+
+int ast_frame_clear(struct ast_frame *frame)
+{
+       struct ast_frame *next;
+
+       for (next = AST_LIST_NEXT(frame, frame_list);
+                frame;
+                frame = next, next = frame ? AST_LIST_NEXT(frame, frame_list) : NULL) {
+               memset(frame->data.ptr, 0, frame->datalen);
+       }
+       return 0;
+}
index e4fd82c..4faf464 100644 (file)
@@ -116,19 +116,6 @@ static const struct ast_datastore_info mute_datastore = {
        .destroy = destroy_callback
 };
 
-/*! \brief Wipe out all audio samples from an ast_frame. Clean it. */
-static void ast_frame_clear(struct ast_frame *frame)
-{
-       struct ast_frame *next;
-
-       for (next = AST_LIST_NEXT(frame, frame_list);
-               frame;
-               frame = next, next = frame ? AST_LIST_NEXT(frame, frame_list) : NULL) {
-               memset(frame->data.ptr, 0, frame->datalen);
-        }
-}
-
-
 /*! \brief The callback from the audiohook subsystem. We basically get a frame to have fun with */
 static int mute_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
 {