[asterisk-commits] mjordan: branch mjordan/cdrs-of-doom r386309 - in /team/mjordan/cdrs-of-doom:...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Apr 22 13:16:36 CDT 2013
Author: mjordan
Date: Mon Apr 22 13:16:33 2013
New Revision: 386309
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=386309
Log:
Merge in bridge_construction
Modified:
team/mjordan/cdrs-of-doom/ (props changed)
team/mjordan/cdrs-of-doom/bridges/bridge_builtin_features.c
team/mjordan/cdrs-of-doom/channels/chan_local.c
team/mjordan/cdrs-of-doom/include/asterisk/bridging.h
team/mjordan/cdrs-of-doom/include/asterisk/srv.h
team/mjordan/cdrs-of-doom/main/bridging.c
team/mjordan/cdrs-of-doom/main/channel.c
Propchange: team/mjordan/cdrs-of-doom/
------------------------------------------------------------------------------
Binary property 'branch-11-merged' - no diff available.
Propchange: team/mjordan/cdrs-of-doom/
------------------------------------------------------------------------------
--- cdrs-of-doom-integrated (original)
+++ cdrs-of-doom-integrated Mon Apr 22 13:16:33 2013
@@ -1,1 +1,1 @@
-/team/group/bridge_construction:1-386287
+/team/group/bridge_construction:1-386308
Modified: team/mjordan/cdrs-of-doom/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/bridges/bridge_builtin_features.c?view=diff&rev=386309&r1=386308&r2=386309
==============================================================================
--- team/mjordan/cdrs-of-doom/bridges/bridge_builtin_features.c (original)
+++ team/mjordan/cdrs-of-doom/bridges/bridge_builtin_features.c Mon Apr 22 13:16:33 2013
@@ -47,8 +47,14 @@
#include "asterisk/file.h"
#include "asterisk/app.h"
#include "asterisk/astobj2.h"
-
-/*! \brief Helper function that presents dialtone and grabs extension */
+#include "asterisk/pbx.h"
+
+/*!
+ * \brief Helper function that presents dialtone and grabs extension
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len, const char *context)
{
int res;
@@ -56,15 +62,35 @@
/* Play the simple "transfer" prompt out and wait */
res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
ast_stopstream(chan);
-
- /* If the person hit a DTMF digit while the above played back stick it into the buffer */
+ if (res < 0) {
+ /* Hangup or error */
+ return -1;
+ }
if (res) {
- exten[0] = (char)res;
+ /* Store the DTMF digit that interrupted playback of the file. */
+ exten[0] = res;
}
/* Drop to dialtone so they can enter the extension they want to transfer to */
- res = ast_app_dtget(chan, context, exten, exten_len, 100, 1000);
-
+/* BUGBUG the timeout needs to be configurable from features.conf. */
+ res = ast_app_dtget(chan, context, exten, exten_len, exten_len - 1, 3000);
+ if (res < 0) {
+ /* Hangup or error */
+ res = -1;
+ } else if (!res) {
+ /* 0 for invalid extension dialed. */
+ if (ast_strlen_zero(exten)) {
+ ast_debug(1, "%s dialed no digits.\n", ast_channel_name(chan));
+ } else {
+ ast_debug(1, "%s dialed '%s@%s' does not exist.\n",
+ ast_channel_name(chan), exten, context);
+ }
+ ast_stream_and_wait(chan, "pbx-invalid", AST_DIGIT_NONE);
+ res = -1;
+ } else {
+ /* Dialed extension is valid. */
+ res = 0;
+ }
return res;
}
@@ -76,10 +102,17 @@
int cause;
/* Fill the variable with the extension and context we want to call */
+/* BUGBUG if local channel optimization is using masquerades then this needs /n so the destination keeps its DTMF features.
+ * Or use /n to keep the peer channel stable until after the atxfer completes and remove the /n from the channel.
+ *
+ * Local channel optimization currently is disabled because I don't set the chan->bridge pointers.
+ */
snprintf(destination, sizeof(destination), "%s@%s", exten, context);
/* Now we request that chan_local prepare to call the destination */
- if (!(chan = ast_request("Local", ast_channel_nativeformats(caller), caller, destination, &cause))) {
+ chan = ast_request("Local", ast_channel_nativeformats(caller), caller, destination,
+ &cause);
+ if (!chan) {
return NULL;
}
@@ -100,67 +133,113 @@
return chan;
}
+/*!
+ * \internal
+ * \brief Determine the transfer context to use.
+ * \since 12.0.0
+ *
+ * \param transferer Channel initiating the transfer.
+ * \param context User supplied context if available. May be NULL.
+ *
+ * \return The context to use for the transfer.
+ */
+static const char *get_transfer_context(struct ast_channel *transferer, const char *context)
+{
+ if (!ast_strlen_zero(context)) {
+ return context;
+ }
+ context = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
+ if (!ast_strlen_zero(context)) {
+ return context;
+ }
+ context = ast_channel_macrocontext(transferer);
+ if (!ast_strlen_zero(context)) {
+ return context;
+ }
+ context = ast_channel_context(transferer);
+ if (!ast_strlen_zero(context)) {
+ return context;
+ }
+ return "default";
+}
+
/*! \brief Internal built in feature for blind transfers */
static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
char exten[AST_MAX_EXTENSION] = "";
struct ast_channel *chan = NULL;
struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt;
- const char *context = (blind_transfer && !ast_strlen_zero(blind_transfer->context) ? blind_transfer->context : ast_channel_context(bridge_channel->chan));
+ const char *context;
+
+/* BUGBUG the peer needs to be put on hold for the transfer. */
+ ast_channel_lock(bridge_channel->chan);
+ context = ast_strdupa(get_transfer_context(bridge_channel->chan,
+ blind_transfer ? blind_transfer->context : NULL));
+ ast_channel_unlock(bridge_channel->chan);
/* Grab the extension to transfer to */
- if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
- ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
- return 0;
- }
+ if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
+ return 0;
+ }
+
+/* BUGBUG just need to ast_async_goto the peer so this bridge will go away and not accumulate local channels and bridges if the destination is to an application. */
+/* ast_async_goto actually is a blind transfer. */
+/* BUGBUG Use the bridge count to determine if can do DTMF transfer features. If count is not 2 then don't allow it. */
/* Get a channel that is the destination we wish to call */
- if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
- ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
- return 0;
- }
-
- /* This is sort of the fun part. We impart the above channel onto the bridge, and have it take our place. */
- ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL, 1);
-
+ chan = dial_transfer(bridge_channel->chan, exten, context);
+ if (!chan) {
+ return 0;
+ }
+
+ /* Impart the new channel onto the bridge, and have it take our place. */
+ if (ast_bridge_impart(bridge_channel->bridge, chan, bridge_channel->chan, NULL, 1)) {
+ ast_hangup(chan);
+ return 0;
+ }
+
+ return 0;
+}
+
+/*! Attended transfer code */
+enum atxfer_code {
+ /*! Party C hungup or other reason to abandon the transfer. */
+ ATXFER_INCOMPLETE,
+ /*! Transfer party C to party A. */
+ ATXFER_COMPLETE,
+ /*! Turn the transfer into a threeway call. */
+ ATXFER_THREEWAY,
+ /*! Hangup party C and return party B to the bridge. */
+ ATXFER_ABORT,
+};
+
+/*! \brief Attended transfer feature to complete transfer */
+static int attended_transfer_complete(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ enum atxfer_code *transfer_code = hook_pvt;
+
+ *transfer_code = ATXFER_COMPLETE;
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
return 0;
}
/*! \brief Attended transfer feature to turn it into a threeway call */
-static int attended_threeway_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
- /*
- * This is sort of abusing the depart state but in this instance
- * it is only going to be handled by feature_attended_transfer()
- * so it is okay.
- */
- ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
- return 0;
-}
-
-/*! \brief Attended transfer abort feature */
-static int attended_abort_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
- struct ast_bridge_channel *called_bridge_channel = NULL;
-
- /* It is possible (albeit unlikely) that the bridge channels list may change, so we have to ensure we do all of our magic while locked */
- ao2_lock(bridge);
-
- if (AST_LIST_FIRST(&bridge->channels) != bridge_channel) {
- called_bridge_channel = AST_LIST_FIRST(&bridge->channels);
- } else {
- called_bridge_channel = AST_LIST_LAST(&bridge->channels);
- }
-
- /* Now we basically eject the other channel from the bridge. This will cause their thread to hang them up, and our own code to consider the transfer failed. */
- if (called_bridge_channel) {
- ast_bridge_change_state(called_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
- }
-
- ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
-
- ao2_unlock(bridge);
-
+static int attended_transfer_threeway(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ enum atxfer_code *transfer_code = hook_pvt;
+
+ *transfer_code = ATXFER_THREEWAY;
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+ return 0;
+}
+
+/*! \brief Attended transfer feature to abort transfer */
+static int attended_transfer_abort(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ enum atxfer_code *transfer_code = hook_pvt;
+
+ *transfer_code = ATXFER_ABORT;
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
return 0;
}
@@ -168,70 +247,156 @@
static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
char exten[AST_MAX_EXTENSION] = "";
- struct ast_channel *chan = NULL;
- struct ast_bridge *attended_bridge = NULL;
- struct ast_bridge_features caller_features, called_features;
- enum ast_bridge_channel_state attended_bridge_result;
+ struct ast_channel *peer;
+ struct ast_bridge *attended_bridge;
+ struct ast_bridge_features caller_features;
+ int xfer_failed;
struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt;
- const char *context = (attended_transfer && !ast_strlen_zero(attended_transfer->context) ? attended_transfer->context : ast_channel_context(bridge_channel->chan));
+ const char *context;
+ enum atxfer_code transfer_code = ATXFER_INCOMPLETE;
+
+ bridge = ast_bridge_channel_merge_inhibit(bridge_channel, +1);
+
+/* BUGBUG the peer needs to be put on hold for the transfer. */
+ ast_channel_lock(bridge_channel->chan);
+ context = ast_strdupa(get_transfer_context(bridge_channel->chan,
+ attended_transfer ? attended_transfer->context : NULL));
+ ast_channel_unlock(bridge_channel->chan);
/* Grab the extension to transfer to */
- if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
- ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
+ if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
return 0;
}
/* Get a channel that is the destination we wish to call */
- if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
- ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
+ peer = dial_transfer(bridge_channel->chan, exten, context);
+ if (!peer) {
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+/* BUGBUG beeperr needs to be configurable from features.conf */
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
+ return 0;
+ }
+
+/* BUGBUG bridging API features does not support features.conf featuremap */
+/* BUGBUG bridging API features does not support the features.conf atxfer bounce between C & B channels */
+ /* Setup a DTMF menu to control the transfer. */
+ if (ast_bridge_features_init(&caller_features)
+ || ast_bridge_hangup_hook(&caller_features,
+ attended_transfer_complete, &transfer_code, NULL, 0)
+ || ast_bridge_dtmf_hook(&caller_features,
+ attended_transfer && !ast_strlen_zero(attended_transfer->abort)
+ ? attended_transfer->abort : "*1",
+ attended_transfer_abort, &transfer_code, NULL, 0)
+ || ast_bridge_dtmf_hook(&caller_features,
+ attended_transfer && !ast_strlen_zero(attended_transfer->complete)
+ ? attended_transfer->complete : "*2",
+ attended_transfer_complete, &transfer_code, NULL, 0)
+ || ast_bridge_dtmf_hook(&caller_features,
+ attended_transfer && !ast_strlen_zero(attended_transfer->threeway)
+ ? attended_transfer->threeway : "*3",
+ attended_transfer_threeway, &transfer_code, NULL, 0)) {
+ ast_bridge_features_cleanup(&caller_features);
+ ast_hangup(peer);
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+/* BUGBUG beeperr needs to be configurable from features.conf */
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
return 0;
}
/* Create a bridge to use to talk to the person we are calling */
- if (!(attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) {
- ast_hangup(chan);
- ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
- return 0;
- }
-
- /* Setup our called features structure so that if they hang up we immediately get thrown out of the bridge */
- ast_bridge_features_init(&called_features);
- ast_bridge_features_set_flag(&called_features, AST_BRIDGE_FLAG_DISSOLVE);
+ attended_bridge = ast_bridge_base_new(
+ AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX,
+ AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
+ if (!attended_bridge) {
+ ast_bridge_features_cleanup(&caller_features);
+ ast_hangup(peer);
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+/* BUGBUG beeperr needs to be configurable from features.conf */
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
+ return 0;
+ }
+ ast_bridge_merge_inhibit(attended_bridge, +1);
/* This is how this is going down, we are imparting the channel we called above into this bridge first */
- ast_bridge_impart(attended_bridge, chan, NULL, &called_features, 1);
-
- /* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */
- ast_bridge_features_init(&caller_features);
- ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP,
- (attended_transfer && !ast_strlen_zero(attended_transfer->complete) ? attended_transfer->complete : "*1"), NULL);
- ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->threeway) ? attended_transfer->threeway : "*2"),
- attended_threeway_transfer, NULL, NULL);
- ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->abort) ? attended_transfer->abort : "*3"),
- attended_abort_transfer, NULL, NULL);
-
- /* But for the caller we want to join the bridge in a blocking fashion so we don't spin around in this function doing nothing while waiting */
- attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features, NULL);
-
- /* Since the above returned the caller features structure is of no more use */
+/* BUGBUG we should impart the peer as an independent and move it to the original bridge. */
+ if (ast_bridge_impart(attended_bridge, peer, NULL, NULL, 0)) {
+ ast_bridge_destroy(attended_bridge);
+ ast_bridge_features_cleanup(&caller_features);
+ ast_hangup(peer);
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+/* BUGBUG beeperr needs to be configurable from features.conf */
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
+ return 0;
+ }
+
+ /*
+ * For the caller we want to join the bridge in a blocking
+ * fashion so we don't spin around in this function doing
+ * nothing while waiting.
+ */
+ ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features, NULL, 0);
+
+/*
+ * BUGBUG there is a small window where the channel does not point to the bridge_channel.
+ *
+ * This window is expected to go away when atxfer is redesigned
+ * to fully support existing functionality. There will be one
+ * and only one ast_bridge_channel structure per channel.
+ */
+ /* Point the channel back to the original bridge_channel. */
+ ast_channel_lock(bridge_channel->chan);
+ ast_channel_internal_bridge_channel_set(bridge_channel->chan, bridge_channel);
+ ast_channel_unlock(bridge_channel->chan);
+
+ /* Wait for peer thread to exit bridge and die. */
+ if (!ast_autoservice_start(bridge_channel->chan)) {
+ ast_bridge_depart(peer);
+ ast_autoservice_stop(bridge_channel->chan);
+ } else {
+ ast_bridge_depart(peer);
+ }
+
+ /* Now that all channels are out of it we can destroy the bridge and the feature structures */
+ ast_bridge_destroy(attended_bridge);
ast_bridge_features_cleanup(&caller_features);
- /* Drop the channel we are transferring to out of the above bridge since it has ended */
- if ((attended_bridge_result != AST_BRIDGE_CHANNEL_STATE_HANGUP) && !ast_bridge_depart(attended_bridge, chan)) {
- /* If the user wants to turn this into a threeway transfer then do so, otherwise they take our place */
- if (attended_bridge_result == AST_BRIDGE_CHANNEL_STATE_DEPART) {
- /* We want to impart them upon the bridge and just have us return to it as normal */
- ast_bridge_impart(bridge, chan, NULL, NULL, 1);
- } else {
- ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL, 1);
+ xfer_failed = -1;
+ switch (transfer_code) {
+ case ATXFER_INCOMPLETE:
+ /* Peer hungup */
+ break;
+ case ATXFER_COMPLETE:
+ /* The peer takes our place in the bridge. */
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+ xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, bridge_channel->chan, NULL, 1);
+ break;
+ case ATXFER_THREEWAY:
+ /*
+ * Transferer wants to convert to a threeway call.
+ *
+ * Just impart the peer onto the bridge and have us return to it
+ * as normal.
+ */
+ xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, NULL, NULL, 1);
+ break;
+ case ATXFER_ABORT:
+ /* Transferer decided not to transfer the call after all. */
+ break;
+ }
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+ if (xfer_failed) {
+ ast_hangup(peer);
+ if (!ast_check_hangup_locked(bridge_channel->chan)) {
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
}
- } else {
- ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
- }
-
- /* Now that all channels are out of it we can destroy the bridge and the called features structure */
- ast_bridge_features_cleanup(&called_features);
- ast_bridge_destroy(attended_bridge);
+ }
return 0;
}
Modified: team/mjordan/cdrs-of-doom/channels/chan_local.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/channels/chan_local.c?view=diff&rev=386309&r1=386308&r2=386309
==============================================================================
--- team/mjordan/cdrs-of-doom/channels/chan_local.c (original)
+++ team/mjordan/cdrs-of-doom/channels/chan_local.c Mon Apr 22 13:16:33 2013
@@ -54,6 +54,7 @@
#include "asterisk/stringfields.h"
#include "asterisk/devicestate.h"
#include "asterisk/astobj2.h"
+#include "asterisk/bridging.h"
/*** DOCUMENTATION
<manager name="LocalOptimizeAway" language="en_US">
@@ -108,7 +109,6 @@
static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
static int local_sendtext(struct ast_channel *ast, const char *text);
static int local_devicestate(const char *data);
-static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen);
static int local_setoption(struct ast_channel *chan, int option, void *data, int datalen);
@@ -131,7 +131,6 @@
.send_html = local_sendhtml,
.send_text = local_sendtext,
.devicestate = local_devicestate,
- .bridged_channel = local_bridgedchannel,
.queryoption = local_queryoption,
.setoption = local_setoption,
};
@@ -153,11 +152,9 @@
struct ast_channel *chan; /*!< Outbound channel - PBX is run here */
};
-#define LOCAL_ALREADY_MASQED (1 << 0) /*!< Already masqueraded */
-#define LOCAL_LAUNCHED_PBX (1 << 1) /*!< PBX was launched */
-#define LOCAL_NO_OPTIMIZATION (1 << 2) /*!< Do not optimize using masquerading */
-#define LOCAL_BRIDGE (1 << 3) /*!< Report back the "true" channel as being bridged to */
-#define LOCAL_MOH_PASSTHRU (1 << 4) /*!< Pass through music on hold start/stop frames */
+#define LOCAL_LAUNCHED_PBX (1 << 0) /*!< PBX was launched */
+#define LOCAL_NO_OPTIMIZATION (1 << 1) /*!< Do not optimize using masquerading */
+#define LOCAL_MOH_PASSTHRU (1 << 2) /*!< Pass through music on hold start/stop frames */
/*!
* \brief Send a pvt in with no locks held and get all locks
@@ -329,37 +326,6 @@
return res;
}
-/*! \brief Return the bridged channel of a Local channel */
-static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
-{
- struct local_pvt *p = ast_channel_tech_pvt(bridge);
- struct ast_channel *bridged = bridge;
-
- if (!p) {
- ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning <none>\n",
- ast_channel_name(chan), ast_channel_name(bridge));
- return NULL;
- }
-
- ao2_lock(p);
-
- if (ast_test_flag(p, LOCAL_BRIDGE)) {
- /* Find the opposite channel */
- bridged = (bridge == p->owner ? p->chan : p->owner);
-
- /* Now see if the opposite channel is bridged to anything */
- if (!bridged) {
- bridged = bridge;
- } else if (ast_channel_internal_bridged_channel(bridged)) {
- bridged = ast_channel_internal_bridged_channel(bridged);
- }
- }
-
- ao2_unlock(p);
-
- return bridged;
-}
-
/* Called with ast locked */
static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen)
{
@@ -483,146 +449,32 @@
/*!
* \internal
- * \note This function assumes that we're only called from the "outbound" local channel side
+ * \brief Check and optimize out the local channels between bridges.
+ * \since 12.0.0
*
- * \note it is assummed p is locked and reffed before entering this function
+ * \param ast Channel writing a frame into the local channels.
+ * \param p Local channel private.
+ *
+ * \note It is assumed that ast is locked.
+ * \note It is assumed that p is locked.
+ *
+ * \retval 0 if local channels were not optimized out.
+ * \retval non-zero if local channels were optimized out.
*/
-static void check_bridge(struct ast_channel *ast, struct local_pvt *p)
-{
- struct ast_channel *owner;
- struct ast_channel *chan;
- struct ast_channel *bridged_chan;
- struct ast_frame *f;
-
+static int got_optimized_out(struct ast_channel *ast, struct local_pvt *p)
+{
/* Do a few conditional checks early on just to see if this optimization is possible */
- if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION | LOCAL_ALREADY_MASQED)
- || !p->chan || !p->owner) {
- return;
- }
-
- /* Safely get the channel bridged to p->chan */
- chan = ast_channel_ref(p->chan);
-
- ao2_unlock(p); /* don't call bridged channel with the pvt locked */
- bridged_chan = ast_bridged_channel(chan);
- ao2_lock(p);
-
- chan = ast_channel_unref(chan);
-
- /* since we had to unlock p to get the bridged chan, validate our
- * data once again and verify the bridged channel is what we expect
- * it to be in order to perform this optimization */
- if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION | LOCAL_ALREADY_MASQED)
- || !p->chan || !p->owner
- || (ast_channel_internal_bridged_channel(p->chan) != bridged_chan)) {
- return;
- }
-
- /* only do the masquerade if we are being called on the outbound channel,
- if it has been bridged to another channel and if there are no pending
- frames on the owner channel (because they would be transferred to the
- outbound channel during the masquerade)
- */
- if (!ast_channel_internal_bridged_channel(p->chan) /* Not ast_bridged_channel! Only go one step! */
- || !AST_LIST_EMPTY(ast_channel_readq(p->owner))
- || ast != p->chan /* Sanity check (should always be false) */) {
- return;
- }
-
- /* Masquerade bridged channel into owner */
- /* Lock everything we need, one by one, and give up if
- we can't get everything. Remember, we'll get another
- chance in just a little bit */
- if (ast_channel_trylock(ast_channel_internal_bridged_channel(p->chan))) {
- return;
- }
- if (ast_check_hangup(ast_channel_internal_bridged_channel(p->chan))
- || ast_channel_trylock(p->owner)) {
- ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
- return;
- }
-
- /*
- * At this point we have 4 locks:
- * p, p->chan (same as ast), p->chan->_bridge, p->owner
- *
- * Flush a voice or video frame on the outbound channel to make
- * the queue empty faster so we can get optimized out.
- */
- f = AST_LIST_FIRST(ast_channel_readq(p->chan));
- if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) {
- AST_LIST_REMOVE_HEAD(ast_channel_readq(p->chan), frame_list);
- ast_frfree(f);
- f = AST_LIST_FIRST(ast_channel_readq(p->chan));
- }
-
- if (f
- || ast_check_hangup(p->owner)
- || ast_channel_masquerade(p->owner, ast_channel_internal_bridged_channel(p->chan))) {
- ast_channel_unlock(p->owner);
- ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
- return;
- }
-
- /* Masquerade got setup. */
- ast_debug(4, "Masquerading %s <- %s\n",
- ast_channel_name(p->owner),
- ast_channel_name(ast_channel_internal_bridged_channel(p->chan)));
- if (ast_channel_monitor(p->owner)
- && !ast_channel_monitor(ast_channel_internal_bridged_channel(p->chan))) {
- struct ast_channel_monitor *tmp;
-
- /* If a local channel is being monitored, we don't want a masquerade
- * to cause the monitor to go away. Since the masquerade swaps the monitors,
- * pre-swapping the monitors before the masquerade will ensure that the monitor
- * ends up where it is expected.
- */
- tmp = ast_channel_monitor(p->owner);
- ast_channel_monitor_set(p->owner, ast_channel_monitor(ast_channel_internal_bridged_channel(p->chan)));
- ast_channel_monitor_set(ast_channel_internal_bridged_channel(p->chan), tmp);
- }
- if (ast_channel_audiohooks(p->chan)) {
- struct ast_audiohook_list *audiohooks_swapper;
-
- audiohooks_swapper = ast_channel_audiohooks(p->chan);
- ast_channel_audiohooks_set(p->chan, ast_channel_audiohooks(p->owner));
- ast_channel_audiohooks_set(p->owner, audiohooks_swapper);
- }
-
- /* If any Caller ID was set, preserve it after masquerade like above. We must check
- * to see if Caller ID was set because otherwise we'll mistakingly copy info not
- * set from the dialplan and will overwrite the real channel Caller ID. The reason
- * for this whole preswapping action is because the Caller ID is set on the channel
- * thread (which is the to be masqueraded away local channel) before both local
- * channels are optimized away.
- */
- if (ast_channel_caller(p->owner)->id.name.valid || ast_channel_caller(p->owner)->id.number.valid
- || ast_channel_caller(p->owner)->id.subaddress.valid || ast_channel_caller(p->owner)->ani.name.valid
- || ast_channel_caller(p->owner)->ani.number.valid || ast_channel_caller(p->owner)->ani.subaddress.valid) {
- SWAP(*ast_channel_caller(p->owner), *ast_channel_caller(ast_channel_internal_bridged_channel(p->chan)));
- }
- if (ast_channel_redirecting(p->owner)->from.name.valid || ast_channel_redirecting(p->owner)->from.number.valid
- || ast_channel_redirecting(p->owner)->from.subaddress.valid || ast_channel_redirecting(p->owner)->to.name.valid
- || ast_channel_redirecting(p->owner)->to.number.valid || ast_channel_redirecting(p->owner)->to.subaddress.valid) {
- SWAP(*ast_channel_redirecting(p->owner), *ast_channel_redirecting(ast_channel_internal_bridged_channel(p->chan)));
- }
- if (ast_channel_dialed(p->owner)->number.str || ast_channel_dialed(p->owner)->subaddress.valid) {
- SWAP(*ast_channel_dialed(p->owner), *ast_channel_dialed(ast_channel_internal_bridged_channel(p->chan)));
- }
- ast_app_group_update(p->chan, p->owner);
- ast_set_flag(p, LOCAL_ALREADY_MASQED);
-
- ast_channel_unlock(p->owner);
- ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
-
- /* Do the masquerade now. */
- owner = ast_channel_ref(p->owner);
- ao2_unlock(p);
- ast_channel_unlock(ast);
- ast_do_masquerade(owner);
- ast_channel_unref(owner);
- ast_channel_lock(ast);
- ao2_lock(p);
+ if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION) || !p->chan || !p->owner) {
+ return 0;
+ }
+ if (ast == p->owner) {
+ return ast_bridge_local_optimized_out(p->owner, p->chan);
+ }
+ if (ast == p->chan) {
+ return ast_bridge_local_optimized_out(p->chan, p->owner);
+ }
+ /* ast is not valid to optimize. */
+ return 0;
}
static struct ast_frame *local_read(struct ast_channel *ast)
@@ -634,28 +486,24 @@
{
struct local_pvt *p = ast_channel_tech_pvt(ast);
int res = -1;
- int isoutbound;
if (!p) {
return -1;
}
/* Just queue for delivery to the other side */
- ao2_ref(p, 1); /* ref for local_queue_frame */
+ ao2_ref(p, 1);
ao2_lock(p);
- isoutbound = IS_OUTBOUND(ast, p);
-
- if (isoutbound
- && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) {
- check_bridge(ast, p);
- }
-
- if (!ast_test_flag(p, LOCAL_ALREADY_MASQED)) {
- res = local_queue_frame(p, isoutbound, f, ast, 1);
- } else {
- ast_debug(1, "Not posting to '%s' queue since already masqueraded out\n",
- ast_channel_name(ast));
- res = 0;
+ switch (f->frametype) {
+ case AST_FRAME_VOICE:
+ case AST_FRAME_VIDEO:
+ if (got_optimized_out(ast, p)) {
+ break;
+ }
+ /* fall through */
+ default:
+ res = local_queue_frame(p, IS_OUTBOUND(ast, p), f, ast, 1);
+ break;
}
ao2_unlock(p);
ao2_ref(p, -1);
@@ -928,6 +776,7 @@
ast_channel_language_set(chan, ast_channel_language(owner));
ast_channel_accountcode_set(chan, ast_channel_accountcode(owner));
ast_channel_musicclass_set(chan, ast_channel_musicclass(owner));
+ ast_cdr_update(chan);
ast_channel_cc_params_init(chan, ast_channel_get_cc_config_params(owner));
@@ -1191,9 +1040,6 @@
"to use the 'j' option to enable the jitterbuffer\n");
}
}
- if (strchr(opts, 'b')) {
- ast_set_flag(tmp, LOCAL_BRIDGE);
- }
if (strchr(opts, 'm')) {
ast_set_flag(tmp, LOCAL_MOH_PASSTHRU);
}
Modified: team/mjordan/cdrs-of-doom/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/include/asterisk/bridging.h?view=diff&rev=386309&r1=386308&r2=386309
==============================================================================
--- team/mjordan/cdrs-of-doom/include/asterisk/bridging.h (original)
+++ team/mjordan/cdrs-of-doom/include/asterisk/bridging.h Mon Apr 22 13:16:33 2013
@@ -1,8 +1,9 @@
/*
* Asterisk -- An open source telephony toolkit.
*
- * Copyright (C) 2007 - 2009, Digium, Inc.
- *
+ * Copyright (C) 2007 - 2009, 2013 Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett at digium.com>
* Joshua Colp <jcolp at digium.com>
*
* See http://www.asterisk.org for more information about
@@ -16,10 +17,16 @@
* at the top of the source tree.
*/
-/*! \file
+/*!
+ * \file
* \brief Channel Bridging API
+ *
+ * \author Richard Mudgett <rmudgett at digium.com>
* \author Joshua Colp <jcolp at digium.com>
* \ref AstBridging
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
*/
/*!
@@ -63,54 +70,41 @@
#endif
#include "asterisk/bridging_features.h"
+#include "asterisk/bridging_roles.h"
#include "asterisk/dsp.h"
+#include "asterisk/uuid.h"
/*! \brief Capabilities for a bridge technology */
enum ast_bridge_capability {
+ /*! Bridge technology can service calls on hold */
+ AST_BRIDGE_CAPABILITY_HOLDING = (1 << 0),
+ /*! Bridge waits for channel to answer. Passes early media. */
+ AST_BRIDGE_CAPABILITY_EARLY = (1 << 1),
+ /*! Bridge should natively bridge two channels if possible */
+ AST_BRIDGE_CAPABILITY_NATIVE = (1 << 2),
/*! Bridge is only capable of mixing 2 channels */
- AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 1),
+ AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 3),
/*! Bridge is capable of mixing 2 or more channels */
- AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 2),
- /*! Bridge should natively bridge two channels if possible */
- AST_BRIDGE_CAPABILITY_NATIVE = (1 << 3),
- /*! Bridge should run using the multithreaded model */
- AST_BRIDGE_CAPABILITY_MULTITHREADED = (1 << 4),
- /*! Bridge should run a central bridge thread */
- AST_BRIDGE_CAPABILITY_THREAD = (1 << 5),
- /*! Bridge technology can do video mixing (or something along those lines) */
- AST_BRIDGE_CAPABILITY_VIDEO = (1 << 6),
- /*! Bridge technology can optimize things based on who is talking */
- AST_BRIDGE_CAPABILITY_OPTIMIZE = (1 << 7),
+ AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 4),
};
/*! \brief State information about a bridged channel */
enum ast_bridge_channel_state {
/*! Waiting for a signal (Channel in the bridge) */
AST_BRIDGE_CHANNEL_STATE_WAIT = 0,
- /*! Bridged channel has ended itself (it has hung up) */
+ /*! Bridged channel was forced out and should be hung up (Bridge may dissolve.) */
AST_BRIDGE_CHANNEL_STATE_END,
/*! Bridged channel was forced out and should be hung up */
AST_BRIDGE_CHANNEL_STATE_HANGUP,
- /*! Bridged channel was ast_bridge_depart() from the bridge without being hung up */
- AST_BRIDGE_CHANNEL_STATE_DEPART,
- /*! Bridged channel is executing a feature hook */
- AST_BRIDGE_CHANNEL_STATE_FEATURE,
- /*! Bridged channel is sending a DTMF stream out */
- AST_BRIDGE_CHANNEL_STATE_DTMF,
- /*! Bridged channel began talking */
- AST_BRIDGE_CHANNEL_STATE_START_TALKING,
- /*! Bridged channel has stopped talking */
- AST_BRIDGE_CHANNEL_STATE_STOP_TALKING,
};
-/*! \brief Return values for bridge technology write function */
-enum ast_bridge_write_result {
- /*! Bridge technology wrote out frame fine */
- AST_BRIDGE_WRITE_SUCCESS = 0,
- /*! Bridge technology attempted to write out the frame but failed */
- AST_BRIDGE_WRITE_FAILED,
- /*! Bridge technology does not support writing out a frame of this type */
- AST_BRIDGE_WRITE_UNSUPPORTED,
+enum ast_bridge_channel_thread_state {
+ /*! Bridge channel thread is idle/waiting. */
+ AST_BRIDGE_CHANNEL_THREAD_IDLE,
+ /*! Bridge channel thread is writing a normal/simple frame. */
+ AST_BRIDGE_CHANNEL_THREAD_SIMPLE,
+ /*! Bridge channel thread is processing a frame. */
+ AST_BRIDGE_CHANNEL_THREAD_FRAME,
};
struct ast_bridge_technology;
@@ -136,6 +130,7 @@
* \brief Structure that contains information regarding a channel in a bridge
*/
struct ast_bridge_channel {
+/* BUGBUG cond is only here because of external party suspend/unsuspend support. */
/*! Condition, used if we want to wake up a thread waiting on the bridged channel */
ast_cond_t cond;
/*! Current bridged channel state */
@@ -144,29 +139,99 @@
struct ast_channel *chan;
/*! Asterisk channel we are swapping with (if swapping) */
struct ast_channel *swap;
- /*! Bridge this channel is participating in */
+ /*!
+ * \brief Bridge this channel is participating in
+ *
+ * \note The bridge pointer cannot change while the bridge or
+ * bridge_channel is locked.
+ */
struct ast_bridge *bridge;
- /*! Private information unique to the bridge technology */
+ /*!
+ * \brief Bridge class private channel data.
+ *
+ * \note This information is added when the channel is pushed
+ * into the bridge and removed when it is pulled from the
+ * bridge.
+ */
void *bridge_pvt;
- /*! Thread handling the bridged channel */
+ /*!
+ * \brief Private information unique to the bridge technology.
+ *
+ * \note This information is added when the channel joins the
+ * bridge's technology and removed when it leaves the bridge's
+ * technology.
+ */
+ void *tech_pvt;
+ /*! Thread handling the bridged channel (Needed by ast_bridge_depart) */
pthread_t thread;
- /*! Additional file descriptors to look at */
- int fds[4];
- /*! Bit to indicate whether the channel is suspended from the bridge or not */
+ /* v-- These flags change while the bridge is locked or before the channel is in the bridge. */
+ /*! TRUE if the channel is in a bridge. */
+ unsigned int in_bridge:1;
+ /*! TRUE if the channel just joined the bridge. */
+ unsigned int just_joined:1;
+ /*! TRUE if the channel is suspended from the bridge. */
unsigned int suspended:1;
- /*! Bit to indicate if a imparted channel is allowed to get hungup after leaving the bridge by the bridging core. */
- unsigned int allow_impart_hangup:1;
+ /*! TRUE if the channel must wait for an ast_bridge_depart to reclaim the channel. */
+ unsigned int depart_wait:1;
+ /* ^-- These flags change while the bridge is locked or before the channel is in the bridge. */
/*! Features structure for features that are specific to this channel */
struct ast_bridge_features *features;
/*! Technology optimization parameters used by bridging technologies capable of
* optimizing based upon talk detection. */
struct ast_bridge_tech_optimizations tech_args;
- /*! Queue of DTMF digits used for DTMF streaming */
- char dtmf_stream_q[8];
+ /*! Copy of read format used by chan before join */
+ struct ast_format read_format;
+ /*! Copy of write format used by chan before join */
+ struct ast_format write_format;
/*! Call ID associated with bridge channel */
struct ast_callid *callid;
+ /*! A clone of the roles living on chan when the bridge channel joins the bridge. This may require some opacification */
+ struct bridge_roles_datastore *bridge_roles;
/*! Linked list information */
AST_LIST_ENTRY(ast_bridge_channel) entry;
+ /*! Queue of outgoing frames to the channel. */
+ AST_LIST_HEAD_NOLOCK(, ast_frame) wr_queue;
+ /*! Pipe to alert thread when frames are put into the wr_queue. */
+ int alert_pipe[2];
+ /*! TRUE if the bridge channel thread is waiting on channels (needs to be atomically settable) */
+ int waiting;
+ /*!
+ * \brief The bridge channel thread activity.
+ *
+ * \details Used by local channel optimization to determine if
+ * the thread is in an acceptable state to optimize.
+ *
+ * \note Needs to be atomically settable.
+ */
+ enum ast_bridge_channel_thread_state activity;
+};
+
+enum ast_bridge_action_type {
+ /*! Bridged channel is to detect a feature hook */
+ AST_BRIDGE_ACTION_FEATURE,
+ /*! Bridged channel is to act on an interval hook */
+ AST_BRIDGE_ACTION_INTERVAL,
+ /*! Bridged channel is to send a DTMF stream out */
+ AST_BRIDGE_ACTION_DTMF_STREAM,
+ /*! Bridged channel is to indicate talking start */
+ AST_BRIDGE_ACTION_TALKING_START,
+ /*! Bridged channel is to indicate talking stop */
+ AST_BRIDGE_ACTION_TALKING_STOP,
+ /*! Bridge channel is to play the indicated sound file. */
+ AST_BRIDGE_ACTION_PLAY_FILE,
+ /*! Bridge channel is to run the indicated application. */
+ AST_BRIDGE_ACTION_RUN_APP,
+
+ /*
+ * Bridge actions put after this comment must never be put onto
+ * the bridge_channel wr_queue because they have other resources
+ * that must be freed.
+ */
+
+ /*! Bridge reconfiguration deferred technology destruction. */
+ AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY = 1000,
+ /*! Bridge deferred dissolving. */
+ AST_BRIDGE_ACTION_DEFERRED_DISSOLVING,
};
enum ast_bridge_video_mode_type {
@@ -207,13 +272,151 @@
};
/*!
+ * \brief Destroy the bridge.
+ *
+ * \param self Bridge to operate upon.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_bridge_destructor_fn)(struct ast_bridge *self);
+
+/*!
+ * \brief The bridge is being dissolved.
+ *
+ * \param self Bridge to operate upon.
+ *
+ * \details
[... 6668 lines stripped ...]
More information about the asterisk-commits
mailing list