[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