]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Allow support for early media on AMI originates and call files.
authorMark Michelson <mmichelson@digium.com>
Wed, 8 Aug 2012 22:39:40 +0000 (22:39 +0000)
committerMark Michelson <mmichelson@digium.com>
Wed, 8 Aug 2012 22:39:40 +0000 (22:39 +0000)
This is based on the work done by Olle Johansson on review board.

The idea is that the channel specified in an AMI originate or call
file is typically not connected to the outgoing extension until the
channel has been answered. With this change, an EarlyMedia header can
be specified for AMI originates and an early_media option can
be specified in call files. With this option set, once early media is
received on a channel, it will be connected with the outgoing extension.

(closes issue ASTERISK-18644)
Reported by Olle Johansson

Review: https://reviewboard.asterisk.org/r/1472

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@370951 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
apps/app_originate.c
include/asterisk/channel.h
include/asterisk/pbx.h
main/channel.c
main/manager.c
main/pbx.c
pbx/pbx_spool.c
res/res_clioriginate.c

diff --git a/CHANGES b/CHANGES
index 9dbd911a99ab815b36db7f7ffb5640f87308f756..d19cfeb2822a7393d966ac3e4d64cbd8986f683f 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -425,6 +425,9 @@ Core
  * Asterisk can now use a system-provided NetBSD editline library (libedit) if it
    is available.
 
+ * Call files now support the "early_media" option to connect with an outgoing
+   extension when early media is received.
+
 AGI
 ------------------
  * A new channel variable, AGIEXITONHANGUP, has been added which allows
@@ -438,6 +441,10 @@ AGI
 
 AMI (Asterisk Manager Interface)
 ------------------
+ * The originate action now has an option "EarlyMedia" that enables the
+   call to bridge when we get early media in the call. Previously,
+   early media was disregarded always when originating calls using AMI.
+
  * Added setvar= option to manager accounts (much like sip.conf)
 
  * Originate now generates an error response if the extension given is not found
index 4756b68b6fb07b1386c1691532f52baf2b5094b8..8eb8ba329c73856f51705a83ecf57ff459e8d0fb 100644 (file)
@@ -178,7 +178,7 @@ static int originate_exec(struct ast_channel *chan, const char *data)
 
                ast_pbx_outgoing_exten(chantech, cap_slin, chandata,
                                timeout * 1000, args.arg1, exten, priority, &outgoing_status, 0, NULL,
-                               NULL, NULL, NULL, NULL);
+                               NULL, NULL, NULL, NULL, 0);
        } else if (!strcasecmp(args.type, "app")) {
                ast_debug(1, "Originating call to '%s/%s' and connecting them to %s(%s)\n",
                                chantech, chandata, args.arg1, S_OR(args.arg2, ""));
index e8785cfe364f72351ed2dfc035e532a3bbbc7c8e..c9d13187c0a6df0dc6c523eb15f4bdd6cfb1c5c3 100644 (file)
@@ -907,6 +907,7 @@ struct outgoing_helper {
        const char *context;
        const char *exten;
        int priority;
+       int connect_on_early_media;     /* If set, treat session progress as answer */
        const char *cid_num;
        const char *cid_name;
        const char *account;
index bea7e5ebf086ef21b018766a5ab86a5e56c45d06..b1e820983e0e373d9ca28c2a290e129384d6e414 100644 (file)
@@ -1010,7 +1010,7 @@ int ast_async_goto_by_name(const char *chan, const char *context, const char *ex
 
 /*! Synchronously or asynchronously make an outbound call and send it to a
    particular extension */
-int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel);
+int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, int early_media);
 
 /*! Synchronously or asynchronously make an outbound call and send it to a
    particular application with given extension */
index f7e9d631a547b54b74367735ccc105ca6bfc0e07..d5f1d31c16fd07cbbec7b0c8f6211e674b94d6a9 100644 (file)
@@ -5620,8 +5620,14 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
                                        ast_channel_hangupcause_hash_set(chan, f->data.ptr, f->datalen);
                                        break;
 
-                               /* Ignore these */
                                case AST_CONTROL_PROGRESS:
+                                       if (oh && oh->connect_on_early_media) {
+                                               *outstate = f->subclass.integer;
+                                               timeout = 0;            /* trick to force exit from the while() */
+                                               break;
+                                       }
+                                       /* Fallthrough */
+                               /* Ignore these */
                                case AST_CONTROL_PROCEEDING:
                                case AST_CONTROL_HOLD:
                                case AST_CONTROL_UNHOLD:
index 12aa851c7e07c06d1392b793abcc38b463cdd8ae..e1bf19f680a6cdfec55194b75b7bc75b6ff2b9c1 100644 (file)
@@ -465,6 +465,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <parameter name="Account">
                                <para>Account code.</para>
                        </parameter>
+                       <parameter name="EarlyMedia">
+                               <para>Set to <literal>true</literal> to force call bridge on early media..</para>
+                       </parameter>
                        <parameter name="Async">
                                <para>Set to <literal>true</literal> for fast origination.</para>
                        </parameter>
@@ -3915,6 +3918,7 @@ action_command_cleanup:
 struct fast_originate_helper {
        int timeout;
        struct ast_format_cap *cap;                             /*!< Codecs used for a call */
+       int early_media;
        AST_DECLARE_STRING_FIELDS (
                AST_STRING_FIELD(tech);
                /*! data can contain a channel name, extension number, username, password, etc. */
@@ -3966,7 +3970,7 @@ static void *fast_originate(void *data)
                        in->timeout, in->context, in->exten, in->priority, &reason, 1,
                        S_OR(in->cid_num, NULL),
                        S_OR(in->cid_name, NULL),
-                       in->vars, in->account, &chan);
+                       in->vars, in->account, &chan, in->early_media);
        }
        /* Any vars memory was passed to the ast_pbx_outgoing_xxx() calls. */
        in->vars = NULL;
