[asterisk-commits] rmudgett: branch group/bridge_construction r380137 - in /team/group/bridge_co...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri Jan 25 16:08:34 CST 2013
Author: rmudgett
Date: Fri Jan 25 16:08:15 2013
New Revision: 380137
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=380137
Log:
Initial conversion of the system to use the Bridging API.
Expected things to not work:
* Local channel optimization is disabled.
* Channel driver transfers may not work.
* DTMF Monitor, MixMonitor, Parking features do not work.
* DTMF blind and attended transfer work but only on by the called party
and they currently work differently.
* Dynamic features do not work.
ast_bridge_impart()/ast_bridge_depart() usage changed.
Modified:
team/group/bridge_construction/bridges/bridge_builtin_features.c
team/group/bridge_construction/include/asterisk/bridging.h
team/group/bridge_construction/include/asterisk/bridging_features.h
team/group/bridge_construction/main/bridging.c
team/group/bridge_construction/main/features.c
Modified: team/group/bridge_construction/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/bridges/bridge_builtin_features.c?view=diff&rev=380137&r1=380136&r2=380137
==============================================================================
--- team/group/bridge_construction/bridges/bridge_builtin_features.c (original)
+++ team/group/bridge_construction/bridges/bridge_builtin_features.c Fri Jan 25 16:08:15 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,34 @@
/* 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);
-
+ 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;
}
@@ -79,7 +104,9 @@
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,28 +127,65 @@
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;
+
+ 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);
+ if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
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);
- 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, chan, bridge_channel->chan, NULL, 1)) {
+ ast_hangup(chan);
+ return 0;
+ }
return 0;
}
@@ -138,100 +202,111 @@
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);
-
- return 0;
-}
-
/*! \brief Internal built in feature for attended transfers */
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;
+ struct ast_channel *peer;
+ struct ast_bridge *attended_bridge;
+ struct ast_bridge_features caller_features;
enum ast_bridge_channel_state attended_bridge_result;
+ 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;
+
+ 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)) {
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_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_new(AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE,
+ AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
+ if (!attended_bridge) {
+ ast_hangup(peer);
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
+ return 0;
+ }
/* 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);
+ if (ast_bridge_impart(attended_bridge, peer, NULL, NULL, 0)) {
+ ast_bridge_destroy(attended_bridge);
+ ast_hangup(peer);
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
+ return 0;
+ }
/* 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);
+ 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);
/* 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 */
+ /* Wait for peer thread to exit bridge and die. */
+ if (!ast_autoservice_start(bridge_channel->chan)) {
+ ast_bridge_depart(attended_bridge, peer);
+ ast_autoservice_stop(bridge_channel->chan);
+ } else {
+ ast_bridge_depart(attended_bridge, peer);
+ }
+
+ /* Now that all channels are out of it we can destroy the bridge and the feature structures */
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);
+ ast_bridge_destroy(attended_bridge);
+
+ xfer_failed = -1;
+ switch (attended_bridge_result) {
+ case AST_BRIDGE_CHANNEL_STATE_END:
+ if (!ast_check_hangup_locked(bridge_channel->chan)) {
+ /* Transferer aborted the transfer. */
+ break;
}
- } 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);
+
+ /* 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, peer, bridge_channel->chan, NULL, 1);
+ break;
+ case AST_BRIDGE_CHANNEL_STATE_HANGUP:
+ /* Peer hungup */
+ break;
+ case AST_BRIDGE_CHANNEL_STATE_DEPART:
+ /*
+ * 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, peer, NULL, NULL, 1);
+ break;
+ default:
+ break;
+ }
+ 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);
+ }
+ }
return 0;
}
Modified: team/group/bridge_construction/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/bridging.h?view=diff&rev=380137&r1=380136&r2=380137
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging.h Fri Jan 25 16:08:15 2013
@@ -156,8 +156,8 @@
int fds[4];
/*! Bit to indicate whether the channel is suspended from the bridge or not */
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 imparted channel must wait for an explicit depart from the bridge to reclaim the channel. */
+ unsigned int depart_wait:1;
/*! Features structure for features that are specific to this channel */
struct ast_bridge_features *features;
/*! Technology optimization parameters used by bridging technologies capable of
@@ -212,6 +212,8 @@
* \brief Structure that contains information about a bridge
*/
struct ast_bridge {
+ /*! Condition, used if we want to wake up the bridge thread. */
+ ast_cond_t cond;
/*! Number of channels participating in the bridge */
int num;
/*! The video mode this bridge is using */
@@ -249,7 +251,9 @@
struct ast_callid *callid;
/*! Linked list of channels participating in the bridge */
AST_LIST_HEAD_NOLOCK(, ast_bridge_channel) channels;
- };
+ /*! Linked list of channels removed from the bridge and waiting to be departed. */
+ AST_LIST_HEAD_NOLOCK(, ast_bridge_channel) depart_wait;
+};
/*!
* \brief Create a new bridge
@@ -264,7 +268,7 @@
*
* \code
* struct ast_bridge *bridge;
- * bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE);
+ * bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
* \endcode
*
* This creates a simple two party bridge that will be destroyed once one of
@@ -374,9 +378,9 @@
*
* \param bridge Bridge to impart on
* \param chan Channel to impart
- * \param swap Channel to swap out if swapping
- * \param features Bridge features structure
- * \param allow_hangup Indicates if the bridge thread should manage hanging up of the channel or not.
+ * \param swap Channel to swap out if swapping. NULL if not swapping.
+ * \param features Bridge features structure. Must be NULL or obtained by ast_bridge_features_new().
+ * \param independent TRUE if caller does not want to reclaim the channel using ast_bridge_depart().
*
* \retval 0 on success
* \retval -1 on failure
@@ -387,18 +391,34 @@
* ast_bridge_impart(bridge, chan, NULL, NULL, 0);
* \endcode
*
- * This adds a channel pointed to by the chan pointer to the bridge pointed to by
- * the bridge pointer. This function will return immediately and will not wait
- * until the channel is no longer part of the bridge.
- *
- * If this channel will be replacing another channel the other channel can be specified
- * in the swap parameter. The other channel will be thrown out of the bridge in an
- * atomic fashion.
- *
- * If channel specific features are enabled a pointer to the features structure
- * can be specified in the features parameter.
- */
-int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, int allow_hangup);
+ * \details
+ * This adds a channel pointed to by the chan pointer to the
+ * bridge pointed to by the bridge pointer. This function will
+ * return immediately and will not wait until the channel is no
+ * longer part of the bridge.
+ *
+ * If this channel will be replacing another channel the other
+ * channel can be specified in the swap parameter. The other
+ * channel will be thrown out of the bridge in an atomic
+ * fashion.
+ *
+ * If channel specific features are enabled, a pointer to the
+ * features structure can be specified in the features
+ * parameter.
+ *
+ * \note If you impart a channel as not independent you MUST
+ * ast_bridge_depart() the channel. The bridge channel thread
+ * is created join-able. The implication is that the channel is
+ * special and is not intended to be moved to another bridge.
+ *
+ * \note If you impart a channel as independent you must not
+ * ast_bridge_depart() the channel. The bridge channel thread
+ * is created non-join-able. The channel must be treated as if
+ * it were placed into the bridge by ast_bridge_join().
+ * Channels placed into a bridge by ast_bridge_join() are
+ * removed by a third party using ast_bridge_remove().
+ */
+int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, int independent);
/*!
* \brief Depart a channel from a bridge
@@ -420,7 +440,7 @@
* This does not hang up the channel.
*
* \note This API call can only be used on channels that were added to the bridge
- * using the ast_bridge_impart API call.
+ * using the ast_bridge_impart API call with the independent flag FALSE.
*/
int ast_bridge_depart(struct ast_bridge *bridge, struct ast_channel *chan);
Modified: team/group/bridge_construction/include/asterisk/bridging_features.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/bridging_features.h?view=diff&rev=380137&r1=380136&r2=380137
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging_features.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging_features.h Fri Jan 25 16:08:15 2013
@@ -30,10 +30,12 @@
/*! \brief Flags used for bridge features */
enum ast_bridge_feature_flags {
- /*! Upon hangup the bridge should be discontinued */
- AST_BRIDGE_FLAG_DISSOLVE = (1 << 0),
+ /*! Upon channel hangup the bridge should be ended. */
+ AST_BRIDGE_FLAG_DISSOLVE_HANGUP = (1 << 0),
/*! Move between bridging technologies as needed. */
AST_BRIDGE_FLAG_SMART = (1 << 1),
+ /*! The bridge ends when the last channel leaves. (There is no external bridge manager.) */
+ AST_BRIDGE_FLAG_DISSOLVE_EMPTY = (1 << 2),
};
/*! \brief Built in DTMF features */
@@ -52,6 +54,25 @@
* AST_BRIDGE_CHANNEL_STATE_END.
*/
AST_BRIDGE_BUILTIN_HANGUP,
+ /*!
+ * DTMF based Park
+ *
+ * \details The bridge is parked and the channel hears the
+ * parking slot to which it was parked.
+ */
+ AST_BRIDGE_BUILTIN_PARKCALL,
+ /*!
+ * DTMF one-touch-record toggle using Monitor app.
+ *
+ * \note Only valid on two party bridges.
+ */
+ AST_BRIDGE_BUILTIN_AUTOMON,
+ /*!
+ * DTMF one-touch-record toggle using MixMonitor app.
+ *
+ * \note Only valid on two party bridges.
+ */
+ AST_BRIDGE_BUILTIN_AUTOMIXMON,
/*! End terminator for list of built in features. Must remain last. */
AST_BRIDGE_BUILTIN_END
@@ -153,14 +174,12 @@
* \brief Structure that contains configuration information for the attended transfer built in feature
*/
struct ast_bridge_features_attended_transfer {
- /*! DTMF string used to abort the transfer */
- char abort[MAXIMUM_DTMF_FEATURE_STRING];
+ /*! Context to use for transfers */
+ char context[AST_MAX_CONTEXT];
/*! DTMF string used to turn the transfer into a three way conference */
char threeway[MAXIMUM_DTMF_FEATURE_STRING];
/*! DTMF string used to complete the transfer */
char complete[MAXIMUM_DTMF_FEATURE_STRING];
- /*! Context to use for transfers */
- char context[AST_MAX_CONTEXT];
};
/*!
@@ -288,11 +307,11 @@
* \code
* struct ast_bridge_features features;
* ast_bridge_features_init(&features);
- * ast_bridge_features_set_flag(&features, AST_BRIDGE_FLAG_DISSOLVE);
- * \endcode
- *
- * This sets the AST_BRIDGE_FLAG_DISSOLVE feature to be enabled on the features structure
- * 'features'.
+ * ast_bridge_features_set_flag(&features, AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
+ * \endcode
+ *
+ * This sets the AST_BRIDGE_FLAG_DISSOLVE_HANGUP feature to be
+ * enabled on the features structure 'features'.
*/
void ast_bridge_features_set_flag(struct ast_bridge_features *features, enum ast_bridge_feature_flags flag);
@@ -341,6 +360,41 @@
void ast_bridge_features_cleanup(struct ast_bridge_features *features);
/*!
+ * \brief Allocate a new bridge features struct.
+ * \since 12.0.0
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features *features;
+ * features = ast_bridge_features_new();
+ * ast_bridge_features_destroy(features);
+ * \endcode
+ *
+ * \retval features New allocated features struct.
+ * \retval NULL on error.
+ */
+struct ast_bridge_features *ast_bridge_features_new(void);
+
+/*!
+ * \brief Destroy an allocated bridge features struct.
+ * \since 12.0.0
+ *
+ * \param features Bridge features structure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features *features;
+ * features = ast_bridge_features_new();
+ * ast_bridge_features_destroy(features);
+ * \endcode
+ *
+ * \return Nothing
+ */
+void ast_bridge_features_destroy(struct ast_bridge_features *features);
+
+/*!
* \brief Play a DTMF stream into a bridge, optionally not to a given channel
*
* \param bridge Bridge to play stream into
Modified: team/group/bridge_construction/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/bridging.c?view=diff&rev=380137&r1=380136&r2=380137
==============================================================================
--- team/group/bridge_construction/main/bridging.c (original)
+++ team/group/bridge_construction/main/bridging.c Fri Jan 25 16:08:15 2013
@@ -154,8 +154,9 @@
static void bridge_poke(struct ast_bridge *bridge)
{
/* Poke the thread just in case */
- if (bridge->thread != AST_PTHREADT_NULL && bridge->thread != AST_PTHREADT_STOP) {
+ if (bridge->thread != AST_PTHREADT_NULL) {
pthread_kill(bridge->thread, SIGURG);
+ ast_cond_signal(&bridge->cond);
}
}
@@ -170,13 +171,16 @@
*/
static void bridge_stop(struct ast_bridge *bridge)
{
- pthread_t thread = bridge->thread;
+ pthread_t thread;
bridge->stop = 1;
bridge_poke(bridge);
+ thread = bridge->thread;
+ bridge->thread = AST_PTHREADT_NULL;
ao2_unlock(bridge);
pthread_join(thread, NULL);
ao2_lock(bridge);
+ bridge->stop = 0;
}
/*!
@@ -211,6 +215,8 @@
bridge->array = new_array;
bridge->array_size += BRIDGE_ARRAY_GROW;
}
+
+ bridge_poke(bridge);
}
/*! \brief Helper function to remove a channel from the bridge array
@@ -285,10 +291,10 @@
/*! \brief Internal function to see whether a bridge should dissolve, and if so do it */
static void bridge_check_dissolve(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
{
- if (!ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE)
+ if (!ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP)
&& (!bridge_channel->features
|| !bridge_channel->features->usable
- || !ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_FLAG_DISSOLVE))) {
+ || !ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP))) {
return;
}
@@ -324,6 +330,14 @@
static int bridge_drop_control_frame(int subclass)
{
switch (subclass) {
+ case AST_CONTROL_READ_ACTION:
+ case AST_CONTROL_CC:
+ case AST_CONTROL_MCID:
+ case AST_CONTROL_AOC:
+ case AST_CONTROL_CONNECTED_LINE:
+ case AST_CONTROL_REDIRECTING:
+ return 1;
+
case AST_CONTROL_ANSWER:
case -1:
return 1;
@@ -404,7 +418,10 @@
/*! \brief Generic thread loop, TODO: Rethink this/improve it */
static int generic_thread_loop(struct ast_bridge *bridge)
{
- while (!bridge->stop && !bridge->refresh && bridge->array_num) {
+ if (bridge->stop || bridge->refresh || !bridge->array_num) {
+ return 0;
+ }
+ for (;;) {
struct ast_channel *winner;
int to = -1;
@@ -422,11 +439,13 @@
bridge->waiting = 0;
ao2_lock(bridge);
+ if (bridge->stop || bridge->refresh || !bridge->array_num) {
+ return 0;
+ }
+
/* Process whatever they did */
ast_bridge_handle_trip(bridge, NULL, winner, -1);
}
-
- return 0;
}
/*! \brief Bridge thread function */
@@ -444,7 +463,13 @@
ast_debug(1, "Started bridge thread for %p\n", bridge);
/* Loop around until we are told to stop */
- while (!bridge->stop && bridge->array_num && !res) {
+ while (!bridge->stop) {
+ if (!bridge->array_num) {
+ /* Wait for a channel to be added to the bridge. */
+ ast_cond_wait(&bridge->cond, ao2_object_get_lockaddr(bridge));
+ continue;
+ }
+
/* In case the refresh bit was set simply set it back to off */
bridge->refresh = 0;
@@ -459,14 +484,20 @@
res = bridge->technology->thread
? bridge->technology->thread(bridge)
: generic_thread_loop(bridge);
+ if (res) {
+ /*
+ * A bridge error occurred. Sleep and try again later so we
+ * won't flood the logs.
+ */
+ ao2_unlock(bridge);
+ sleep(1);
+ ao2_lock(bridge);
+ }
}
ast_debug(1, "Ending bridge thread for %p\n", bridge);
- /* Indicate the bridge thread is no longer active */
- bridge->thread = AST_PTHREADT_NULL;
ao2_unlock(bridge);
-
ao2_ref(bridge, -1);
return NULL;
@@ -513,6 +544,7 @@
/* There should not be any channels left in the bridge. */
ast_assert(AST_LIST_EMPTY(&bridge->channels));
+ ast_assert(AST_LIST_EMPTY(&bridge->depart_wait));
/* Pass off the bridge to the technology to destroy if needed */
if (bridge->technology->destroy) {
@@ -533,6 +565,7 @@
/* Drop the array of channels */
ast_free(bridge->array);
+ ast_cond_destroy(&bridge->cond);
}
struct ast_bridge *ast_bridge_new(uint32_t capabilities, int flags)
@@ -569,6 +602,7 @@
return NULL;
}
+ ast_cond_init(&bridge->cond, NULL);
bridge->technology = bridge_technology;
bridge->thread = AST_PTHREADT_NULL;
@@ -695,6 +729,7 @@
.bridge_pvt = bridge->bridge_pvt,
};
struct ast_bridge_channel *bridge_channel2;
+ pthread_t thread;
/* Based on current feature determine whether we want to change bridge technologies or not */
if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX) {
@@ -723,10 +758,16 @@
return -1;
}
+ /*
+ * We are now committed to changing the bridge technology. We
+ * must not release the bridge lock until we have installed the
+ * new bridge technology.
+ */
ast_debug(1, "Performing smart bridge operation on bridge %p, moving from bridge technology %s to %s\n",
bridge, old_technology->name, new_technology->name);
/* If a thread is currently executing for the current technology tell it to stop */
+ thread = AST_PTHREADT_NULL;
if (bridge->thread != AST_PTHREADT_NULL) {
/*
* If the new bridge technology also needs a thread simply tell
@@ -740,7 +781,10 @@
bridge_poke(bridge);
} else {
ast_debug(1, "Telling current bridge thread for bridge %p to stop\n", bridge);
- bridge_stop(bridge);
+ bridge->stop = 1;
+ bridge_poke(bridge);
+ thread = bridge->thread;
+ bridge->thread = AST_PTHREADT_NULL;
}
}
@@ -795,6 +839,14 @@
/* Fourth we tell them to wake up so they become aware that the above has happened */
bridge_channel_poke(bridge_channel2);
+ }
+
+ if (thread != AST_PTHREADT_NULL) {
+ /* Wait for the old bridge thread to die. */
+ ao2_unlock(bridge);
+ pthread_join(thread, NULL);
+ ao2_lock(bridge);
+ bridge->stop = 0;
}
/* Now that all the channels have been moved over we need to get rid of all the information the old technology may have left around */
@@ -964,7 +1016,11 @@
* here if the hook did not already change the state.
*/
if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) {
- ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+ ao2_lock(bridge_channel);
+ if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_FEATURE) {
+ ast_bridge_change_state_nolock(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+ }
+ ao2_unlock(bridge_channel);
}
} else {
ast_bridge_dtmf_stream(bridge, dtmf, bridge_channel->chan);
@@ -1052,10 +1108,17 @@
while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
/* Update bridge pointer on channel */
ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge);
+
+ /* Wait for an old bridge thread to die. */
+ while (bridge_channel->bridge->stop) {
+ ao2_unlock(bridge_channel->bridge);
+ sched_yield();
+ ao2_lock(bridge_channel->bridge);
+ }
+
/* If the technology requires a thread and one is not running, start it up */
if (bridge_channel->bridge->thread == AST_PTHREADT_NULL
&& (bridge_channel->bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD)) {
- bridge_channel->bridge->stop = 0;
ast_debug(1, "Starting a bridge thread for bridge %p\n", bridge_channel->bridge);
ao2_ref(bridge_channel->bridge, +1);
if (ast_pthread_create(&bridge_channel->bridge->thread, NULL, bridge_thread, bridge_channel->bridge)) {
@@ -1134,6 +1197,12 @@
bridge_channel->bridge->num--;
AST_LIST_REMOVE(&bridge_channel->bridge->channels, bridge_channel, entry);
+ if (bridge_channel->depart_wait
+ && bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_DEPART) {
+ /* Put the channel into the ast_bridge_depart wait list. */
+ AST_LIST_INSERT_TAIL(&bridge_channel->bridge->depart_wait, bridge_channel, entry);
+ }
+
bridge_array_remove(bridge_channel->bridge, bridge_channel->chan);
/* Perform the smart bridge operation if needed since a channel has left */
@@ -1225,37 +1294,69 @@
return state;
}
-/*! \brief Thread responsible for imparted bridged channels */
-static void *bridge_channel_thread(void *data)
+/*! \brief Thread responsible for imparted bridged channels to be departed */
+static void *bridge_channel_depart_thread(void *data)
{
struct ast_bridge_channel *bridge_channel = data;
- enum ast_bridge_channel_state state;
if (bridge_channel->callid) {
ast_callid_threadassoc_add(bridge_channel->callid);
}
+ bridge_channel_join(bridge_channel);
+
+ /* cleanup */
+ bridge_channel->swap = NULL;
+ ast_bridge_features_destroy(bridge_channel->features);
+ bridge_channel->features = NULL;
+
+ return NULL;
+}
+
+/*! \brief Thread responsible for independent imparted bridged channels */
+static void *bridge_channel_ind_thread(void *data)
+{
+ struct ast_bridge_channel *bridge_channel = data;
+ struct ast_channel *chan;
+ enum ast_bridge_channel_state state;
+
+ if (bridge_channel->callid) {
+ ast_callid_threadassoc_add(bridge_channel->callid);
+ }
+
state = bridge_channel_join(bridge_channel);
- /* If no other thread is going to take the channel then hang it up, or else we would have to service it until something else came along */
- if (bridge_channel->allow_impart_hangup && (state == AST_BRIDGE_CHANNEL_STATE_END || state == AST_BRIDGE_CHANNEL_STATE_HANGUP)) {
- ast_hangup(bridge_channel->chan);
- }
+ chan = bridge_channel->chan;
/* cleanup */
- ao2_lock(bridge_channel);
bridge_channel->chan = NULL;
bridge_channel->swap = NULL;
+ ast_bridge_features_destroy(bridge_channel->features);
bridge_channel->features = NULL;
- ao2_unlock(bridge_channel);
ao2_ref(bridge_channel, -1);
+ switch (state) {
+ case AST_BRIDGE_CHANNEL_STATE_DEPART:
+ ast_log(LOG_ERROR, "Independently imparted channel was departed: %s\n",
+ ast_channel_name(chan));
+ ast_assert(0);
+ /* fallthrough */
+ case AST_BRIDGE_CHANNEL_STATE_HANGUP:
+ case AST_BRIDGE_CHANNEL_STATE_END:
+ ast_hangup(chan);
+ break;
+ default:
+ ast_hangup(chan);
+ break;
+ }
+
return NULL;
}
-int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, int allow_hangup)
-{
+int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, int independent)
+{
+ int res;
struct ast_bridge_channel *bridge_channel;
/* Try to allocate a structure for the bridge channel */
@@ -1268,11 +1369,23 @@
bridge_channel->chan = chan;
bridge_channel->swap = swap;
bridge_channel->features = features;
- bridge_channel->allow_impart_hangup = allow_hangup;
+ bridge_channel->depart_wait = independent ? 0 : 1;
bridge_channel->callid = ast_read_threadstorage_callid();
/* Actually create the thread that will handle the channel */
- if (ast_pthread_create(&bridge_channel->thread, NULL, bridge_channel_thread, bridge_channel)) {
+ if (independent) {
+ res = ast_pthread_create_detached(&bridge_channel->thread, NULL,
+ bridge_channel_ind_thread, bridge_channel);
+ } else {
+ res = ast_pthread_create(&bridge_channel->thread, NULL,
+ bridge_channel_depart_thread, bridge_channel);
+ }
+ if (res) {
+ /* cleanup */
+ bridge_channel->chan = NULL;
+ bridge_channel->swap = NULL;
+ bridge_channel->features = NULL;
+
ao2_ref(bridge_channel, -1);
return -1;
}
@@ -1287,19 +1400,44 @@
ao2_lock(bridge);
- /* Try to find the channel that we want to depart */
- if (!(bridge_channel = find_bridge_channel(bridge, chan))) {
- ao2_unlock(bridge);
- return -1;
- }
-
- ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
+ do {
+ /* Try to find the channel that we want to depart */
+ bridge_channel = find_bridge_channel(bridge, chan);
+ if (bridge_channel) {
+ /* Channel is still in the bridge. */
+ if (!bridge_channel->depart_wait) {
+ ast_log(LOG_ERROR, "Bridged channel cannot be departed: %s\n",
+ ast_channel_name(bridge_channel->chan));
+ ao2_unlock(bridge);
+ return -1;
+ }
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
+ break;
+ }
+
+ /* Was the channel already removed from the bridge? */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&bridge->depart_wait, bridge_channel, entry) {
+ if (bridge_channel->chan == chan) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ if (!bridge_channel) {
+ /* Channel does not exist. */
+ ao2_unlock(bridge);
+ return -1;
+ }
+ } while (0);
thread = bridge_channel->thread;
ao2_unlock(bridge);
+ /* Wait for the depart thread to die */
pthread_join(thread, NULL);
+ /* We can get rid of the bridge_channel after the depart thread has died. */
+ ao2_ref(bridge_channel, -1);
return 0;
}
@@ -1325,6 +1463,7 @@
int ast_bridge_merge(struct ast_bridge *bridge0, struct ast_bridge *bridge1)
{
struct ast_bridge_channel *bridge_channel;
+ pthread_t thread;
ao2_lock(bridge0);
ao2_lock(bridge1);
@@ -1350,9 +1489,13 @@
}
/* If a thread is currently executing on bridge1 tell it to stop */
- if (bridge1->thread) {
+ thread = AST_PTHREADT_NULL;
+ if (bridge1->thread != AST_PTHREADT_NULL) {
ast_debug(1, "Telling bridge thread on bridge %p to stop as it is being merged into %p\n", bridge1, bridge0);
- bridge1->thread = AST_PTHREADT_STOP;
+ bridge1->stop = 1;
+ bridge_poke(bridge1);
+ thread = bridge1->thread;
+ bridge1->thread = AST_PTHREADT_NULL;
}
/* Move channels from bridge1 over to bridge0 */
@@ -1396,8 +1539,14 @@
ast_debug(1, "Merged channels from bridge %p into bridge %p\n", bridge1, bridge0);
+ ao2_unlock(bridge0);
ao2_unlock(bridge1);
- ao2_unlock(bridge0);
+ if (thread != AST_PTHREADT_NULL) {
+ pthread_join(thread, NULL);
+ ao2_lock(bridge1);
+ bridge1->stop = 0;
+ ao2_unlock(bridge1);
+ }
return 0;
}
@@ -1565,6 +1714,23 @@
features->talker_destructor_cb(features->talker_pvt_data);
features->talker_pvt_data = NULL;
}
+}
+
+struct ast_bridge_features *ast_bridge_features_new(void)
+{
+ struct ast_bridge_features *features;
+
+ features = ast_calloc(1, sizeof(*features));
+ return features;
+}
+
+void ast_bridge_features_destroy(struct ast_bridge_features *features)
+{
+ if (!features) {
+ return;
+ }
+ ast_bridge_features_cleanup(features);
+ ast_free(features);
}
int ast_bridge_dtmf_stream(struct ast_bridge *bridge, const char *dtmf, struct ast_channel *chan)
Modified: team/group/bridge_construction/main/features.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/features.c?view=diff&rev=380137&r1=380136&r2=380137
==============================================================================
--- team/group/bridge_construction/main/features.c (original)
+++ team/group/bridge_construction/main/features.c Fri Jan 25 16:08:15 2013
@@ -72,6 +72,7 @@
#include "asterisk/astobj2.h"
#include "asterisk/cel.h"
#include "asterisk/test.h"
+#include "asterisk/bridging.h"
/*
* Party A - transferee
@@ -3710,6 +3711,7 @@
return res;
}
+#if 0//BUGBUG
/*!
* \brief Check the dynamic features
* \param chan,peer,config,code,sense
@@ -3756,6 +3758,7 @@
return res;
}
+#endif
int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, const char *code, struct ast_call_feature *feature) {
@@ -3763,6 +3766,7 @@
return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, FEATURE_INTERPRET_DETECT, feature);
}
+#if 0//BUGBUG
/*! \brief Check if a feature exists */
static int feature_check(struct ast_channel *chan, struct ast_flags *features, char *code) {
struct ast_str *chan_dynamic_features;
@@ -3781,6 +3785,7 @@
return res;
}
+#endif
static void set_config_flags(struct ast_channel *chan, struct ast_bridge_config *config)
{
@@ -4309,6 +4314,78 @@
}
/*!
+ * \internal
+ * \brief Setup bridge channel features.
+ * \since 12.0.0
+ *
+ * \param features Bridge features to setup.
+ * \param flags DTMF features to enable.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+static int setup_bridge_channel_features(struct ast_bridge_features *features, struct ast_flags *flags)
+{
+ struct ast_call_feature *dtmf;
+ int res = 0;
+
+ if (ast_test_flag(flags, AST_FEATURE_REDIRECT)) {
+ /* Add atxfer and blind transfer. */
+ ast_rwlock_rdlock(&features_lock);
+ dtmf = ast_find_call_feature("blindxfer");
+ if (dtmf && !ast_strlen_zero(dtmf->exten)) {
+/* BUGBUG need to supply a blind transfer structure and destructor to use other than defaults */
+ res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_BLINDTRANSFER, dtmf->exten, NULL);
+ }
+ dtmf = ast_find_call_feature("atxfer");
+ if (dtmf && !ast_strlen_zero(dtmf->exten)) {
[... 442 lines stripped ...]
More information about the asterisk-commits
mailing list