[svn-commits] jrose: trunk r393309 - in /trunk: ./ apps/ bridges/ configs/ include/asterisk...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Jul 1 11:01:27 CDT 2013


Author: jrose
Date: Mon Jul  1 11:01:24 2013
New Revision: 393309

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393309
Log:
bridge_features: Support One touch Monitor/MixMonitor

In addition to porting those features, they now enjoy greater feature parity
with one another. Specifically, AutoMixMon now has a start and stop
message that can be specified with TOUCH_MIXMONITOR_MESSAGE_START and
TOUCH_MIXMONITOR_MESSAGE_STOP.

(closes issue ASTERISK-21553)
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/2620/

Added:
    trunk/include/asterisk/mixmonitor.h   (with props)
    trunk/main/mixmonitor.c   (with props)
Modified:
    trunk/CHANGES
    trunk/apps/app_mixmonitor.c
    trunk/bridges/bridge_builtin_features.c
    trunk/configs/features.conf.sample
    trunk/include/asterisk/channel.h
    trunk/include/asterisk/features_config.h
    trunk/main/features_config.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=393309&r1=393308&r2=393309
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Mon Jul  1 11:01:24 2013
@@ -283,7 +283,7 @@
    will now apply the feature to the calling party while use of a lowercase
    letter will apply that feature to the called party.
 
- * Add support for automixmonitor to the BRIDGE_FEATURES channel variable.
+ * Add support for automixmon to the BRIDGE_FEATURES channel variable.
 
  * Parking has been pulled from core and placed into a separate module called
    res_parking. See Parking changes below for more details.
@@ -291,6 +291,14 @@
  * You can now have the settings for a channel updated using the FEATURE()
    and FEATUREMAP() functions inherited to child channels by setting
    FEATURE(inherit)=yes.
+
+ * automixmon now supports additional channel variables from automon including:
+   TOUCH_MIXMONITOR_PREFIX, TOUCH_MIXMONITOR_MESSAGE_START,
+   and TOUCH_MIXMONITOR_MESSAGE_STOP
+
+ * A new general features.conf option 'recordingfailsound' has been added which
+   allowssetting a failure sound for a user tries to invoke a recording feature
+   such as automon or automixmon and it fails.
 
 Logging
 -------------------

Modified: trunk/apps/app_mixmonitor.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_mixmonitor.c?view=diff&rev=393309&r1=393308&r2=393309
==============================================================================
--- trunk/apps/app_mixmonitor.c (original)
+++ trunk/apps/app_mixmonitor.c Mon Jul  1 11:01:24 2013
@@ -56,6 +56,7 @@
 #include "asterisk/mod_format.h"
 #include "asterisk/linkedlists.h"
 #include "asterisk/test.h"
