[asterisk-commits] dvossel: branch dvossel/hd_confbridge r310879 - in /team/dvossel/hd_confbridg...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Mar 15 11:06:57 CDT 2011


Author: dvossel
Date: Tue Mar 15 11:06:53 2011
New Revision: 310879

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=310879
Log:
Added ability to record conference calls.

Modified:
    team/dvossel/hd_confbridge/apps/app_confbridge.c
    team/dvossel/hd_confbridge/apps/confbridge/conf_config_parser.c
    team/dvossel/hd_confbridge/apps/confbridge/include/confbridge.h
    team/dvossel/hd_confbridge/configs/confbridge.conf.sample

Modified: team/dvossel/hd_confbridge/apps/app_confbridge.c
URL: http://svnview.digium.com/svn/asterisk/team/dvossel/hd_confbridge/apps/app_confbridge.c?view=diff&rev=310879&r1=310878&r2=310879
==============================================================================
--- team/dvossel/hd_confbridge/apps/app_confbridge.c (original)
+++ team/dvossel/hd_confbridge/apps/app_confbridge.c Tue Mar 15 11:06:53 2011
@@ -117,6 +117,151 @@
 	return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
 }
 
+static struct ast_frame *rec_read(struct ast_channel *ast)
+{
+	return &ast_null_frame;
+}
+static int rec_write(struct ast_channel *ast, struct ast_frame *f)
+{
+	return 0;
+}
+static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, void *data, int *cause);
+static struct ast_channel_tech record_tech = {
+	.type = "ConfBridgeRec",
+	.description = "Conference Bridge Recording Channel",
+	.requester = rec_request,
+	.read = rec_read,
+	.write = rec_write,
+};
+static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, void *data, int *cause)
+{
+	struct ast_channel *tmp;
+	struct ast_format fmt;
+	if (!(tmp = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", NULL, 0,
+		"ConfBrideRecorder/%d%d",
+		(int) ast_random(),
+		(int) ast_random()))) {
+		return NULL;
+	}
+	ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0);
+	tmp->tech = &record_tech;
+	ast_format_cap_add_all(tmp->nativeformats);
+	ast_format_copy(&tmp->writeformat, &fmt);
+	ast_format_copy(&tmp->rawwriteformat, &fmt);
+	ast_format_copy(&tmp->readformat, &fmt);
+	ast_format_copy(&tmp->rawreadformat, &fmt);
+	return tmp;
+}
+
+static void *record_thread(void *obj)
+{
+	struct conference_bridge *conference_bridge = obj;
+	struct ast_app *mixmonapp = pbx_findapp("MixMonitor");
+	struct ast_channel *chan;
+	struct ast_str *filename = ast_str_alloca(128);
+	time_t now;
+
+	if (!mixmonapp) {
+		ao2_ref(conference_bridge, -1);
+		return NULL;
+	}
+
+	ao2_lock(conference_bridge);
+	if (!(conference_bridge->record_chan)) {
+		conference_bridge->record_thread = AST_PTHREADT_NULL;
+		ao2_unlock(conference_bridge);
+		ao2_ref(conference_bridge, -1);
+		return NULL;
+	}
+	chan = ast_channel_ref(conference_bridge->record_chan);
+
+	time(&now);
+	ast_str_append(&filename, 0, "confbridge-%s-%u.wav",
+		conference_bridge->name,
+		(unsigned int) now);
+	ao2_unlock(conference_bridge);
+
+	ast_answer(chan);
+	pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
+	ast_bridge_join(conference_bridge->bridge, chan, NULL, NULL);
+
+	chan = ast_channel_unref(chan);
+
+	ao2_lock(conference_bridge);
+	conference_bridge->record_thread = AST_PTHREADT_NULL;
+	ao2_unlock(conference_bridge);
+
+	ao2_ref(conference_bridge, -1);
+	return NULL;
+}
+
+/*!
+ * \internal
+ * \brief Stops the confbridge recording thread.
+ *
+ * \note do not call this function with any locks
+ */
+static int conf_stop_record(struct conference_bridge *conference_bridge)
+{
+	ao2_lock(conference_bridge);
+
+	if (conference_bridge->record_thread != AST_PTHREADT_NULL) {
+		struct ast_channel *chan = ast_channel_ref(conference_bridge->record_chan);
+		pthread_t thread = conference_bridge->record_thread;
+		ao2_unlock(conference_bridge);
+
+		ast_bridge_remove(conference_bridge->bridge, chan);
+		chan = ast_channel_unref(chan);
+		pthread_join(thread, NULL);
+
+		ao2_lock(conference_bridge);
+	}
+
+	if (conference_bridge->record_chan) {
+		ast_hangup(conference_bridge->record_chan);
+		conference_bridge->record_chan = NULL;
+	}
+
+	ao2_unlock(conference_bridge);
+	return 0;
+}
+
+static int conf_start_record(struct conference_bridge *conference_bridge)
+{
+	struct ast_format_cap *cap = ast_format_cap_alloc_nolock();
+	struct ast_format tmpfmt;
+	int cause;
+
+	ao2_lock(conference_bridge);
+	if (conference_bridge->record_chan || conference_bridge->record_thread != AST_PTHREADT_NULL) {
+		ao2_unlock(conference_bridge);
+		return 0; /* already recording */
+	}
+	if (!cap) {
+		ao2_unlock(conference_bridge);
+		return -1;
+	}
+	if (!pbx_findapp("MixMonitor")) {
+		ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
+		cap = ast_format_cap_destroy(cap);
+		ao2_unlock(conference_bridge);
+		return -1;
+	}
+	ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
+	if (!(conference_bridge->record_chan = ast_request("ConfBridgeRec", cap, NULL, "", &cause))) {
+		cap = ast_format_cap_destroy(cap);
+		ao2_unlock(conference_bridge);
+		return -1;
+	}
+
+	cap = ast_format_cap_destroy(cap);
+	ao2_ref(conference_bridge, +1); /* give the record thread a ref */
+	ao2_unlock(conference_bridge);
+
+	ast_pthread_create_detached_background(&conference_bridge->record_thread, NULL, record_thread, conference_bridge);
+	return 0;
+}
+
 /*!
  * \brief Announce number of users in the conference bridge to the caller
  *
@@ -320,6 +465,7 @@
 {
 	struct conference_bridge *conference_bridge = NULL;
 	struct conference_bridge tmp;
+	int start_record = 0;
 
 	ast_copy_string(tmp.name, name, sizeof(tmp.name));
 
@@ -360,6 +506,7 @@
 		}
 
 		/* Setup conference bridge parameters */
