[asterisk-commits] mmichelson: branch mmichelson/atxfer_features r393029 - /team/mmichelson/atxf...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Jun 26 18:14:38 CDT 2013


Author: mmichelson
Date: Wed Jun 26 18:14:37 2013
New Revision: 393029

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393029
Log:
Progress on the recall logic for atxferdropcall=no.

Here's what works so far:
* Blond Transfer
* Recalling Transferer if call to target times out

Here's what doesn't work as well:
* Detecting target hangup during blond transfer
* Recalling target if transferer times out or rejects call

I've encountered a bizarre race condition a couple of times during
testing where when I attempt a blond transfer, the hangup hook on
the transferer channel triggers twice and a crash occurs in bridge_softmix.
This is strange for two reasons:

1) I only hung up once
2) There should never have been any involvement from bridge_softmix at all.


Modified:
    team/mmichelson/atxfer_features/main/bridging_basic.c

Modified: team/mmichelson/atxfer_features/main/bridging_basic.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/atxfer_features/main/bridging_basic.c?view=diff&rev=393029&r1=393028&r2=393029
==============================================================================
--- team/mmichelson/atxfer_features/main/bridging_basic.c (original)
+++ team/mmichelson/atxfer_features/main/bridging_basic.c Wed Jun 26 18:14:37 2013
@@ -43,6 +43,7 @@
 #include "asterisk/file.h"
 #include "asterisk/app.h"
 #include "asterisk/bridging_private.h"
+#include "asterisk/dial.h"
 
 #define NORMAL_FLAGS	AST_BRIDGE_FLAG_DISSOLVE_HANGUP | AST_BRIDGE_FLAG_DISSOLVE_EMPTY \
 			| AST_BRIDGE_FLAG_SMART
@@ -345,8 +346,10 @@
 	TRANSFER_DOUBLECHECKING,
 	/* The transfer has reached a successful conclusion */
 	TRANSFER_COMPLETE,
-	/* Transfer target is ringing, Transferer has hung up */
+	/* Transfer target is ringing, Transferer has hung up. atxferdropcall=yes */
 	TRANSFER_BLOND,
+	/* Transfer target is ringing, Transferer has hung up. atxferdropcall=no */
+	TRANSFER_BLOND_NONFINAL,
 	/* Transfer target did not answer, re-call transferer */
 	TRANSFER_RECALLING,
 	/* Transferer did not answer re-call, re-call transfer target */
@@ -406,6 +409,9 @@
 	char exten[AST_MAX_EXTENSION];
 	char context[AST_MAX_CONTEXT];
 	char failsound[PATH_MAX];
+	char transferer_type[AST_CHANNEL_NAME];
+	char transferer_addr[AST_CHANNEL_NAME];
+	struct ast_dial *dial;
 };
 
 static void attended_transfer_properties_destructor(void *obj)