+#include "asterisk/mixmonitor.h"
 
 /*** DOCUMENTATION
 	<application name="MixMonitor" language="en_US">
@@ -1246,6 +1247,31 @@
 	return AMI_SUCCESS;
 }
 
+static int start_mixmonitor_callback(struct ast_channel *chan, const char *filename, const char *options)
+{
+	char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
+	struct ast_flags flags = { 0 };
+	char args[PATH_MAX] = "";
+	int res;
+
+	if (!ast_strlen_zero(options)) {
+		ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
+	}
+
+	snprintf(args, sizeof(args), "%s,%s", filename, options);
+
+	ast_channel_lock(chan);
+	res = mixmonitor_exec(chan, args);
+	ast_channel_unlock(chan);
+
+	return res;
+}
+
+static int stop_mixmonitor_callback(struct ast_channel *chan, const char *mixmonitor_id)
+{
+	return stop_mixmonitor_full(chan, mixmonitor_id);
+}
+
 static int manager_mixmonitor(struct mansession *s, const struct message *m)
 {
 	struct ast_channel *c = NULL;
@@ -1356,6 +1382,21 @@
 	AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
 };
 
+static int set_mixmonitor_methods(void)
+{
+	struct ast_mixmonitor_methods mixmonitor_methods = {
+		.start = start_mixmonitor_callback,
+		.stop = stop_mixmonitor_callback,
+	};
+
+	return ast_set_mixmonitor_methods(&mixmonitor_methods);
+}
+
+static int clear_mixmonitor_methods(void)
+{
+	return ast_clear_mixmonitor_methods();
+}
+
 static int unload_module(void)
 {
 	int res;
@@ -1366,6 +1407,7 @@
 	res |= ast_manager_unregister("MixMonitorMute");
 	res |= ast_manager_unregister("MixMonitor");
 	res |= ast_manager_unregister("StopMixMonitor");
+	res |= clear_mixmonitor_methods();
 
 	return res;
 }
@@ -1380,6 +1422,7 @@
 	res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
 	res |= ast_manager_register_xml("MixMonitor", 0, manager_mixmonitor);
 	res |= ast_manager_register_xml("StopMixMonitor", 0, manager_stop_mixmonitor);
+	res |= set_mixmonitor_methods();
 
 	return res;
 }

Modified: trunk/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/bridges/bridge_builtin_features.c?view=diff&rev=393309&r1=393308&r2=393309
==============================================================================
--- trunk/bridges/bridge_builtin_features.c (original)
+++ trunk/bridges/bridge_builtin_features.c Mon Jul  1 11:01:24 2013
@@ -50,6 +50,9 @@
 #include "asterisk/pbx.h"
 #include "asterisk/parking.h"
 #include "asterisk/features_config.h"
+#include "asterisk/monitor.h"
+#include "asterisk/mixmonitor.h"
+#include "asterisk/audiohook.h"
 
 /*!
  * \brief Helper function that presents dialtone and grabs extension
@@ -463,6 +466,304 @@
 	return 0;
 }
 
+static void stop_automonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg)
+{
+	const char *stop_message;
+
+	ast_channel_lock(bridge_channel->chan);
+	stop_message = pbx_builtin_getvar_helper(bridge_channel->chan, "TOUCH_MONITOR_MESSAGE_STOP");
+	stop_message = ast_strdupa(S_OR(stop_message, ""));
+	ast_channel_unlock(bridge_channel->chan);
+
+	ast_verb(3, "AutoMonitor used to stop recording call.\n");
+
+	ast_channel_lock(peer_chan);
+	if (ast_channel_monitor(peer_chan)) {
+		if (ast_channel_monitor(peer_chan)->stop(peer_chan, 1)) {
+			ast_verb(3, "Cannot stop AutoMonitor for %s\n", ast_channel_name(bridge_channel->chan));
+			if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
+				ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
+			}
+			ast_channel_unlock(peer_chan);
+			return;
+		}
+	} else {
+		/* Something else removed the Monitor before we got to it. */
+		ast_channel_unlock(peer_chan);
+		return;
+	}
+
+	ast_channel_unlock(peer_chan);
+
+	if (features_cfg && !(ast_strlen_zero(features_cfg->courtesytone))) {
+		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
+		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
+	}
+
+	if (!ast_strlen_zero(stop_message)) {
+		ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
+		ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
+	}
+}
+
+enum set_touch_variables_res {
+	SET_TOUCH_SUCCESS = 0,
+	SET_TOUCH_UNSET,
+	SET_TOUCH_ALLOC_FAILURE,
+};
+
+static int set_touch_variables(struct ast_channel *chan, int is_mixmonitor, char **touch_format, char **touch_monitor, char **touch_monitor_prefix)
+{
+	enum set_touch_variables_res res = SET_TOUCH_UNSET;
+	const char *c_touch_format, *c_touch_monitor, *c_touch_monitor_prefix;
+
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	c_touch_format = pbx_builtin_getvar_helper(chan, is_mixmonitor ? "TOUCH_MIXMONITOR_FORMAT" : "TOUCH_MONITOR_FORMAT");
+
+	if (!ast_strlen_zero(c_touch_format)) {
+		if (!(*touch_format = ast_strdup(c_touch_format))) {
+			return SET_TOUCH_ALLOC_FAILURE;
+		}
+		res = SET_TOUCH_SUCCESS;
+	}
+
+	c_touch_monitor = pbx_builtin_getvar_helper(chan, is_mixmonitor ? "TOUCH_MIXMONITOR" : "TOUCH_MONITOR");
+
+	if (!ast_strlen_zero(c_touch_monitor)) {
+		if (!(*touch_monitor = ast_strdup(c_touch_monitor))) {
+			return SET_TOUCH_ALLOC_FAILURE;
+		}
+		res = SET_TOUCH_SUCCESS;
+	}
+
+	c_touch_monitor_prefix = pbx_builtin_getvar_helper(chan, is_mixmonitor ? "TOUCH_MIXMONITOR_PREFIX" : "TOUCH_MONITOR_PREFIX");
+
+	if (!ast_strlen_zero(c_touch_monitor_prefix)) {
+		if (!(*touch_monitor_prefix = ast_strdup(c_touch_monitor_prefix))) {
+			return SET_TOUCH_ALLOC_FAILURE;
+		}
+		res = SET_TOUCH_SUCCESS;
+	}
+
+	return res;
+}
+
+static int feature_automonitor(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	char *caller_chan_id = NULL, *peer_chan_id = NULL, *touch_filename = NULL;
+	size_t len;
+	const char *automon_message;
+	int x;
+	enum set_touch_variables_res set_touch_res;
+
+	RAII_VAR(char *, touch_format, NULL, ast_free);
+	RAII_VAR(char *, touch_monitor, NULL, ast_free);
+	RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
+
+	RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup);
+	RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup);
+
+	features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
+	peer_chan = ast_bridge_peer(bridge, bridge_channel->chan);
+
+	if (!peer_chan) {
+		ast_verb(3, "Cannot start AutoMonitor for %s - can not determine peer in bridge.\n", ast_channel_name(bridge_channel->chan));
+		if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
+			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
+		}
+		return 0;
+	}
+
+	if (ast_channel_monitor(peer_chan)) {
+		stop_automonitor(bridge_channel, peer_chan, features_cfg);
+		return 0;
+	}
+
+	if ((set_touch_res = set_touch_variables(bridge_channel->chan, 0, &touch_format, &touch_monitor, &touch_monitor_prefix))) {
+		if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
+			return 0;
+		}
+		if (set_touch_variables(peer_chan, 0, &touch_format, &touch_monitor, &touch_monitor_prefix) == SET_TOUCH_ALLOC_FAILURE) {
+			return 0;
+		}
+	}
+
+	if (!ast_strlen_zero(touch_monitor)) {
+		len = strlen(touch_monitor) + 50;
+		touch_filename = ast_alloca(len);
+		snprintf(touch_filename, len, "%s-%ld-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), touch_monitor);
+	} else {
+		caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
+			ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
+		peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid,
+			ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan)));
+		len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50;
+		touch_filename = ast_alloca(len);
+		snprintf(touch_filename, len, "%s-%ld-%s-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), caller_chan_id, peer_chan_id);
+	}
+
+	for ( x = 0; x < strlen(touch_filename); x++) {
+		if (touch_filename[x] == '/') {
+			touch_filename[x] = '-';
+		}
+	}
+
+	ast_verb(3, "AutoMonitor used to record call. Filename: %s\n", touch_filename);
+
+	if (ast_monitor_start(peer_chan, touch_format, touch_filename, 1, X_REC_IN | X_REC_OUT)) {
+		ast_verb(3, "automon feature was tried by '%s' but monitor failed to start.\n", ast_channel_name(bridge_channel->chan));
+		return 0;
+	}
+
+	ast_channel_lock(bridge_channel->chan);
+	if ((automon_message = pbx_builtin_getvar_helper(bridge_channel->chan, "TOUCH_MONITOR_MESSAGE_START"))) {
+		automon_message = ast_strdupa(automon_message);
+	}
+	ast_channel_unlock(bridge_channel->chan);
+
+	if ((features_cfg = ast_get_chan_features_general_config(bridge_channel->chan)) && !(ast_strlen_zero(features_cfg->courtesytone))) {
+		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
+		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
+	}
+
+	if (!ast_strlen_zero(automon_message)) {
+		ast_bridge_channel_queue_playfile(bridge_channel, NULL, automon_message, NULL);
+		ast_bridge_channel_write_playfile(bridge_channel, NULL, automon_message, NULL);
+	}
+
+	pbx_builtin_setvar_helper(peer_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
+
+	return 0;
+}
+
+static void stop_automixmonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg)
+{
+	const char *stop_message;
+
+	ast_channel_lock(bridge_channel->chan);
+	stop_message = pbx_builtin_getvar_helper(bridge_channel->chan, "TOUCH_MIXMONITOR_MESSAGE_STOP");
+	stop_message = ast_strdupa(S_OR(stop_message, ""));
+	ast_channel_unlock(bridge_channel->chan);
+
+	ast_verb(3, "AutoMixMonitor used to stop recording call.\n");
+
+	if (ast_stop_mixmonitor(peer_chan, NULL)) {
+		ast_verb(3, "Failed to stop Mixmonitor for %s.\n", ast_channel_name(bridge_channel->chan));
+		if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
+			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
+		}
+		return;
+	}
+
+	if (features_cfg && !(ast_strlen_zero(features_cfg->courtesytone))) {
+		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
+		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
+	}
+
+	if (!ast_strlen_zero(stop_message)) {
+		ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
+		ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
+	}
+
+}
+
+static int feature_automixmonitor(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	char *caller_chan_id = NULL, *peer_chan_id = NULL, *touch_filename = NULL;
+	size_t len;
+	const char *automon_message;
+	static char *mixmonitor_spy_type = "MixMonitor";
+	int count, x;
+	enum set_touch_variables_res set_touch_res;
+
+	RAII_VAR(char *, touch_format, NULL, ast_free);
+	RAII_VAR(char *, touch_monitor, NULL, ast_free);
+	RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
+
+	RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup);
+	RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup);
+
+	features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
+
+	peer_chan = ast_bridge_peer(bridge, bridge_channel->chan);
+
+	if (!peer_chan) {
+		ast_verb(3, "Cannot start AutoMixMonitor for %s - can not determine peer in bridge.\n", ast_channel_name(bridge_channel->chan));
+		if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
+			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
+		}
+		return 0;
+	}
+
+	count = ast_channel_audiohook_count_by_source(peer_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
+	if (count > 0) {
+		stop_automixmonitor(bridge_channel, peer_chan, features_cfg);
+		return 0;
+	}
+
+	if ((set_touch_res = set_touch_variables(bridge_channel->chan, 1, &touch_format, &touch_monitor, &touch_monitor_prefix))) {
+		if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
+			return 0;
+		}
+		if (set_touch_variables(peer_chan, 1, &touch_format, &touch_monitor, &touch_monitor_prefix) == SET_TOUCH_ALLOC_FAILURE) {
+			return 0;
+		}
+	}
+
+	if (!ast_strlen_zero(touch_monitor)) {
+		len = strlen(touch_monitor) + 50;
+		touch_filename = ast_alloca(len);
+		snprintf(touch_filename, len, "%s-%ld-%s.%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), touch_monitor, S_OR(touch_format, "wav"));
+	} else {
+		caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
+			ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
+		peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid,
+			ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan)));
+		len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50;
+		touch_filename = ast_alloca(len);
+		snprintf(touch_filename, len, "%s-%ld-%s-%s.%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), caller_chan_id, peer_chan_id, S_OR(touch_format, "wav"));
+	}
+
+	for ( x = 0; x < strlen(touch_filename); x++) {
+		if (touch_filename[x] == '/') {
+			touch_filename[x] = '-';
+		}
+	}
+
+	ast_verb(3, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename);
+
+	if (ast_start_mixmonitor(peer_chan, touch_filename, "b")) {
+		ast_verb(3, "automixmon feature was tried by '%s' but mixmonitor failed to start.\n", ast_channel_name(bridge_channel->chan));
+
+		if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
+			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
+		}
+
+		return 0;
+	}
+
+	ast_channel_lock(bridge_channel->chan);
+	if ((automon_message = pbx_builtin_getvar_helper(bridge_channel->chan, "TOUCH_MIXMONITOR_MESSAGE_START"))) {
+		automon_message = ast_strdupa(automon_message);
+	}
+	ast_channel_unlock(bridge_channel->chan);
+
+	if ((features_cfg = ast_get_chan_features_general_config(bridge_channel->chan)) && !(ast_strlen_zero(features_cfg->courtesytone))) {
+		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
+		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
+	}
+
+	if (!ast_strlen_zero(automon_message)) {
+		ast_bridge_channel_queue_playfile(bridge_channel, NULL, automon_message, NULL);
+		ast_bridge_channel_write_playfile(bridge_channel, NULL, automon_message, NULL);
+	}
+
+	pbx_builtin_setvar_helper(peer_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
+
+	return 0;
+}
+
 /*! \brief Internal built in feature for hangup */
 static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
