[svn-commits] rmudgett: branch rmudgett/bridge_phase r389038 - in /team/rmudgett/bridge_pha...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri May 17 18:42:51 CDT 2013


Author: rmudgett
Date: Fri May 17 18:42:47 2013
New Revision: 389038

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=389038
Log:
Make ConfBridge use its own unreal announcer channel driver and delete chan_bridge.

Removed:
    team/rmudgett/bridge_phase/channels/chan_bridge.c
Modified:
    team/rmudgett/bridge_phase/apps/app_confbridge.c

Modified: team/rmudgett/bridge_phase/apps/app_confbridge.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/apps/app_confbridge.c?view=diff&rev=389038&r1=389037&r2=389038
==============================================================================
--- team/rmudgett/bridge_phase/apps/app_confbridge.c (original)
+++ team/rmudgett/bridge_phase/apps/app_confbridge.c Fri May 17 18:42:47 2013
@@ -70,6 +70,7 @@
 #include "asterisk/stasis.h"
 #include "asterisk/stasis_bridging.h"
 #include "asterisk/json.h"
+#include "asterisk/core_unreal.h"
 
 /*** DOCUMENTATION
 	<application name="ConfBridge" language="en_US">
@@ -488,42 +489,240 @@
 	send_conf_stasis(conference, chan, "confbridge_unmute", NULL, 1);
 }
 
+static int rec_call(struct ast_channel *chan, const char *addr, int timeout)
+{
+	/* Make sure anyone calling ast_call() for this channel driver is going to fail. */
+	return -1;
+}
 
 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, const char *data, int *cause);