@@ -429,6 +435,9 @@
 		const char *context)
 {
 	struct attended_transfer_properties *props;
+	char *tech;
+	char *addr;
+	char *serial;
 	RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
 
 	props = ao2_alloc(sizeof(*props), attended_transfer_properties_destructor);
@@ -455,6 +464,21 @@
 	props->timeout = ast_samp2tv(xfer_cfg->atxfernoanswertimeout, 1000);
 	ast_copy_string(props->context, get_transfer_context(transferer, context), sizeof(props->context));
 	ast_copy_string(props->failsound, xfer_cfg->xferfailsound, sizeof(props->failsound));
+
+	tech = ast_strdupa(ast_channel_name(props->transferer));
+	addr = strchr(tech, '/');
+	if (!addr) {
+		ast_channel_unref(props->transferer);
+		return NULL;
+	}
+	*addr++ = '\0';
+	serial = strrchr(addr, '-');
+	if (serial) {
+		*serial = '\0';
+	}
+	ast_copy_string(props->transferer_type, tech, sizeof(props->transferer_type));
+	ast_copy_string(props->transferer_addr, addr, sizeof(props->transferer_addr));
+
 	ast_channel_unlock(props->transferer);
 
 	return props;
@@ -617,7 +641,7 @@
 	case STIMULUS_DTMF_ATXFER_COMPLETE:
 	case STIMULUS_TRANSFERER_HANGUP:
 		bridge_unhold(props->transferee_bridge);
-		return TRANSFER_BLOND;
+		return props->atxferdropcall ? TRANSFER_BLOND : TRANSFER_BLOND_NONFINAL;
 	case STIMULUS_TARGET_ANSWER:
 		return TRANSFER_CONSULTING;
 	case STIMULUS_TARGET_HANGUP:
@@ -658,7 +682,7 @@
 		return TRANSFER_FAIL;
 	case STIMULUS_DTMF_ATXFER_COMPLETE:
 	case STIMULUS_TRANSFERER_HANGUP:
-		return TRANSFER_BLOND;
+		return props->atxferdropcall ? TRANSFER_BLOND : TRANSFER_BLOND_NONFINAL;
 	case STIMULUS_TARGET_ANSWER:
 		return TRANSFER_CONSULTING;
 	case STIMULUS_TARGET_HANGUP:
@@ -822,20 +846,88 @@
 	return 0;
 }
 
+static enum attended_transfer_state blond_nonfinal_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus)
+{
+	switch (stimulus) {
+	default:
+	case STIMULUS_NONE:
+	case STIMULUS_DTMF_ATXFER_ABORT:
+	case STIMULUS_DTMF_ATXFER_COMPLETE:
+	case STIMULUS_DTMF_ATXFER_THREEWAY:
+	case STIMULUS_DTMF_ATXFER_SWAP:
+	case STIMULUS_TRANSFERER_HANGUP:
+	case STIMULUS_TRANSFERER_ANSWER:
+		ast_assert(0);
+	case STIMULUS_TRANSFEREE_HANGUP:
+		return TRANSFER_FAIL;
+	case STIMULUS_TARGET_ANSWER:
+		return TRANSFER_RESUME;
+	case STIMULUS_TARGET_HANGUP:
+	case STIMULUS_TIMEOUT:
+		return TRANSFER_RECALLING;
+	}
+}
+
+static void recall_callback(struct ast_dial *dial);
+
 static int recalling_enter(struct attended_transfer_properties *props)
 {
-	/* XXX Need to:
-	 * 1) Request transferer channel
-	 * 2) Add transferer role to channel
-	 * 3) Place transferer channel into transferee bridge
-	 * 4) Place outgoing call to transferer channel
+	RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc_nolock(), ast_format_cap_destroy);
+	struct ast_format fmt;
+
+	if (!cap) {
+		return -1;
+	}
+
+	ast_format_cap_add(cap, ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0));
+
+	/* When we dial the transfer target, since we are communicating
+	 * with a local channel, we can place the local channel in a bridge
+	 * and then call out to it. When recalling the transferer, though, we
+	 * have to use the dialing API because the channel is not local.
 	 */
+
+	/* First, let's lose our reference to the transferer. We're going to
+	 * replace it anyway.
+	 */
+	props->transferer = ast_channel_unref(props->transferer);
+
+	props->dial = ast_dial_create();
+	if (!props->dial) {
+		return -1;
+	}
+
+	if (ast_dial_append(props->dial, props->transferer_type, props->transferer_addr)) {
+		return -1;
+	}
+
+	if (ast_dial_prerun(props->dial, NULL, cap)) {
+		return -1;
+	}
+
+	props->transferer = ast_channel_ref(ast_dial_get_channel(props->dial, 0));
+
+	ast_dial_set_state_callback(props->dial, &recall_callback);
+
+	ao2_ref(props, +1);
+	ast_dial_set_user_data(props->dial, props);
+
+	if (ast_dial_run(props->dial, NULL, 1) == AST_DIAL_RESULT_FAILED) {
+		ao2_ref(props, -1);
+		return -1;
+	}
+
 	return 0;
 }
 
 static enum attended_transfer_state recalling_next(struct attended_transfer_properties *props,
 		enum attended_transfer_stimulus stimulus)
 {
+	/* No matter what the outcome was, we need to kill off the dial */
+	ast_dial_destroy(props->dial);
+	props->dial = NULL;
+
 	switch (stimulus) {
 	default:
 	case STIMULUS_NONE:
@@ -845,10 +937,10 @@
 	case STIMULUS_DTMF_ATXFER_SWAP:
 	case STIMULUS_TARGET_HANGUP:
 	case STIMULUS_TARGET_ANSWER:
-	case STIMULUS_TRANSFERER_HANGUP:
 		ast_assert(0);
 	case STIMULUS_TRANSFEREE_HANGUP:
 		return TRANSFER_FAIL;
+	case STIMULUS_TRANSFERER_HANGUP:
 	case STIMULUS_TIMEOUT:
 		++props->retry_attempts;
 		if (props->retry_attempts >= props->atxfercallbackretries) {
@@ -856,6 +948,7 @@
 		}
 		return TRANSFER_RETRANSFER;
 	case STIMULUS_TRANSFERER_ANSWER:
+		ast_bridge_impart(props->transferee_bridge, props->transferer, NULL, NULL, 1);
 		return TRANSFER_RESUME;
 	}
 }