@@ -485,6 +786,8 @@
 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_BLINDTRANSFER, feature_blind_transfer, NULL);
 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, feature_attended_transfer, NULL);
 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL);
+	ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMON, feature_automonitor, NULL);
+	ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMIXMON, feature_automixmonitor, NULL);
 
 	/* Bump up our reference count so we can't be unloaded */
 	ast_module_ref(ast_module_info->self);

Modified: trunk/configs/features.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/features.conf.sample?view=diff&rev=393309&r1=393308&r2=393309
==============================================================================
--- trunk/configs/features.conf.sample (original)
+++ trunk/configs/features.conf.sample Mon Jul  1 11:01:24 2013
@@ -105,6 +105,8 @@
 ;pickupfailsound = beeperr      ; to indicate that the pickup failed (default: no sound)
 ;featuredigittimeout = 1000     ; Max time (ms) between digits for
                                 ; feature activation  (default is 1000 ms)
+;recordingfailsound = beeperr   ; indicates that a one-touch monitor or one-touch mixmonitor feature failed
+								; to be applied to the call. (default: no sound)
 ;atxfernoanswertimeout = 15     ; Timeout for answer on attended transfer default is 15 seconds.
 ;atxferdropcall = no            ; If someone does an attended transfer, then hangs up before the transferred
                                 ; caller is connected, then by default, the system will try to call back the