@@ -4245,6 +4249,7 @@ static int action_originate(struct mansession *s, const struct message *m)
        const char *async = astman_get_header(m, "Async");
        const char *id = astman_get_header(m, "ActionID");
        const char *codecs = astman_get_header(m, "Codecs");
+       const char *early_media = astman_get_header(m, "Earlymedia");
        struct ast_variable *vars = NULL;
        char *tech, *data;
        char *l = NULL, *n = NULL;
@@ -4257,6 +4262,7 @@ static int action_originate(struct mansession *s, const struct message *m)
        struct ast_format_cap *cap = ast_format_cap_alloc_nolock();
        struct ast_format tmp_fmt;
        pthread_t th;
+       int bridge_early = 0;
 
        if (!cap) {
                astman_send_error(s, m, "Internal Error. Memory allocation failure.");
@@ -4359,6 +4365,9 @@ static int action_originate(struct mansession *s, const struct message *m)
                }
        }
 
+       /* For originate async - we can bridge in early media stage */
+       bridge_early = ast_true(early_media);
+
        if (ast_true(async)) {
                struct fast_originate_helper *fast;
 
@@ -4384,6 +4393,7 @@ static int action_originate(struct mansession *s, const struct message *m)
                        fast->cap = cap;
                        cap = NULL; /* transfered originate helper the capabilities structure.  It is now responsible for freeing it. */
                        fast->timeout = to;
+                       fast->early_media = bridge_early;
                        fast->priority = pi;
                        if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
                                destroy_fast_originate_helper(fast);
@@ -4397,7 +4407,7 @@ static int action_originate(struct mansession *s, const struct message *m)
                /* Any vars memory was passed to ast_pbx_outgoing_app(). */
        } else {
                if (exten && context && pi) {
-                       res = ast_pbx_outgoing_exten(tech, cap, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
+                       res = ast_pbx_outgoing_exten(tech, cap, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL, bridge_early);
                        /* Any vars memory was passed to ast_pbx_outgoing_exten(). */
                } else {
                        astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
index ef04d664420b63b569cfc4ead2d87095be543374..3195da3bcad626430c6301aa4c2de6312909fea3 100644 (file)
@@ -9559,6 +9559,7 @@ struct async_stat {
        int timeout;
        char app[AST_MAX_EXTENSION];
        char appdata[1024];
+       int early_media;                        /* Connect the bridge if early media arrives, don't wait for answer */
 };
 
 static void *async_wait(void *data)
@@ -9569,6 +9570,7 @@ static void *async_wait(void *data)
        int res;
        struct ast_frame *f;
        struct ast_app *app;
+       int have_early_media = 0;
 
        if (chan) {
                struct ast_callid *callid = ast_channel_callid(chan);
@@ -9593,10 +9595,18 @@ static void *async_wait(void *data)
                                ast_frfree(f);
                                break;
                        }
+                       if (as->early_media && f->subclass.integer == AST_CONTROL_PROGRESS) {
+                               have_early_media = 1;
+                               ast_frfree(f);
+                               break;
+                       }
                }
                ast_frfree(f);
        }
-       if (ast_channel_state(chan) == AST_STATE_UP) {
+       if (ast_channel_state(chan) == AST_STATE_UP || have_early_media) {
+               if (have_early_media) {
+                       ast_debug(2, "Activating pbx since we have early media \n");
+               }
                if (!ast_strlen_zero(as->app)) {
                        app = pbx_findapp(as->app);
                        if (app) {
@@ -9657,7 +9667,7 @@ static int ast_pbx_outgoing_cdr_failed(void)
        return 0;  /* success */
 }
 
-int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context, const char *exten, int priority, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel)
+int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context, const char *exten, int priority, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel, int early_media)
 {
        struct ast_channel *chan;
        struct async_stat *as;
@@ -9666,6 +9676,8 @@ int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const c
        int res = -1, cdr_res = -1;
        struct outgoing_helper oh;
 
+       oh.connect_on_early_media = early_media;
+
        callid_created = ast_callid_threadstorage_auto(&callid);
 
        if (synchronous) {
@@ -9695,9 +9707,9 @@ int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const c
                                }
                        }
 
-                       if (ast_channel_state(chan) == AST_STATE_UP) {
+                       if (ast_channel_state(chan) == AST_STATE_UP || (early_media && *reason == AST_CONTROL_PROGRESS)) {
                                        res = 0;
-                               ast_verb(4, "Channel %s was answered.\n", ast_channel_name(chan));
+                               ast_verb(4, "Channel %s %s\n", ast_channel_name(chan), ast_channel_state(chan) == AST_STATE_UP ? "was answered" : "got early media");
 
                                if (synchronous > 1) {
                                        if (channel)
index 43bac620e38b50ccd0bb28c2205e74f921e7b345..d061f356b759662546f3c9438d8aa988767674dc 100644 (file)
@@ -67,6 +67,8 @@ enum {
        SPOOL_FLAG_ALWAYS_DELETE = (1 << 0),
        /* Don't unlink the call file after processing, move in qdonedir */
        SPOOL_FLAG_ARCHIVE = (1 << 1),
+       /* Connect the channel with the outgoing extension once early media is received */
+       SPOOL_FLAG_EARLY_MEDIA = (1 << 2),
 };
 
 static char qdir[255];
@@ -253,6 +255,8 @@ static int apply_outgoing(struct outgoing *o, const char *fn, FILE *f)
                                        ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ALWAYS_DELETE);
                                } else if (!strcasecmp(buf, "archive")) {
                                        ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE);
+                               } else if (!strcasecmp(buf, "early_media")) {
+                                       ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_EARLY_MEDIA);
                                } else {
                                        ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", buf, lineno, fn);
                                }
@@ -357,7 +361,8 @@ static void *attempt_thread(void *data)
                ast_verb(3, "Attempting call on %s/%s for %s@%s:%d (Retry %d)\n", o->tech, o->dest, o->exten, o->context,o->priority, o->retries);
                res = ast_pbx_outgoing_exten(o->tech, o->capabilities, o->dest,
                        o->waittime * 1000, o->context, o->exten, o->priority, &reason,
-                       2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL);
+                       2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL,
+                       ast_test_flag(&o->options, SPOOL_FLAG_EARLY_MEDIA));
                o->vars = NULL;
        }
        if (res) {
index 7ac5605b9b0a9b9f7bb294c66a16f183e20e045c..42030d798558575199ac26e66bfb03bf6030179c 100644 (file)
@@ -119,7 +119,7 @@ static char *orig_exten(int fd, const char *chan, const char *data)
                return CLI_FAILURE;
        }
        ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
-       ast_pbx_outgoing_exten(chantech, cap, chandata, TIMEOUT * 1000, context, exten, 1, &reason, 0, NULL, NULL, NULL, NULL, NULL);
+       ast_pbx_outgoing_exten(chantech, cap, chandata, TIMEOUT * 1000, context, exten, 1, &reason, 0, NULL, NULL, NULL, NULL, NULL, 0);
        cap = ast_format_cap_destroy(cap);
 
        return CLI_SUCCESS;