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; } -- _____________________________________________________________________ -- Bandwidth and Colocation Provided by http://www.api-digital.com -- svn-commits mailing list To UNSUBSCRIBE or update options visit: http://lists.digium.com/mailman/listinfo/svn-commits