Modified: trunk/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/channel.h?view=diff&rev=393309&r1=393308&r2=393309
==============================================================================
--- trunk/include/asterisk/channel.h (original)
+++ trunk/include/asterisk/channel.h Mon Jul  1 11:01:24 2013
@@ -2595,6 +2595,17 @@
  * \since 1.8
  */
 #define ast_channel_unref(c) ({ ao2_ref(c, -1); (struct ast_channel *) (NULL); })
+
+/*!
+ * \brief Cleanup a channel reference
+ *
+ * \param c the channel (NULL tolerant)
+ *
+ * \retval NULL always
+ *
+ * \since 12.0.0
+ */
+#define ast_channel_cleanup(c) ({ ao2_cleanup(c); (struct ast_channel *) (NULL); })
 
 /*! Channel Iterating @{ */
 

Modified: trunk/include/asterisk/features_config.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/features_config.h?view=diff&rev=393309&r1=393308&r2=393309
==============================================================================
--- trunk/include/asterisk/features_config.h (original)
+++ trunk/include/asterisk/features_config.h Mon Jul  1 11:01:24 2013
@@ -30,6 +30,8 @@
 	AST_DECLARE_STRING_FIELDS(
 		/*! Sound played when automon or automixmon features are used */
 		AST_STRING_FIELD(courtesytone);
+		/*! Sound played when automon or automixmon features fail when used */
+		AST_STRING_FIELD(recordingfailsound);
 	);
 	/*! Milliseconds allowed between digit presses when entering feature code */
 	unsigned int featuredigittimeout;