@@ -945,6 +1038,12 @@
 		.enter = blond_enter,
 		.flags = TRANSFER_STATE_IS_TERMINAL,
 	},
+	[TRANSFER_BLOND_NONFINAL] = {
+		.state_name = "Blond Non-Final",
+		.enter = blond_enter,
+		.next = blond_nonfinal_next,
+		.flags = TRANSFER_STATE_IS_TIMED,
+	},
 	[TRANSFER_RECALLING] = {
 		.state_name = "Recalling",
 		.enter = recalling_enter,
@@ -1018,6 +1117,7 @@
 {
 	struct attended_transfer_properties *props = hook_pvt;
 
+	ast_log(LOG_NOTICE, "Coming from hangup hook?\n");
 	stimulate_attended_transfer(props, STIMULUS_TRANSFERER_HANGUP);
 	return 0;
 }
@@ -1044,6 +1144,39 @@
 	ao2_cleanup(props);
 }
 
+static void recall_callback(struct ast_dial *dial)
+{
+	struct attended_transfer_properties *props = ast_dial_get_user_data(dial);
+
+	switch (ast_dial_state(dial)) {
+	default:
+	case AST_DIAL_RESULT_INVALID:
+	case AST_DIAL_RESULT_FAILED:
+	case AST_DIAL_RESULT_TIMEOUT:
+	case AST_DIAL_RESULT_HANGUP:
+	case AST_DIAL_RESULT_UNANSWERED:
+		/* Failure cases */
+		ast_log(LOG_NOTICE, "Coming from recall callback? dial state == %d\n", ast_dial_state(dial));
+		stimulate_attended_transfer(props, STIMULUS_TRANSFERER_HANGUP);
+		ao2_ref(props, -1);
+		break;
+	case AST_DIAL_RESULT_RINGING:
+	case AST_DIAL_RESULT_PROGRESS:
+	case AST_DIAL_RESULT_PROCEEDING:
+	case AST_DIAL_RESULT_TRYING:
+		/* Don't care about these cases */
+		break;
+	case AST_DIAL_RESULT_ANSWERED:
+		/* We struck gold! */
+		props->transferer = ast_dial_answered_steal(dial);
+		stimulate_attended_transfer(props, STIMULUS_TRANSFERER_ANSWER);
+		ao2_ref(props, -1);
+		break;
+	}
+
+
+}
+
 static int bridge_personality_atxfer_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
 {
 	const char *abort_dtmf;
@@ -1094,7 +1227,7 @@
 	struct bridge_basic_personality *personality = self->personality;
 	struct attended_transfer_properties *props = personality->pvt;
 
-	if (self->num_channels > 1) {
+	if (self->num_channels > 1 || bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
 		return;
 	}
 




More information about the asterisk-commits mailing list