+		conference_bridge->record_thread = AST_PTHREADT_NULL;
 		ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
 		memcpy(&conference_bridge->b_profile, &conference_bridge_user->b_profile, sizeof(conference_bridge->b_profile));
 
@@ -381,6 +528,9 @@
 		/* Link it into the conference bridges container */
 		ao2_link(conference_bridges, conference_bridge);
 
+		if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) {
+			start_record = 1;
+		}
 		ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
 	}
 
@@ -415,6 +565,10 @@
 	}
 
 	ao2_unlock(conference_bridge);
+
+	if (start_record) {
+		conf_start_record(conference_bridge);
+	}
 
 	return conference_bridge;
 }
@@ -485,6 +639,10 @@
 
 	/* Done mucking with the conference bridge, huzzah */
 	ao2_unlock(conference_bridge);
+
+	if (!conference_bridge->users) {
+		conf_stop_record(conference_bridge);
+	}
 
 	ao2_ref(conference_bridge, -1);
 }
@@ -1150,12 +1308,24 @@
 
 	conf_destroy_config();
 
+	ast_channel_unregister(&record_tech);
+	record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
+
 	return res;
 }
 
 /*! \brief Called when module is being loaded */
 static int load_module(void)
 {
+	if (!(record_tech.capabilities = ast_format_cap_alloc())) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
+	ast_format_cap_add_all(record_tech.capabilities);
+	if (ast_channel_register(&record_tech)) {
+		ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
 	/* Create a container to hold the conference bridges */
 	if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
 		return AST_MODULE_LOAD_DECLINE;

Modified: team/dvossel/hd_confbridge/apps/confbridge/conf_config_parser.c
URL: http://svnview.digium.com/svn/asterisk/team/dvossel/hd_confbridge/apps/confbridge/conf_config_parser.c?view=diff&rev=310879&r1=310878&r2=310879
==============================================================================
--- team/dvossel/hd_confbridge/apps/confbridge/conf_config_parser.c (original)
+++ team/dvossel/hd_confbridge/apps/confbridge/conf_config_parser.c Tue Mar 15 11:06:53 2011
@@ -143,6 +143,7 @@
 	ao2_lock(b_profile);
 	/* set defaults */
 	b_profile->internal_sample_rate = 0;
+	b_profile->flags = 0;
 
 	for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
 		if (!strcasecmp(var->name, "internal_sample_rate")) {
@@ -154,6 +155,10 @@
 			}
 		} else if (!strcasecmp(var->name, "type")) {
 			continue;
+		} else if (!strcasecmp(var->name, "record_conference")) {
+			b_profile->flags = ast_true(var->value) ?
+				b_profile->flags | BRIDGE_OPT_RECORD_CONFERENCE :
+				b_profile->flags & ~BRIDGE_OPT_RECORD_CONFERENCE;
 		} else {
 			ast_log(LOG_WARNING, "Unknown option '%s' at line %d of %s is not supported.\n",
 				var->name, var->lineno, CONF_CONFIG);
@@ -536,6 +541,9 @@
 
 	ast_cli(a->fd,"--------------------------------------------\n");
 	ast_cli(a->fd,"Name:                 %s\n", b_profile.name);
+	ast_cli(a->fd,"Record Conference:    %s\n",
+		b_profile.flags & BRIDGE_OPT_RECORD_CONFERENCE ?
+		"yes" : "no");
 	if (b_profile.internal_sample_rate) {
 		snprintf(tmp, sizeof(tmp), "%d", b_profile.internal_sample_rate);
 	} else {

Modified: team/dvossel/hd_confbridge/apps/confbridge/include/confbridge.h
URL: http://svnview.digium.com/svn/asterisk/team/dvossel/hd_confbridge/apps/confbridge/include/confbridge.h?view=diff&rev=310879&r1=310878&r2=310879
==============================================================================
--- team/dvossel/hd_confbridge/apps/confbridge/include/confbridge.h (original)
+++ team/dvossel/hd_confbridge/apps/confbridge/include/confbridge.h Tue Mar 15 11:06:53 2011
@@ -42,6 +42,11 @@
 	USER_OPT_WAITMARKED =   (1 << 7), /*!< Set if the conference must wait for a marked user before starting */
 	USER_OPT_DENOISE =      (1 << 8), /*!< Sets if denoise filter should be used on audio before mixing. */
 	USER_OPT_ANNOUNCE_JOIN_LEAVE = (1 << 9), /*!< Sets if the user's name should be recorded and announced on join and leave. */
+};
+
+
+enum bridge_profile_flags {
+	BRIDGE_OPT_RECORD_CONFERENCE = (1 << 0), /*!< Set if the conference should be recorded */
 };
 
 enum conf_menu_action_id {
@@ -103,6 +108,7 @@
 
 struct bridge_profile {
 	char name[64];
+	unsigned int flags;
 	unsigned int internal_sample_rate;     /*!< The internal sample rate of the bridge. 0 when set to auto adjust mode. */
 	int delme;
 };
@@ -122,6 +128,8 @@
 	unsigned int markedusers;                                         /*!< Number of marked users present */
 	unsigned int locked:1;                                            /*!< Is this conference bridge locked? */
 	struct ast_channel *playback_chan;                                /*!< Channel used for playback into the conference bridge */
+	struct ast_channel *record_chan;                                  /*!< Channel used for recording the conference */
+	pthread_t record_thread;                                          /*!< The thread the recording chan lives in */
 	ast_mutex_t playback_lock;                                        /*!< Lock used for playback channel */
 	AST_LIST_HEAD_NOLOCK(, conference_bridge_user) users_list;        /*!< List of users participating in the conference bridge */
 };

Modified: team/dvossel/hd_confbridge/configs/confbridge.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/dvossel/hd_confbridge/configs/confbridge.conf.sample?view=diff&rev=310879&r1=310878&r2=310879
==============================================================================
--- team/dvossel/hd_confbridge/configs/confbridge.conf.sample (original)
+++ team/dvossel/hd_confbridge/configs/confbridge.conf.sample Tue Mar 15 11:06:53 2011
@@ -40,6 +40,13 @@
 ; --- ConfBridge Bridge Profile Options ---
 [default_bridge]
 type=bridge
+;record_conference=yes         ; Records the conference call starting when the first user
+                               ; enters the room, and ending when the last user exits the room.
+                               ; The default recorded filename is
+                               ; 'confbridge-<name of conference bridge>-<start time>.wav
+                               ; and the default format is 8khz slinear.  This file will be
+                               ; located in the configured monitoring directory in asterisk.conf.
+
 ;internal_sample_rate=auto     ; Sets the internal native sample rate the
                                ; conference is mixed at.  This is set to automatically
                                ; adjust the sample rate to the best quality by default.




More information about the asterisk-commits mailing list