Added: trunk/include/asterisk/mixmonitor.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/mixmonitor.h?view=auto&rev=393309
==============================================================================
--- trunk/include/asterisk/mixmonitor.h (added)
+++ trunk/include/asterisk/mixmonitor.h Mon Jul  1 11:01:24 2013
@@ -1,0 +1,105 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Jonathan Rose <jrose at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief loadable MixMonitor functionality
+ *
+ * \author Jonathan Rose <jrose at digium.com>
+ */
+
+/*!
+ * \brief Start a mixmonitor on a channel.
+ * \since 12.0.0
+ *
+ * \param chan Which channel to put the MixMonitor on
+ * \param filename What the name of the file should be
+ * \param options What options should be used for the mixmonitor
+ *
+ * \retval 0 on success
+ * \retval non-zero on failure
+ */
+typedef int (*ast_mixmonitor_start_fn)(struct ast_channel *chan, const char *filename, const char *options);
+
+/*!
+ * \brief Stop a mixmonitor on a channel.
+ * \since 12.0.0
+ *
+ * \param chan Which channel to stop a MixMonitor on
+ * \param mixmon_id Stop the MixMonitor with this mixmonid if it is on the channel (may be NULL)
+ *
+ * \retval 0 on success
+ * \retval non-zero on failure
+ */
+typedef int (*ast_mixmonitor_stop_fn)(struct ast_channel *chan, const char *mixmon_id);
+
+/*!
+ * \brief MixMonitor virtual methods table definition
+ * \since 12.0.0
+ */
+struct ast_mixmonitor_methods {
+	ast_mixmonitor_start_fn start;
+	ast_mixmonitor_stop_fn stop;
+};
+
+/*!
+ * \brief Setup MixMonitor virtual methods table. Use this to provide the MixMonitor functionality from a loadable module.
+ * \since 12.0.0
+ *
+ * \param vmethod_table pointer to vmethod table providing mixmonitor functions
+ *
+ * \retval 0 if successful
+ * \retval non-zero on failure
+ */
+int ast_set_mixmonitor_methods(struct ast_mixmonitor_methods *vmethod_table);
+
+/*!
+ * \brief Clear the MixMonitor virtual methods table. Use this to cleanup function pointers provided by a module that set.
+ * \since 12.0.0
+ *
+ * \retval 0 if successful
+ * \retval non-zero on failure (occurs when methods aren't loaded)
+ */
+int ast_clear_mixmonitor_methods(void);
+
+/*!
+ * \brief Start a mixmonitor on a channel with the given parameters
+ * \since 12.0.0
+ *
+ * \param chan Which channel to apply the MixMonitor to
+ * \param filename filename to use for the recording
+ * \param options Optional arguments to be interpreted by the MixMonitor start function
+ *
+ * \retval 0 if successful
+ * \retval non-zero on failure
+ *
+ * \note This function will always fail is nothing has set the mixmonitor methods
+ */
+int ast_start_mixmonitor(struct ast_channel *chan, const char *filename, const char *options);
+
+/*!
+ * \brief Stop a mixmonitor on a channel with the given parameters
+ * \since 12.0.0
+ *
+ * \param chan Which channel to stop a MixMonitor on (may be NULL if mixmon_id is provided)
+ * \param mixmon_id Which mixmon_id should be stopped (may be NULL if chan is provided)
+ *
+ * \retval 0 if successful
+ * \retval non-zero on failure
+ */
+int ast_stop_mixmonitor(struct ast_channel *chan, const char *mixmon_id);