+
 static struct ast_channel_tech record_tech = {
-	.type = "ConfBridgeRec",
+	.type = "CBRec",
 	.description = "Conference Bridge Recording Channel",
 	.requester = rec_request,
+	.call = rec_call,
 	.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, const char *data, int *cause)
 {
-	struct ast_channel *tmp;
-	struct ast_format fmt;
+	struct ast_channel *chan;
+	struct ast_format format;
 	const char *conf_name = data;
-	if (!(tmp = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", NULL, 0,
-		"ConfBridgeRecorder/conf-%s-uid-%d",
-		conf_name,
-		(int) ast_random()))) {
+
+	chan = ast_channel_alloc(1, AST_STATE_UP, NULL, NULL, NULL, NULL, NULL, NULL, 0,
+		"CBRec/conf-%s-uid-%d",
+		conf_name, (int) ast_random());
+	if (!chan) {
 		return NULL;
 	}
-	ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0);
-	ast_channel_tech_set(tmp, &record_tech);
-	ast_format_cap_add_all(ast_channel_nativeformats(tmp));
-	ast_format_copy(ast_channel_writeformat(tmp), &fmt);
-	ast_format_copy(ast_channel_rawwriteformat(tmp), &fmt);
-	ast_format_copy(ast_channel_readformat(tmp), &fmt);
-	ast_format_copy(ast_channel_rawreadformat(tmp), &fmt);
-	return tmp;
+	if (ast_channel_add_bridge_role(chan, "recorder")) {
+		ast_channel_release(chan);
+		return NULL;
+	}
+	ast_format_set(&format, AST_FORMAT_SLINEAR, 0);
+	ast_channel_tech_set(chan, &record_tech);
+	ast_format_cap_add_all(ast_channel_nativeformats(chan));
+	ast_format_copy(ast_channel_writeformat(chan), &format);
+	ast_format_copy(ast_channel_rawwriteformat(chan), &format);
+	ast_format_copy(ast_channel_readformat(chan), &format);
+	ast_format_copy(ast_channel_rawreadformat(chan), &format);
+	return chan;
+}
+
+/*! ConfBridge announcer channel private. */
+struct announce_pvt {
+	/*! Unreal channel driver base class values. */
+	struct ast_unreal_pvt base;
+	/*! Conference bridge associated with this announcer. */
+	struct ast_bridge *bridge;
+};
+
+static int announce_call(struct ast_channel *chan, const char *addr, int timeout)
+{
+	/* Make sure anyone calling ast_call() for this channel driver is going to fail. */
+	return -1;
+}
+
+static int announce_hangup(struct ast_channel *ast)
+{
+	struct announce_pvt *p = ast_channel_tech_pvt(ast);
+	int res;
+
+	if (!p) {
+		return -1;
+	}
+
+	/* give the pvt a ref to fulfill calling requirements. */
+	ao2_ref(p, +1);
+	res = ast_unreal_hangup(&p->base, ast);
+	ao2_ref(p, -1);
+
+	return res;
+}
+
+static struct ast_channel *announce_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
+
+static struct ast_channel_tech announce_tech = {
+	.type = "CBAnn",
+	.description = "Conference Bridge Announcing Channel",
+	.requester = announce_request,
+	.call = announce_call,
+	.hangup = announce_hangup,
+
+	.send_digit_begin = ast_unreal_digit_begin,
+	.send_digit_end = ast_unreal_digit_end,
+	.read = ast_unreal_read,
+	.write = ast_unreal_write,
+	.write_video = ast_unreal_write,
+	.exception = ast_unreal_read,
+	.indicate = ast_unreal_indicate,
+	.fixup = ast_unreal_fixup,
+	.send_html = ast_unreal_sendhtml,
+	.send_text = ast_unreal_sendtext,
+	.queryoption = ast_unreal_queryoption,
+	.setoption = ast_unreal_setoption,
+};
+
+static void announce_pvt_destructor(void *vdoomed)
+{
+	struct announce_pvt *doomed = vdoomed;
+
+	ao2_cleanup(doomed->bridge);
+	doomed->bridge = NULL;
+}
+
+static struct ast_channel *announce_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
+{
+	struct ast_channel *chan;
+	const char *conf_name = data;
+	RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
+	RAII_VAR(struct announce_pvt *, pvt, NULL, ao2_cleanup);
+
+	conference = ao2_find(conference_bridges, conf_name, OBJ_KEY);
+	if (!conference) {
+		return NULL;
+	}
+	ast_assert(conference->bridge != NULL);
+
+	/* Allocate a new private structure and then Asterisk channels */
+	pvt = (struct announce_pvt *) ast_unreal_alloc(sizeof(*pvt), announce_pvt_destructor,
+		cap);
+	if (!pvt) {
+		return NULL;
+	}
+	ast_set_flag(&pvt->base, AST_UNREAL_NO_OPTIMIZATION);
+	ast_copy_string(pvt->base.name, conf_name, sizeof(pvt->base.name));
+	pvt->bridge = conference->bridge;
+	ao2_ref(pvt->bridge, +1);
+
+	chan = ast_unreal_new_channels(&pvt->base, &announce_tech, AST_STATE_UP, AST_STATE_UP,
+		NULL, NULL, requestor, NULL);
+	if (chan) {
+		ast_answer(pvt->base.owner);
+		ast_answer(pvt->base.chan);
+		if (ast_channel_add_bridge_role(pvt->base.chan, "announcer")) {
+			ast_hangup(chan);
+			chan = NULL;
+		}
+	}
+
+	return chan;
+}
+
+/*!
+ * \internal
+ * \brief Remove the announcer channel from the conference.
+ * \since 12.0.0
+ *
+ * \param chan Either channel in the announcer channel pair.
+ *
+ * \return Nothing
+ */
+static void announce_channel_depart(struct ast_channel *chan)
+{
+	struct announce_pvt *p = ast_channel_tech_pvt(chan);
+
+	if (!p) {
+		return;
+	}
+
+	ao2_ref(p, +1);
+	ao2_lock(p);
+	if (!ast_test_flag(&p->base, AST_UNREAL_CARETAKER_THREAD)) {
+		ao2_unlock(p);
+		ao2_ref(p, -1);
+		return;
+	}
+	ast_clear_flag(&p->base, AST_UNREAL_CARETAKER_THREAD);
+	chan = p->base.chan;
+	if (chan) {
+		ast_channel_ref(chan);
+	}
+	ao2_unlock(p);
+	ao2_ref(p, -1);
+	if (chan) {
+		ast_bridge_depart(chan);
+		ast_channel_unref(chan);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Push the announcer channel into the conference.
+ * \since 12.0.0
+ *
+ * \param ast Either channel in the announcer channel pair.
+ *
+ * \return Nothing
+ */
+static int announce_channel_push(struct ast_channel *ast)
+{
+	struct ast_bridge_features *features;
+	RAII_VAR(struct announce_pvt *, p, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel *, chan, NULL, ast_channel_unref);
+
+	ast_channel_lock(ast);
+	p = ast_channel_tech_pvt(ast);
+	if (!p) {
+		ast_channel_unlock(ast);
+		return -1;
+	}
+	ao2_ref(p, +1);
+	chan = p->base.chan;
+	if (!chan) {
+		ast_channel_unlock(ast);
+		return -1;
+	}
+	ast_channel_ref(chan);
+	ast_channel_unlock(ast);
+
+	features = ast_bridge_features_new();
+	if (!features) {
+		return -1;
+	}
+	ast_set_flag(&features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
+
+	/* Impart the output channel into the bridge */
+	if (ast_bridge_impart(p->bridge, chan, NULL, features, 0)) {
+		return -1;
+	}
+	ao2_lock(p);
+	ast_set_flag(&p->base, AST_UNREAL_CARETAKER_THREAD);
+	ao2_unlock(p);
+	return 0;
 }
 
 static void set_rec_filename(struct confbridge_conference *conference, struct ast_str **filename, int is_new)
@@ -596,6 +795,7 @@
 		ao2_ref(conference, -1);
 		return NULL;
 	}
+	ast_set_flag(&features.feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
 
 	/* XXX If we get an EXIT right here, START will essentially be a no-op */
 	while (conference->record_state != CONF_RECORD_EXIT) {
@@ -689,8 +889,7 @@
 static int conf_start_record(struct confbridge_conference *conference)
 {
 	struct ast_format_cap *cap;
-	struct ast_format tmpfmt;
-	int cause;
+	struct ast_format format;
 
 	if (conference->record_state != CONF_RECORD_STOP) {
 		return -1;
@@ -701,18 +900,19 @@
 		return -1;
 	}
 
-	if (!(cap = ast_format_cap_alloc_nolock())) {
+	cap = ast_format_cap_alloc_nolock();
+	if (!cap) {
 		return -1;
 	}
 
-	ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
-
-	if (!(conference->record_chan = ast_request("ConfBridgeRec", cap, NULL, conference->name, &cause))) {
-		cap = ast_format_cap_destroy(cap);
+	ast_format_cap_add(cap, ast_format_set(&format, AST_FORMAT_SLINEAR, 0));
+
+	conference->record_chan = ast_request("CBRec", cap, NULL,
+		conference->name, NULL);
+	cap = ast_format_cap_destroy(cap);
+	if (!conference->record_chan) {
 		return -1;
 	}
-
-	cap = ast_format_cap_destroy(cap);
 
 	conference->record_state = CONF_RECORD_START;
 	ast_mutex_lock(&conference->record_lock);
@@ -923,10 +1123,7 @@
 	ast_debug(1, "Destroying conference bridge '%s'\n", conference->name);
 
 	if (conference->playback_chan) {
-		struct ast_channel *underlying_channel = ast_channel_tech(conference->playback_chan)->bridged_channel(conference->playback_chan, NULL);
-		if (underlying_channel) {
-			ast_hangup(underlying_channel);
-		}
+		announce_channel_depart(conference->playback_chan);
 		ast_hangup(conference->playback_chan);
 		conference->playback_chan = NULL;
 	}
@@ -1360,74 +1557,59 @@
 
 /*!
  * \internal
- * \brief allocates playback chan on a channel
+ * \brief Allocate playback channel for a conference.
  * \pre expects conference to be locked before calling this function
  */
 static int alloc_playback_chan(struct confbridge_conference *conference)
 {
-	int cause;
 	struct ast_format_cap *cap;
-	struct ast_format tmpfmt;
-
-	if (conference->playback_chan) {
-		return 0;
-	}
-	if (!(cap = ast_format_cap_alloc_nolock())) {
+	struct ast_format format;
+
+	cap = ast_format_cap_alloc_nolock();
+	if (!cap) {
 		return -1;
 	}
-	ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
-	if (!(conference->playback_chan = ast_request("Bridge", cap, NULL, "", &cause))) {
-		cap = ast_format_cap_destroy(cap);
+	ast_format_cap_add(cap, ast_format_set(&format, AST_FORMAT_SLINEAR, 0));
+	conference->playback_chan = ast_request("CBAnn", cap, NULL,
+		conference->name, NULL);
+	cap = ast_format_cap_destroy(cap);
+	if (!conference->playback_chan) {
 		return -1;
 	}
-	cap = ast_format_cap_destroy(cap);
-
-	ast_channel_internal_bridge_set(conference->playback_chan, conference->bridge);
-
-	if (ast_call(conference->playback_chan, "", 0)) {
-		ast_hangup(conference->playback_chan);
-		conference->playback_chan = NULL;
-		return -1;
-	}
-
-	ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference->name);
+
+	ast_debug(1, "Created announcer channel '%s' to conference bridge '%s'\n",
+		ast_channel_name(conference->playback_chan), conference->name);
 	return 0;
 }
 
 static int play_sound_helper(struct confbridge_conference *conference, const char *filename, int say_number)
 {
-	struct ast_channel *underlying_channel;
-
 	/* Do not waste resources trying to play files that do not exist */
 	if (!ast_strlen_zero(filename) && !sound_file_exists(filename)) {
 		return 0;
 	}
 
 	ast_mutex_lock(&conference->playback_lock);
-	if (!(conference->playback_chan)) {
-		if (alloc_playback_chan(conference)) {
-			ast_mutex_unlock(&conference->playback_lock);
-			return -1;
-		}
-		underlying_channel = ast_channel_tech(conference->playback_chan)->bridged_channel(conference->playback_chan, NULL);
-	} else {
-		/* Channel was already available so we just need to add it back into the bridge */
-		underlying_channel = ast_channel_tech(conference->playback_chan)->bridged_channel(conference->playback_chan, NULL);
-		if (ast_bridge_impart(conference->bridge, underlying_channel, NULL, NULL, 0)) {
-			ast_mutex_unlock(&conference->playback_lock);
-			return -1;
-		}
+	if (!conference->playback_chan && alloc_playback_chan(conference)) {
+		ast_mutex_unlock(&conference->playback_lock);
+		return -1;
+	}
+	if (announce_channel_push(conference->playback_chan)) {
+		ast_mutex_unlock(&conference->playback_lock);
+		return -1;
 	}
 
 	/* The channel is all under our control, in goes the prompt */
 	if (!ast_strlen_zero(filename)) {
 		ast_stream_and_wait(conference->playback_chan, filename, "");
 	} else if (say_number >= 0) {
-		ast_say_number(conference->playback_chan, say_number, "", ast_channel_language(conference->playback_chan), NULL);
-	}
-
-	ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", ast_channel_name(underlying_channel), conference->bridge);
-	ast_bridge_depart(underlying_channel);
+		ast_say_number(conference->playback_chan, say_number, "",
+			ast_channel_language(conference->playback_chan), NULL);
+	}
+
+	ast_debug(1, "Departing announcer channel '%s' from conference bridge '%s'\n",
+		ast_channel_name(conference->playback_chan), conference->name);
+	announce_channel_depart(conference->playback_chan);
 
 	ast_mutex_unlock(&conference->playback_lock);
 
@@ -3102,6 +3284,46 @@
 	conference->waitingusers--;
 }
 
+/*!
+ * \internal
+ * \brief Unregister a ConfBridge channel technology.
+ * \since 12.0.0
+ *
+ * \param tech What to unregister.
+ *
+ * \return Nothing
+ */
+static void unregister_channel_tech(struct ast_channel_tech *tech)
+{
+	ast_channel_unregister(tech);
+	tech->capabilities = ast_format_cap_destroy(tech->capabilities);
+}
+
+/*!
+ * \internal
+ * \brief Register a ConfBridge channel technology.
+ * \since 12.0.0
+ *
+ * \param tech What to register.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int register_channel_tech(struct ast_channel_tech *tech)
+{
+	tech->capabilities = ast_format_cap_alloc();
+	if (!tech->capabilities) {
+		return -1;
+	}
+	ast_format_cap_add_all(tech->capabilities);
+	if (ast_channel_register(tech)) {
+		ast_log(LOG_ERROR, "Unable to register channel technology %s(%s).\n",
+			tech->type, tech->description);
+		return -1;
+	}
+	return 0;
+}
+
 /*! \brief Called when module is being unloaded */
 static int unload_module(void)
 {
@@ -3132,8 +3354,8 @@
 
 	conf_destroy_config();
 
-	ast_channel_unregister(&record_tech);
-	record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
+	unregister_channel_tech(&announce_tech);
+	unregister_channel_tech(&record_tech);
 
 	return 0;
 }
@@ -3157,13 +3379,8 @@
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
-	if (!(record_tech.capabilities = ast_format_cap_alloc())) {
-		unload_module();
-		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");
+	if (register_channel_tech(&record_tech)
+		|| register_channel_tech(&announce_tech)) {
 		unload_module();
 		return AST_MODULE_LOAD_FAILURE;
 	}




More information about the svn-commits mailing list