mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-30 18:40:46 +00:00
Added MixMonitorMute manager command
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
This commit is contained in:
@@ -46,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
#include "asterisk/app.h"
|
#include "asterisk/app.h"
|
||||||
#include "asterisk/channel.h"
|
#include "asterisk/channel.h"
|
||||||
#include "asterisk/autochan.h"
|
#include "asterisk/autochan.h"
|
||||||
|
#include "asterisk/manager.h"
|
||||||
|
|
||||||
/*** DOCUMENTATION
|
/*** DOCUMENTATION
|
||||||
<application name="MixMonitor" language="en_US">
|
<application name="MixMonitor" language="en_US">
|
||||||
@@ -124,6 +125,26 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
<ref type="application">MixMonitor</ref>
|
<ref type="application">MixMonitor</ref>
|
||||||
</see-also>
|
</see-also>
|
||||||
</application>
|
</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>
|
||||||
|
|
||||||
***/
|
***/
|
||||||
|
|
||||||
@@ -606,6 +627,73 @@ static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_
|
|||||||
return CLI_SUCCESS;
|
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[] = {
|
static struct ast_cli_entry cli_mixmonitor[] = {
|
||||||
AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
|
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));
|
ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
|
||||||
res = ast_unregister_application(stop_app);
|
res = ast_unregister_application(stop_app);
|
||||||
res |= ast_unregister_application(app);
|
res |= ast_unregister_application(app);
|
||||||
|
res |= ast_manager_unregister("MixMonitorMute");
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -628,6 +717,7 @@ static int load_module(void)
|
|||||||
ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
|
ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
|
||||||
res = ast_register_application_xml(app, mixmonitor_exec);
|
res = ast_register_application_xml(app, mixmonitor_exec);
|
||||||
res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
|
res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
|
||||||
|
res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@@ -62,6 +62,8 @@ enum ast_audiohook_flags {
|
|||||||
* slinfactories. We will flush the factories if they contain too many samples.
|
* slinfactories. We will flush the factories if they contain too many samples.
|
||||||
*/
|
*/
|
||||||
AST_AUDIOHOOK_SMALL_QUEUE = (1 << 3),
|
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 */
|
#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);
|
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)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -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)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -127,6 +127,7 @@ int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohoo
|
|||||||
int our_factory_ms;
|
int our_factory_ms;
|
||||||
int other_factory_samples;
|
int other_factory_samples;
|
||||||
int other_factory_ms;
|
int other_factory_ms;
|
||||||
|
int muteme = 0;
|
||||||
|
|
||||||
/* Update last feeding time to be current */
|
/* Update last feeding time to be current */
|
||||||
*rwtime = ast_tvnow();
|
*rwtime = ast_tvnow();
|
||||||
@@ -151,6 +152,17 @@ int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohoo
|
|||||||
ast_slinfactory_flush(other_factory);
|
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 */
|
/* Write frame out to respective factory */
|
||||||
ast_slinfactory_feed(factory, frame);
|
ast_slinfactory_feed(factory, frame);
|
||||||
|
|
||||||
@@ -1001,3 +1013,37 @@ int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_dir
|
|||||||
|
|
||||||
return 0;
|
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);
|
||||||
|
}
|
||||||
|
12
main/frame.c
12
main/frame.c
@@ -1587,3 +1587,15 @@ int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2)
|
|||||||
|
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
@@ -116,19 +116,6 @@ static const struct ast_datastore_info mute_datastore = {
|
|||||||
.destroy = destroy_callback
|
.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 */
|
/*! \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)
|
static int mute_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user