Propchange: trunk/include/asterisk/mixmonitor.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: trunk/include/asterisk/mixmonitor.h
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Propchange: trunk/include/asterisk/mixmonitor.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: trunk/main/features_config.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/features_config.c?view=diff&rev=393309&r1=393308&r2=393309
==============================================================================
--- trunk/main/features_config.c (original)
+++ trunk/main/features_config.c Mon Jul  1 11:01:24 2013
@@ -38,6 +38,9 @@
 				</configOption>
 				<configOption name="courtesytone">
 					<synopsis>Sound to play when automon or automixmon is activated</synopsis>
+				</configOption>
+				<configOption name="recordingfailsound">
+					<synopsis>Sound to play when automon or automixmon is attempted but fails to start</synopsis>
 				</configOption>
 				<configOption name="transferdigittimeout" default="3000">
 					<synopsis>Milliseconds allowed between digit presses when dialing a transfer destination</synopsis>
@@ -292,6 +295,7 @@
 					<enum name="pickupsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='pickupsound']/synopsis/text())" /></para></enum>
 					<enum name="pickupfailsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='pickupfailsound']/synopsis/text())" /></para></enum>
 					<enum name="courtesytone"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='courtesytone']/synopsis/text())" /></para></enum>
+					<enum name="recordingfailsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='recordingfailsound']/synopsis/text())" /></para></enum>
 				</enumlist>
 			</parameter>
 		</syntax>
@@ -338,6 +342,7 @@
 /*! Default general options */
 #define DEFAULT_FEATURE_DIGIT_TIMEOUT               1000
 #define DEFAULT_COURTESY_TONE                       ""
+#define DEFAULT_RECORDING_FAIL_SOUND                ""
 
 /*! Default xfer options */
 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT              3000
