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

Reply via email to