@@ -787,6 +792,8 @@
 		res = ast_parse_arg(value, PARSE_INT32, &general->featuredigittimeout);
 	} else if (!strcasecmp(name, "courtesytone")) {
 		ast_string_field_set(general, courtesytone, value);
+	} else if (!strcasecmp(name, "recordingfailsound")) {
+		ast_string_field_set(general, recordingfailsound, value);
 	} else {
 		/* Unrecognized option */
 		res = -1;
@@ -804,6 +811,8 @@
 		snprintf(buf, len, "%u", general->featuredigittimeout);
 	} else if (!strcasecmp(field, "courtesytone")) {
 		ast_copy_string(buf, general->courtesytone, len);
+	} else if (!strcasecmp(field, "recordingfailsound")) {
+		ast_copy_string(buf, general->recordingfailsound, len);
 	} else {
 		/* Unrecognized option */
 		res = -1;
@@ -1595,8 +1604,10 @@
 
 	aco_option_register_custom(&cfg_info, "featuredigittimeout", ACO_EXACT, global_options,
 			__stringify(DEFAULT_FEATURE_DIGIT_TIMEOUT), general_handler, 0);
+	aco_option_register_custom(&cfg_info, "recordingfailsound", ACO_EXACT, global_options,
+			DEFAULT_RECORDING_FAIL_SOUND, general_handler, 0);
 	aco_option_register_custom(&cfg_info, "courtesytone", ACO_EXACT, global_options,
-			__stringify(DEFAULT_COURTESY_TONE), general_handler, 0);
+			DEFAULT_COURTESY_TONE, general_handler, 0);
 
 	aco_option_register_custom(&cfg_info, "transferdigittimeout", ACO_EXACT, global_options,
 			__stringify(DEFAULT_TRANSFER_DIGIT_TIMEOUT), xfer_handler, 0)

Added: trunk/main/mixmonitor.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/mixmonitor.c?view=auto&rev=393309
==============================================================================
--- trunk/main/mixmonitor.c (added)
+++ trunk/main/mixmonitor.c Mon Jul  1 11:01:24 2013
@@ -1,0 +1,98 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Jonathan Rose <jrose at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief loadable MixMonitor functionality
+ *
+ * \author Jonathan Rose <jrose at digium.com>
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 390830 $")
+
+#include "asterisk/lock.h"
+#include "asterisk/logger.h"
+#include "asterisk/mixmonitor.h"
+#include "asterisk/utils.h"
+#include "asterisk/channel.h"
+
+AST_RWLOCK_DEFINE_STATIC(mixmonitor_lock);
+
+static struct ast_mixmonitor_methods mixmonitor_methods;
+static int table_loaded = 0;
+
+int ast_set_mixmonitor_methods(struct ast_mixmonitor_methods *method_table)
+{
+	SCOPED_WRLOCK(lock, &mixmonitor_lock);
+
+	if (table_loaded) {
+		/* If mixmonitor methods have already been provided, reject the new set */
+		ast_log(LOG_ERROR, "Tried to set mixmonitor methods, but something else has already provided them.\n");
+		return -1;
+	}
+
+	mixmonitor_methods = *method_table;
+
+	table_loaded = 1;
+	return 0;
+}
+
+int ast_clear_mixmonitor_methods(void)
+{
+	SCOPED_WRLOCK(lock, &mixmonitor_lock);
+
+	if (!table_loaded) {
+		ast_log(LOG_ERROR, "Tried to clear mixmonitor methods, but none are currently loaded.\n");
+		return -1;
+	}
+
+	memset(&mixmonitor_methods, 0, sizeof(mixmonitor_methods));
+
+	table_loaded = 0;
+	return 0;
+}
+
+int ast_start_mixmonitor(struct ast_channel *chan, const char *filename, const char *options)
+{
+	SCOPED_RDLOCK(lock, &mixmonitor_lock);
+
+	if (!mixmonitor_methods.start) {
+		ast_log(LOG_ERROR, "No loaded module currently provides MixMonitor starting functionality.\n");
+		return -1;
+	}
+
+	return mixmonitor_methods.start(chan, filename, options);
+}
+
+int ast_stop_mixmonitor(struct ast_channel *chan, const char *mixmon_id)
+{
+	SCOPED_RDLOCK(lock, &mixmonitor_lock);
+
+	if (!mixmonitor_methods.stop) {
+		ast_log(LOG_ERROR, "No loaded module currently provides MixMonitor stopping functionality.\n");
+		return -1;
+	}
+
+	return mixmonitor_methods.stop(chan, mixmon_id);
+}

Propchange: trunk/main/mixmonitor.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: trunk/main/mixmonitor.c
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Propchange: trunk/main/mixmonitor.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain




More information about the svn-commits mailing list