diff --git a/apps/app_agent_pool.c b/apps/app_agent_pool.c index 92209e1eb279eacba67176ec6f5d7322415b02e5..fb73d869fb68108aa471371271339bb397e57e17 100644 --- a/apps/app_agent_pool.c +++ b/apps/app_agent_pool.c @@ -41,6 +41,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/channel.h" #include "asterisk/bridging.h" +#include "asterisk/bridging_internal.h" #include "asterisk/bridging_basic.h" #include "asterisk/config_options.h" #include "asterisk/features_config.h" @@ -1054,7 +1055,7 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru if (!caller_bridge) { /* Reset agent. */ - ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel); return; } res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan, @@ -1062,7 +1063,7 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru if (res) { /* Reset agent. */ ast_bridge_destroy(caller_bridge); - ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel); return; } ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0); @@ -1158,13 +1159,13 @@ static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bri if (deferred_logoff) { ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username); - ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel); } else if (probation_timedout) { ast_debug(1, "Agent %s: Login complete.\n", agent->username); agent_devstate_changed(agent->username); } else if (ack_timedout) { ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username); - ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel); } else if (wrapup_timedout) { ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username); agent_devstate_changed(agent->username); @@ -1269,7 +1270,7 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha * agent will have some slightly different behavior in corner * cases. */ - ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel); return 0; } @@ -1393,11 +1394,11 @@ static struct ast_bridge *bridge_agent_hold_new(void) { struct ast_bridge *bridge; - bridge = ast_bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table); - bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING, + bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table); + bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING, AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED); - bridge = ast_bridge_register(bridge); + bridge = bridge_register(bridge); return bridge; } @@ -1703,7 +1704,7 @@ static void caller_abort_agent(struct agent_pvt *agent) } /* Kick the agent out of the holding bridge to reset it. */ - ast_bridge_change_state_nolock(logged, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge_nolock(logged); ast_bridge_channel_unlock(logged); } @@ -1713,7 +1714,7 @@ static int caller_safety_timeout(struct ast_bridge *bridge, struct ast_bridge_ch if (agent->state == AGENT_STATE_CALL_PRESENT) { ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username); - ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel); caller_abort_agent(agent); } diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c index 87b74d4e2144adf57a77125f6f70d625e196c8e4..8554495eb3d2e60d31f1bd9ac254bda834894439 100644 --- a/bridges/bridge_builtin_features.c +++ b/bridges/bridge_builtin_features.c @@ -483,7 +483,7 @@ static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel * * bridge_channel to force the channel out of the bridge and the * core takes care of the rest. */ - ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel); return 0; } diff --git a/bridges/bridge_builtin_interval_features.c b/bridges/bridge_builtin_interval_features.c index a4fd97ba09a57a62cf50712033880345cbfbf42d..2ca3f7ddb5bec8e031cbdafb946db09f9c172da6 100644 --- a/bridges/bridge_builtin_interval_features.c +++ b/bridges/bridge_builtin_interval_features.c @@ -58,7 +58,7 @@ static int bridge_features_duration_callback(struct ast_bridge *bridge, struct a ast_stream_and_wait(bridge_channel->chan, limits->duration_sound, AST_DIGIT_NONE); } - ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel); ast_test_suite_event_notify("BRIDGE_TIMELIMIT", "Channel1: %s", ast_channel_name(bridge_channel->chan)); return -1; diff --git a/include/asterisk/bridging.h b/include/asterisk/bridging.h index 0bba614344117e1546278ae86ae551f15d3ea90e..bc16ae0bc1ce996e5e6ec210772a3aa793a902f2 100644 --- a/include/asterisk/bridging.h +++ b/include/asterisk/bridging.h @@ -19,7 +19,7 @@ /*! * \file - * \brief Channel Bridging API + * \brief Bridging API * * \author Richard Mudgett <rmudgett@digium.com> * \author Joshua Colp <jcolp@digium.com> @@ -30,7 +30,7 @@ */ /*! - * \page AstBridging Channel Bridging API + * \page AstBridging Bridging API * * The purpose of this API is to provide an easy and flexible way to bridge * channels of different technologies with different features. @@ -70,10 +70,15 @@ extern "C" { #endif #include "asterisk/bridging_features.h" +#include "asterisk/bridging_channel.h" #include "asterisk/bridging_roles.h" #include "asterisk/dsp.h" #include "asterisk/uuid.h" +struct ast_bridge_technology; +struct ast_bridge; +struct ast_bridge_tech_optimizations; + /*! \brief Capabilities for a bridge technology */ enum ast_bridge_capability { /*! Bridge technology can service calls on hold. */ @@ -88,160 +93,7 @@ enum ast_bridge_capability { 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 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, -}; - -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; -struct ast_bridge; - -/*! - * \brief Structure specific to bridge technologies capable of - * performing talking optimizations. - */ -struct ast_bridge_tech_optimizations { - /*! The amount of time in ms that talking must be detected before - * the dsp determines that talking has occurred */ - unsigned int talking_threshold; - /*! The amount of time in ms that silence must be detected before - * the dsp determines that talking has stopped */ - unsigned int silence_threshold; - /*! Whether or not the bridging technology should drop audio - * detected as silence from the mix. */ - unsigned int drop_silence:1; -}; - -/*! - * \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 */ - enum ast_bridge_channel_state state; - /*! Asterisk channel participating in the bridge */ - struct ast_channel *chan; - /*! Asterisk channel we are swapping with (if swapping) */ - struct ast_channel *swap; - /*! - * \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; - /*! - * \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; - /*! - * \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; - /* 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; - /*! 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; - /*! 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 channel is to run the custom callback routine. */ - AST_BRIDGE_ACTION_CALLBACK, - /*! Bridge channel is to get parked. */ - AST_BRIDGE_ACTION_PARK, - /*! Bridge channel is to execute a blind transfer. */ - AST_BRIDGE_ACTION_BLIND_TRANSFER, - /*! Bridge channel is to execute an attended transfer */ - AST_BRIDGE_ACTION_ATTENDED_TRANSFER, - - /* - * 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, -}; - +/*! \brief Video source modes */ enum ast_bridge_video_mode_type { /*! Video is not allowed in the bridge */ AST_BRIDGE_VIDEO_MODE_NONE = 0, @@ -252,14 +104,14 @@ enum ast_bridge_video_mode_type { AST_BRIDGE_VIDEO_MODE_TALKER_SRC, }; -/*! This is used for both SINGLE_SRC mode to set what channel +/*! \brief This is used for both SINGLE_SRC mode to set what channel * should be the current single video feed */ struct ast_bridge_video_single_src_data { /*! Only accept video coming from this channel */ struct ast_channel *chan_vsrc; }; -/*! This is used for both SINGLE_SRC_TALKER mode to set what channel +/*! \brief This is used for both SINGLE_SRC_TALKER mode to set what channel * should be the current single video feed */ struct ast_bridge_video_talker_src_data { /*! Only accept video coming from this channel */ @@ -270,6 +122,7 @@ struct ast_bridge_video_talker_src_data { struct ast_channel *chan_old_vsrc; }; +/*! \brief Data structure that defines a video source mode */ struct ast_bridge_video_mode { enum ast_bridge_video_mode_type mode; /* Add data for all the video modes here. */ @@ -371,7 +224,7 @@ typedef int (*ast_bridge_merge_priority_fn)(struct ast_bridge *self); * \brief Bridge virtual methods table definition. * * \note Any changes to this struct must be reflected in - * ast_bridge_alloc() validity checking. + * bridge_alloc() validity checking. */ struct ast_bridge_methods { /*! Bridge class name for log messages. */ @@ -390,6 +243,8 @@ struct ast_bridge_methods { ast_bridge_merge_priority_fn get_merge_priority; }; +struct ast_bridge_channel; + /*! Softmix technology parameters. */ struct ast_bridge_softmix { /*! The video mode softmix is using */ @@ -455,75 +310,9 @@ struct ast_bridge { char uniqueid[AST_UUID_STR_LEN]; }; -/*! - * \brief Register the new bridge with the system. - * \since 12.0.0 - * - * \param bridge What to register. (Tolerates a NULL pointer) - * - * \code - * struct ast_bridge *ast_bridge_basic_new(uint32_t capabilities, int flags, uint32 dtmf_features) - * { - * void *bridge; - * - * bridge = ast_bridge_alloc(sizeof(struct ast_bridge_basic), &ast_bridge_basic_v_table); - * bridge = ast_bridge_base_init(bridge, capabilities, flags); - * bridge = ast_bridge_basic_init(bridge, dtmf_features); - * bridge = ast_bridge_register(bridge); - * return bridge; - * } - * \endcode - * - * \note This must be done after a bridge constructor has - * completed setting up the new bridge but before it returns. - * - * \note After a bridge is registered, ast_bridge_destroy() must - * eventually be called to get rid of the bridge. - * - * \retval bridge on success. - * \retval NULL on error. - */ -struct ast_bridge *ast_bridge_register(struct ast_bridge *bridge); - -/*! - * \internal - * \brief Allocate the bridge class object memory. - * \since 12.0.0 - * - * \param size Size of the bridge class structure to allocate. - * \param v_table Bridge class virtual method table. - * - * \retval bridge on success. - * \retval NULL on error. - */ -struct ast_bridge *ast_bridge_alloc(size_t size, const struct ast_bridge_methods *v_table); - /*! \brief Bridge base class virtual method table. */ extern struct ast_bridge_methods ast_bridge_base_v_table; -/*! - * \brief Initialize the base class of the bridge. - * - * \param self Bridge to operate upon. (Tolerates a NULL pointer) - * \param capabilities The capabilities that we require to be used on the bridge - * \param flags Flags that will alter the behavior of the bridge - * - * \retval self on success - * \retval NULL on failure, self is already destroyed - * - * Example usage: - * - * \code - * struct ast_bridge *bridge; - * bridge = ast_bridge_alloc(sizeof(*bridge), &ast_bridge_base_v_table); - * bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE_HANGUP); - * \endcode - * - * This creates a no frills two party bridge that will be - * destroyed once one of the channels hangs up. - */ -struct ast_bridge *ast_bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags); - /*! * \brief Create a new base class bridge * @@ -824,20 +613,6 @@ int ast_bridge_move(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge */ void ast_bridge_merge_inhibit(struct ast_bridge *bridge, int request); -/*! - * \brief Adjust the bridge_channel's bridge merge inhibit request count. - * \since 12.0.0 - * - * \param bridge_channel What to operate on. - * \param request Inhibit request increment. - * (Positive to add requests. Negative to remove requests.) - * - * \note This API call is meant for internal bridging operations. - * - * \retval bridge adjusted merge inhibit with reference count. - */ -struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request); - /*! * \brief Suspend a channel temporarily from a bridge * @@ -942,101 +717,6 @@ enum ast_bridge_optimization { enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *chan_bridge, struct ast_bridge *peer_bridge); -/*! - * \brief Try locking the bridge_channel. - * - * \param bridge_channel What to try locking - * - * \retval 0 on success. - * \retval non-zero on error. - */ -#define ast_bridge_channel_trylock(bridge_channel) _ast_bridge_channel_trylock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel) -static inline int _ast_bridge_channel_trylock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var) -{ - return __ao2_trylock(bridge_channel, AO2_LOCK_REQ_MUTEX, file, function, line, var); -} - -/*! - * \brief Lock the bridge_channel. - * - * \param bridge_channel What to lock - * - * \return Nothing - */ -#define ast_bridge_channel_lock(bridge_channel) _ast_bridge_channel_lock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel) -static inline void _ast_bridge_channel_lock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var) -{ - __ao2_lock(bridge_channel, AO2_LOCK_REQ_MUTEX, file, function, line, var); -} - -/*! - * \brief Unlock the bridge_channel. - * - * \param bridge_channel What to unlock - * - * \return Nothing - */ -#define ast_bridge_channel_unlock(bridge_channel) _ast_bridge_channel_unlock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel) -static inline void _ast_bridge_channel_unlock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var) -{ - __ao2_unlock(bridge_channel, file, function, line, var); -} - -/*! - * \brief Lock the bridge associated with the bridge channel. - * \since 12.0.0 - * - * \param bridge_channel Channel that wants to lock the bridge. - * - * \details - * This is an upstream lock operation. The defined locking - * order is bridge then bridge_channel. - * - * \note On entry, neither the bridge nor bridge_channel is locked. - * - * \note The bridge_channel->bridge pointer changes because of a - * bridge-merge/channel-move operation between bridges. - * - * \return Nothing - */ -void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel); - -/*! - * \brief Set bridge channel state to leave bridge (if not leaving already) with no lock. - * - * \param bridge_channel Channel to change the state on - * \param new_state The new state to place the channel into - * - * \note This API call is only meant to be used within the - * bridging module and hook callbacks to request the channel - * exit the bridge. - * - * \note This function assumes the bridge_channel is locked. - */ -void ast_bridge_change_state_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state); - -/*! - * \brief Set bridge channel state to leave bridge (if not leaving already). - * - * \param bridge_channel Channel to change the state on - * \param new_state The new state to place the channel into - * - * Example usage: - * - * \code - * ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); - * \endcode - * - * This places the channel pointed to by bridge_channel into the - * state AST_BRIDGE_CHANNEL_STATE_HANGUP if it was - * AST_BRIDGE_CHANNEL_STATE_WAIT before. - * - * \note This API call is only meant to be used within the - * bridging module and hook callbacks to request the channel - * exit the bridge. - */ -void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state); - /*! * \brief Put an action onto the specified bridge. * \since 12.0.0 @@ -1052,37 +732,6 @@ void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast */ int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action); -/*! - * \brief Update the linkedid for all channels in a bridge - * \since 12.0.0 - * - * \param bridge The bridge to update the linkedids on - * \param bridge_channel The channel joining the bridge - * \param swap The channel being swapped out of the bridge. May be NULL. - * - * \note The bridge must be locked prior to calling this function. - * \note This API call is meant for internal bridging operations. - */ -void ast_bridge_update_linkedids(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap); - -/*! - * \brief Update the accountcodes for a channel entering a bridge - * \since 12.0.0 - * - * This function updates the accountcode and peeraccount on channels in two-party - * bridges. In multi-party bridges, peeraccount is not set - it doesn't make much sense - - * however accountcode propagation will still occur if the channel joining has an - * accountcode. - * - * \param bridge The bridge to update the accountcodes in - * \param bridge_channel The channel joining the bridge - * \param swap The channel being swapped out of the bridge. May be NULL. - * - * \note The bridge must be locked prior to calling this function. - * \note This API call is meant for internal bridging operations. - */ -void ast_bridge_update_accountcodes(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap); - /*! * \brief Queue the given frame to everyone else. * \since 12.0.0 @@ -1099,319 +748,6 @@ void ast_bridge_update_accountcodes(struct ast_bridge *bridge, struct ast_bridge */ int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame); -/*! - * \brief Write a frame to the specified bridge_channel. - * \since 12.0.0 - * - * \param bridge_channel Channel to queue the frame. - * \param fr Frame to write. - * - * \retval 0 on success. - * \retval -1 on error. - * - * \note This API call is meant for internal bridging operations. - * \note BUGBUG This may get moved. - */ -int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr); - -/*! - * \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge. - * \since 12.0.0 - * - * \param bridge_channel Which channel work with. - * \param action Type of bridge action frame. - * \param data Frame payload data to pass. - * \param datalen Frame payload data length to pass. - * - * \retval 0 on success. - * \retval -1 on error. - */ -typedef int (*ast_bridge_channel_post_action_data)(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen); - -/*! - * \brief Queue an action frame onto the bridge channel with data. - * \since 12.0.0 - * - * \param bridge_channel Which channel to queue the frame onto. - * \param action Type of bridge action frame. - * \param data Frame payload data to pass. - * \param datalen Frame payload data length to pass. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen); - -/*! - * \brief Write an action frame into the bridge with data. - * \since 12.0.0 - * - * \param bridge_channel Which channel is putting the frame into the bridge. - * \param action Type of bridge action frame. - * \param data Frame payload data to pass. - * \param datalen Frame payload data length to pass. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen); - -/*! - * \brief Queue a control frame onto the bridge channel with data. - * \since 12.0.0 - * - * \param bridge_channel Which channel to queue the frame onto. - * \param control Type of control frame. - * \param data Frame payload data to pass. - * \param datalen Frame payload data length to pass. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen); - -/*! - * \brief Write a control frame into the bridge with data. - * \since 12.0.0 - * - * \param bridge_channel Which channel is putting the frame into the bridge. - * \param control Type of control frame. - * \param data Frame payload data to pass. - * \param datalen Frame payload data length to pass. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen); - -/*! - * \brief Write a hold frame into the bridge. - * \since 12.0.0 - * - * \param bridge_channel Which channel is putting the hold into the bridge. - * \param moh_class The suggested music class for the other end to use. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class); - -/*! - * \brief Write an unhold frame into the bridge. - * \since 12.0.0 - * - * \param bridge_channel Which channel is putting the hold into the bridge. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel); - -/*! - * \brief Run an application on the bridge channel. - * \since 12.0.0 - * - * \param bridge_channel Which channel to run the application on. - * \param app_name Dialplan application name. - * \param app_args Arguments for the application. (NULL tolerant) - * \param moh_class MOH class to request bridge peers to hear while application is running. - * NULL if no MOH. - * Empty if default MOH class. - * - * \note This is intended to be called by bridge hooks. - * - * \return Nothing - */ -void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class); - -/*! - * \brief Write a bridge action run application frame into the bridge. - * \since 12.0.0 - * - * \param bridge_channel Which channel is putting the frame into the bridge - * \param app_name Dialplan application name. - * \param app_args Arguments for the application. (NULL or empty for no arguments) - * \param moh_class MOH class to request bridge peers to hear while application is running. - * NULL if no MOH. - * Empty if default MOH class. - * - * \note This is intended to be called by bridge hooks. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class); - -/*! - * \brief Queue a bridge action run application frame onto the bridge channel. - * \since 12.0.0 - * - * \param bridge_channel Which channel to put the frame onto - * \param app_name Dialplan application name. - * \param app_args Arguments for the application. (NULL or empty for no arguments) - * \param moh_class MOH class to request bridge peers to hear while application is running. - * NULL if no MOH. - * Empty if default MOH class. - * - * \note This is intended to be called by bridge hooks. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class); - -/*! - * \brief Custom interpretation of the playfile name. - * - * \param bridge_channel Which channel to play the file on - * \param playfile Sound filename to play. - * - * \return Nothing - */ -typedef void (*ast_bridge_custom_play_fn)(struct ast_bridge_channel *bridge_channel, const char *playfile); - -/*! - * \brief Play a file on the bridge channel. - * \since 12.0.0 - * - * \param bridge_channel Which channel to play the file on - * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play) - * \param playfile Sound filename to play. - * \param moh_class MOH class to request bridge peers to hear while file is played. - * NULL if no MOH. - * Empty if default MOH class. - * - * \note This is intended to be called by bridge hooks. - * - * \return Nothing - */ -void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class); - -/*! - * \brief Write a bridge action play file frame into the bridge. - * \since 12.0.0 - * - * \param bridge_channel Which channel is putting the frame into the bridge - * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play) - * \param playfile Sound filename to play. - * \param moh_class MOH class to request bridge peers to hear while file is played. - * NULL if no MOH. - * Empty if default MOH class. - * - * \note This is intended to be called by bridge hooks. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class); - -/*! - * \brief Queue a bridge action play file frame onto the bridge channel. - * \since 12.0.0 - * - * \param bridge_channel Which channel to put the frame onto. - * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play) - * \param playfile Sound filename to play. - * \param moh_class MOH class to request bridge peers to hear while file is played. - * NULL if no MOH. - * Empty if default MOH class. - * - * \note This is intended to be called by bridge hooks. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class); - -/*! - * \brief Custom callback run on a bridge channel. - * - * \param bridge_channel Which channel to operate on. - * \param payload Data to pass to the callback. (NULL if none). - * \param payload_size Size of the payload if payload is non-NULL. A number otherwise. - * - * \note The payload MUST NOT have any resources that need to be freed. - * - * \return Nothing - */ -typedef void (*ast_bridge_custom_callback_fn)(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size); - -/*! - * \brief Write a bridge action custom callback frame into the bridge. - * \since 12.0.0 - * - * \param bridge_channel Which channel is putting the frame into the bridge - * \param callback Custom callback run on a bridge channel. - * \param payload Data to pass to the callback. (NULL if none). - * \param payload_size Size of the payload if payload is non-NULL. A number otherwise. - * - * \note The payload MUST NOT have any resources that need to be freed. - * - * \note This is intended to be called by bridge hooks. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size); - -/*! - * \brief Queue a bridge action custom callback frame onto the bridge channel. - * \since 12.0.0 - * - * \param bridge_channel Which channel to put the frame onto. - * \param callback Custom callback run on a bridge channel. - * \param payload Data to pass to the callback. (NULL if none). - * \param payload_size Size of the payload if payload is non-NULL. A number otherwise. - * - * \note The payload MUST NOT have any resources that need to be freed. - * - * \note This is intended to be called by bridge hooks. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size); - -/*! - * \brief Have a bridge channel park a channel in the bridge - * \since 12.0.0 - * - * \param bridge_channel Bridge channel performing the parking - * \param parkee_uuid Unique id of the channel we want to park - * \param parker_uuid Unique id of the channel parking the call - * \param app_data string indicating data used for park application (NULL allowed) - * - * \note This is intended to be called by bridge hooks. - * - * \retval 0 on success. - * \retval -1 on error. - */ -int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, - const char *parker_uuid, const char *app_data); - -/*! - * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_join - * \since 12.0.0 - * - * \param bridge_channel Channel to restore - */ -void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel); - -/*! - * \brief Get the peer bridge channel of a two party bridge. - * \since 12.0.0 - * - * \param bridge_channel What to get the peer of. - * - * \note On entry, bridge_channel->bridge is already locked. - * - * \note This is an internal bridge function. - * - * \retval peer on success. - * \retval NULL no peer channel. - */ -struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel); - /*! * \brief Adjust the internal mixing sample rate of a bridge * used during multimix mode. @@ -1810,6 +1146,7 @@ struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channe * \return Nothing */ void ast_bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags flags); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/include/asterisk/bridging_channel.h b/include/asterisk/bridging_channel.h new file mode 100644 index 0000000000000000000000000000000000000000..dc559030c2b1f4ad674de75d6f2fbf338be5d174 --- /dev/null +++ b/include/asterisk/bridging_channel.h @@ -0,0 +1,576 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013 Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * Richard Mudgett <rmudgett@digium.com> + * Matt Jordan <mjordan@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Bridging Channel API + * + * An API that act on a channel in a bridge + * + * \author Joshua Colp <jcolp@digium.com> + * \author Richard Mudgett <rmudgett@digium.com> + * \author Matt Jordan <mjordan@digium.com> + * + * See Also: + * \ref bridging.h + * \arg \ref AstCREDITS + */ + +#ifndef _ASTERISK_BRIDGING_CHANNEL_H +#define _ASTERISK_BRIDGING_CHANNEL_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#include "asterisk/bridging_technology.h" + +/*! \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 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, +}; + +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, +}; + +/*! \brief Actions that can be taken on a channel in a bridge */ +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 channel is to run the custom callback routine. */ + AST_BRIDGE_ACTION_CALLBACK, + /*! Bridge channel is to get parked. */ + AST_BRIDGE_ACTION_PARK, + /*! Bridge channel is to execute a blind transfer. */ + AST_BRIDGE_ACTION_BLIND_TRANSFER, + /*! Bridge channel is to execute an attended transfer */ + AST_BRIDGE_ACTION_ATTENDED_TRANSFER, + + /* + * 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, +}; + +struct ast_bridge; +struct ast_bridge_tech_optimizations; + + /*! + * \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 */ + enum ast_bridge_channel_state state; + /*! Asterisk channel participating in the bridge */ + struct ast_channel *chan; + /*! Asterisk channel we are swapping with (if swapping) */ + struct ast_channel *swap; + /*! + * \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; + /*! + * \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; + /*! + * \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; + /* 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; + /*! 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; + /*! 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; +}; + +/*! + * \brief Try locking the bridge_channel. + * + * \param bridge_channel What to try locking + * + * \retval 0 on success. + * \retval non-zero on error. + */ +#define ast_bridge_channel_trylock(bridge_channel) _ast_bridge_channel_trylock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel) +static inline int _ast_bridge_channel_trylock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var) +{ + return __ao2_trylock(bridge_channel, AO2_LOCK_REQ_MUTEX, file, function, line, var); +} + +/*! + * \brief Lock the bridge_channel. + * + * \param bridge_channel What to lock + * + * \return Nothing + */ +#define ast_bridge_channel_lock(bridge_channel) _ast_bridge_channel_lock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel) +static inline void _ast_bridge_channel_lock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var) +{ + __ao2_lock(bridge_channel, AO2_LOCK_REQ_MUTEX, file, function, line, var); +} + +/*! + * \brief Unlock the bridge_channel. + * + * \param bridge_channel What to unlock + * + * \return Nothing + */ +#define ast_bridge_channel_unlock(bridge_channel) _ast_bridge_channel_unlock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel) +static inline void _ast_bridge_channel_unlock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var) +{ + __ao2_unlock(bridge_channel, file, function, line, var); +} + +/*! + * \brief Lock the bridge associated with the bridge channel. + * \since 12.0.0 + * + * \param bridge_channel Channel that wants to lock the bridge. + * + * \details + * This is an upstream lock operation. The defined locking + * order is bridge then bridge_channel. + * + * \note On entry, neither the bridge nor bridge_channel is locked. + * + * \note The bridge_channel->bridge pointer changes because of a + * bridge-merge/channel-move operation between bridges. + * + * \return Nothing + */ +void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel); + +/*! + * \brief Ask the bridged channel to leave the bridge it is currently in + * + * \param bridge_channel Channel to leave the bridge + */ +void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel); + +/*! + * \brief Ask the bridged channel to leave the bridge it is currently in + * + * \param bridge_channel Channel to leave the bridge + * + * \note This function assumes the bridge_channel is locked. + */ +void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel); + +/*! + * \brief Write a frame to the specified bridge_channel. + * \since 12.0.0 + * + * \param bridge_channel Channel to queue the frame. + * \param fr Frame to write. + * + * \retval 0 on success. + * \retval -1 on error. + * + * \note This API call is meant for internal bridging operations. + * \note BUGBUG This may get moved. + */ +int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr); + +/*! + * \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge. + * \since 12.0.0 + * + * \param bridge_channel Which channel work with. + * \param action Type of bridge action frame. + * \param data Frame payload data to pass. + * \param datalen Frame payload data length to pass. + * + * \retval 0 on success. + * \retval -1 on error. + */ +typedef int (*ast_bridge_channel_post_action_data)(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen); + +/*! + * \brief Queue an action frame onto the bridge channel with data. + * \since 12.0.0 + * + * \param bridge_channel Which channel to queue the frame onto. + * \param action Type of bridge action frame. + * \param data Frame payload data to pass. + * \param datalen Frame payload data length to pass. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen); + +/*! + * \brief Write an action frame into the bridge with data. + * \since 12.0.0 + * + * \param bridge_channel Which channel is putting the frame into the bridge. + * \param action Type of bridge action frame. + * \param data Frame payload data to pass. + * \param datalen Frame payload data length to pass. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen); + +/*! + * \brief Queue a control frame onto the bridge channel with data. + * \since 12.0.0 + * + * \param bridge_channel Which channel to queue the frame onto. + * \param control Type of control frame. + * \param data Frame payload data to pass. + * \param datalen Frame payload data length to pass. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen); + +/*! + * \brief Write a control frame into the bridge with data. + * \since 12.0.0 + * + * \param bridge_channel Which channel is putting the frame into the bridge. + * \param control Type of control frame. + * \param data Frame payload data to pass. + * \param datalen Frame payload data length to pass. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen); + +/*! + * \brief Write a hold frame into the bridge. + * \since 12.0.0 + * + * \param bridge_channel Which channel is putting the hold into the bridge. + * \param moh_class The suggested music class for the other end to use. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class); + +/*! + * \brief Write an unhold frame into the bridge. + * \since 12.0.0 + * + * \param bridge_channel Which channel is putting the hold into the bridge. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel); + +/*! + * \brief Run an application on the bridge channel. + * \since 12.0.0 + * + * \param bridge_channel Which channel to run the application on. + * \param app_name Dialplan application name. + * \param app_args Arguments for the application. (NULL tolerant) + * \param moh_class MOH class to request bridge peers to hear while application is running. + * NULL if no MOH. + * Empty if default MOH class. + * + * \note This is intended to be called by bridge hooks. + * + * \return Nothing + */ +void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class); + +/*! + * \brief Write a bridge action run application frame into the bridge. + * \since 12.0.0 + * + * \param bridge_channel Which channel is putting the frame into the bridge + * \param app_name Dialplan application name. + * \param app_args Arguments for the application. (NULL or empty for no arguments) + * \param moh_class MOH class to request bridge peers to hear while application is running. + * NULL if no MOH. + * Empty if default MOH class. + * + * \note This is intended to be called by bridge hooks. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class); + +/*! + * \brief Queue a bridge action run application frame onto the bridge channel. + * \since 12.0.0 + * + * \param bridge_channel Which channel to put the frame onto + * \param app_name Dialplan application name. + * \param app_args Arguments for the application. (NULL or empty for no arguments) + * \param moh_class MOH class to request bridge peers to hear while application is running. + * NULL if no MOH. + * Empty if default MOH class. + * + * \note This is intended to be called by bridge hooks. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class); + +/*! + * \brief Custom interpretation of the playfile name. + * + * \param bridge_channel Which channel to play the file on + * \param playfile Sound filename to play. + * + * \return Nothing + */ +typedef void (*ast_bridge_custom_play_fn)(struct ast_bridge_channel *bridge_channel, const char *playfile); + +/*! + * \brief Play a file on the bridge channel. + * \since 12.0.0 + * + * \param bridge_channel Which channel to play the file on + * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play) + * \param playfile Sound filename to play. + * \param moh_class MOH class to request bridge peers to hear while file is played. + * NULL if no MOH. + * Empty if default MOH class. + * + * \note This is intended to be called by bridge hooks. + * + * \return Nothing + */ +void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class); + +/*! + * \brief Write a bridge action play file frame into the bridge. + * \since 12.0.0 + * + * \param bridge_channel Which channel is putting the frame into the bridge + * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play) + * \param playfile Sound filename to play. + * \param moh_class MOH class to request bridge peers to hear while file is played. + * NULL if no MOH. + * Empty if default MOH class. + * + * \note This is intended to be called by bridge hooks. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class); + +/*! + * \brief Queue a bridge action play file frame onto the bridge channel. + * \since 12.0.0 + * + * \param bridge_channel Which channel to put the frame onto. + * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play) + * \param playfile Sound filename to play. + * \param moh_class MOH class to request bridge peers to hear while file is played. + * NULL if no MOH. + * Empty if default MOH class. + * + * \note This is intended to be called by bridge hooks. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class); + +/*! + * \brief Custom callback run on a bridge channel. + * + * \param bridge_channel Which channel to operate on. + * \param payload Data to pass to the callback. (NULL if none). + * \param payload_size Size of the payload if payload is non-NULL. A number otherwise. + * + * \note The payload MUST NOT have any resources that need to be freed. + * + * \return Nothing + */ +typedef void (*ast_bridge_custom_callback_fn)(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size); + +/*! + * \brief Write a bridge action custom callback frame into the bridge. + * \since 12.0.0 + * + * \param bridge_channel Which channel is putting the frame into the bridge + * \param callback Custom callback run on a bridge channel. + * \param payload Data to pass to the callback. (NULL if none). + * \param payload_size Size of the payload if payload is non-NULL. A number otherwise. + * + * \note The payload MUST NOT have any resources that need to be freed. + * + * \note This is intended to be called by bridge hooks. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size); + +/*! + * \brief Queue a bridge action custom callback frame onto the bridge channel. + * \since 12.0.0 + * + * \param bridge_channel Which channel to put the frame onto. + * \param callback Custom callback run on a bridge channel. + * \param payload Data to pass to the callback. (NULL if none). + * \param payload_size Size of the payload if payload is non-NULL. A number otherwise. + * + * \note The payload MUST NOT have any resources that need to be freed. + * + * \note This is intended to be called by bridge hooks. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size); + +/*! + * \brief Have a bridge channel park a channel in the bridge + * \since 12.0.0 + * + * \param bridge_channel Bridge channel performing the parking + * \param parkee_uuid Unique id of the channel we want to park + * \param parker_uuid Unique id of the channel parking the call + * \param app_data string indicating data used for park application (NULL allowed) + * + * \note This is intended to be called by bridge hooks. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, + const char *parker_uuid, const char *app_data); + +/*! + * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_join + * \since 12.0.0 + * + * \param bridge_channel Channel to restore + */ +void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel); + +/*! + * \brief Get the peer bridge channel of a two party bridge. + * \since 12.0.0 + * + * \param bridge_channel What to get the peer of. + * + * \note On entry, bridge_channel->bridge is already locked. + * + * \note This is an internal bridge function. + * + * \retval peer on success. + * \retval NULL no peer channel. + */ +struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* _ASTERISK_BRIDGING_CHANNEL_H */ \ No newline at end of file diff --git a/include/asterisk/bridging_channel_internal.h b/include/asterisk/bridging_channel_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..8f5eb4dd8f78b7042369080508e95a772fbcec7f --- /dev/null +++ b/include/asterisk/bridging_channel_internal.h @@ -0,0 +1,156 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013 Digium, Inc. + * + * Matt Jordan <mjordan@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Private Bridging Channel API + * + * \author Matt Jordan <mjordan@digium.com> + * + * A private API to manipulate channels in a bridge. These can be called on a channel in + * a bridge by the bridging API, but should not be called by external consumers of the + * Bridging API. + * + * See Also: + * \arg \ref AstCREDITS + */ + +#ifndef _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H +#define _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H + +struct blind_transfer_data { + char exten[AST_MAX_EXTENSION]; + char context[AST_MAX_CONTEXT]; +}; + +/*! + * \brief Adjust the bridge_channel's bridge merge inhibit request count. + * \since 12.0.0 + * + * \param bridge_channel What to operate on. + * \param request Inhibit request increment. + * (Positive to add requests. Negative to remove requests.) + * + * \note This API call is meant for internal bridging operations. + * + * \retval bridge adjusted merge inhibit with reference count. + */ +struct ast_bridge *bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request); + +/*! + * \internal + * \brief Pull the bridge channel out of its current bridge. + * \since 12.0.0 + * + * \param bridge_channel Channel to pull. + * + * \note On entry, bridge_channel->bridge is already locked. + * + * \return Nothing + */ +void bridge_channel_pull(struct ast_bridge_channel *bridge_channel); + +/*! + * \internal + * \brief Push the bridge channel into its specified bridge. + * \since 12.0.0 + * + * \param bridge_channel Channel to push. + * + * \note On entry, bridge_channel->bridge is already locked. + * + * \retval 0 on success. + * \retval -1 on failure. The channel did not get pushed. + */ +int bridge_channel_push(struct ast_bridge_channel *bridge_channel); + +void bridge_channel_join(struct ast_bridge_channel *bridge_channel); + +void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel); + +void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel); + +/*! + * \internal + * \brief Update the linkedids for all channels in a bridge + * \since 12.0.0 + * + * \param bridge_channel The channel joining the bridge + * \param swap The channel being swapped out of the bridge. May be NULL. + * + * \note The bridge must be locked prior to calling this function. This should be called + * during a \ref bridge_channel_push operation, typically by a sub-class of a bridge + */ +void bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap); + +/*! + * \internal + * \brief Update the accountcodes for a channel entering a bridge + * \since 12.0.0 + * + * This function updates the accountcode and peeraccount on channels in two-party + * bridges. In multi-party bridges, peeraccount is not set - it doesn't make much sense - + * however accountcode propagation will still occur if the channel joining has an + * accountcode. + * + * \param bridge_channel The channel joining the bridge + * \param swap The channel being swapped out of the bridge. May be NULL. + * + * \note The bridge must be locked prior to calling this function. This should be called + * during a \ref bridge_channel_push operation, typically by a sub-class of a bridge + */ +void bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap); + + +/*! + * \brief Set bridge channel state to leave bridge (if not leaving already) with no lock. + * + * \param bridge_channel Channel to change the state on + * \param new_state The new state to place the channel into + * + * \note This API call is only meant to be used within the + * bridging module and hook callbacks to request the channel + * exit the bridge. + * + * \note This function assumes the bridge_channel is locked. + */ +void ast_bridge_change_state_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state); + +/*! + * \brief Set bridge channel state to leave bridge (if not leaving already). + * + * \param bridge_channel Channel to change the state on + * \param new_state The new state to place the channel into + * + * Example usage: + * + * \code + * ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); + * \endcode + * + * This places the channel pointed to by bridge_channel into the + * state AST_BRIDGE_CHANNEL_STATE_HANGUP if it was + * AST_BRIDGE_CHANNEL_STATE_WAIT before. + * + * \note This API call is only meant to be used within the + * bridging module and hook callbacks to request the channel + * exit the bridge. + */ +void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state); + +#endif /* _ASTERISK_PRIVATE_BRIDGING_H */ \ No newline at end of file diff --git a/include/asterisk/bridging_internal.h b/include/asterisk/bridging_internal.h index 132c42b76a9df67717347a1333619ab214a93302..cafa693f594b3615b819aae70f0b982c4f340059 100644 --- a/include/asterisk/bridging_internal.h +++ b/include/asterisk/bridging_internal.h @@ -20,6 +20,11 @@ * \file * \brief Private Bridging API * + * Functions in this file are intended to be used by the Bridging API, + * bridge mixing technologies, and bridge sub-classes. Users of bridges + * that do not fit those three categories should *not* use the API + * defined in this file. + * * \author Mark Michelson <mmichelson@digium.com> * * See Also: @@ -32,6 +37,72 @@ struct ast_bridge; struct ast_bridge_channel; +/*! + * \brief Register the new bridge with the system. + * \since 12.0.0 + * + * \param bridge What to register. (Tolerates a NULL pointer) + * + * \code + * struct ast_bridge *ast_bridge_basic_new(uint32_t capabilities, int flags, uint32 dtmf_features) + * { + * void *bridge; + * + * bridge = bridge_alloc(sizeof(struct ast_bridge_basic), &ast_bridge_basic_v_table); + * bridge = bridge_base_init(bridge, capabilities, flags); + * bridge = ast_bridge_basic_init(bridge, dtmf_features); + * bridge = bridge_register(bridge); + * return bridge; + * } + * \endcode + * + * \note This must be done after a bridge constructor has + * completed setting up the new bridge but before it returns. + * + * \note After a bridge is registered, ast_bridge_destroy() must + * eventually be called to get rid of the bridge. + * + * \retval bridge on success. + * \retval NULL on error. + */ +struct ast_bridge *bridge_register(struct ast_bridge *bridge); + +/*! + * \internal + * \brief Allocate the bridge class object memory. + * \since 12.0.0 + * + * \param size Size of the bridge class structure to allocate. + * \param v_table Bridge class virtual method table. + * + * \retval bridge on success. + * \retval NULL on error. + */ +struct ast_bridge *bridge_alloc(size_t size, const struct ast_bridge_methods *v_table); + +/*! + * \brief Initialize the base class of the bridge. + * + * \param self Bridge to operate upon. (Tolerates a NULL pointer) + * \param capabilities The capabilities that we require to be used on the bridge + * \param flags Flags that will alter the behavior of the bridge + * + * \retval self on success + * \retval NULL on failure, self is already destroyed + * + * Example usage: + * + * \code + * struct ast_bridge *bridge; + * bridge = bridge_alloc(sizeof(*bridge), &ast_bridge_base_v_table); + * bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE_HANGUP); + * \endcode + * + * This creates a no frills two party bridge that will be + * destroyed once one of the channels hangs up. + */ +struct ast_bridge *bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags); + /*! * \internal * \brief Move a bridge channel from one bridge to another. @@ -47,7 +118,7 @@ struct ast_bridge_channel; * \retval 0 on success. * \retval -1 on failure. */ -int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, +int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery, unsigned int optimized); /*! @@ -68,7 +139,7 @@ int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri * This moves the channels in src_bridge into the bridge pointed * to by dst_bridge. */ -void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, +void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick, unsigned int optimized); /*! @@ -83,6 +154,58 @@ void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg * \retval bridge_channel if channel is in the bridge. * \retval NULL if not in bridge. */ -struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan); +struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan); + +/*! + * \internal + * \brief Adjust the bridge merge inhibit request count. + * \since 12.0.0 + * + * \param bridge What to operate on. + * \param request Inhibit request increment. + * (Positive to add requests. Negative to remove requests.) + * + * \note This function assumes bridge is locked. + * + * \return Nothing + */ +void bridge_merge_inhibit_nolock(struct ast_bridge *bridge, int request); + +/*! + * \internal + * \brief Notify the bridge that it has been reconfigured. + * \since 12.0.0 + * + * \param bridge Reconfigured bridge. + * \param colp_update Whether to perform COLP updates. + * + * \details + * After a series of bridge_channel_push and + * bridge_channel_pull calls, you need to call this function + * to cause the bridge to complete restructuring for the change + * in the channel makeup of the bridge. + * + * \note On entry, the bridge is already locked. + * + * \return Nothing + */ +void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update); + +/*! + * \internal + * \brief Dissolve the bridge. + * \since 12.0.0 + * + * \param bridge Bridge to eject all channels + * + * \details + * Force out all channels that are not already going out of the + * bridge. Any new channels joining will leave immediately. + * + * \note On entry, bridge is already locked. + * + * \return Nothing + */ +void bridge_dissolve(struct ast_bridge *bridge); #endif /* _ASTERISK_PRIVATE_BRIDGING_H */ diff --git a/include/asterisk/bridging_technology.h b/include/asterisk/bridging_technology.h index 4e680679e40e6d2ec5f1b5a8bd3821a32966acd6..e037b7490ed0594cc3a78e1040345619d444e6b7 100644 --- a/include/asterisk/bridging_technology.h +++ b/include/asterisk/bridging_technology.h @@ -41,6 +41,22 @@ enum ast_bridge_preference { AST_BRIDGE_PREFERENCE_BASE_MULTIMIX = 10, }; +/*! + * \brief Structure specific to bridge technologies capable of + * performing talking optimizations. + */ +struct ast_bridge_tech_optimizations { + /*! The amount of time in ms that talking must be detected before + * the dsp determines that talking has occurred */ + unsigned int talking_threshold; + /*! The amount of time in ms that silence must be detected before + * the dsp determines that talking has stopped */ + unsigned int silence_threshold; + /*! Whether or not the bridging technology should drop audio + * detected as silence from the mix. */ + unsigned int drop_silence:1; +}; + /*! * \brief Structure that is the essence of a bridge technology */ diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index d8db4705d073080fa5324a6843d4dab25476f8f2..f6cdd4e628392713334e5cf8b67b76a5cc5fea97 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -4385,4 +4385,16 @@ int ast_channel_suppress(struct ast_channel *chan, unsigned int direction, enum */ int ast_channel_unsuppress(struct ast_channel *chan, unsigned int direction, enum ast_frame_type frametype); +/*! + * \brief Simulate a DTMF end on a broken bridge channel. + * + * \param chan Channel sending DTMF that has not ended. + * \param digit DTMF digit to stop. + * \param start DTMF digit start time. + * \param why Reason bridge broken. + * + * \return Nothing + */ +void ast_channel_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why); + #endif /* _ASTERISK_CHANNEL_H */ diff --git a/include/asterisk/features.h b/include/asterisk/features.h index 162a7fc372dbed71cc513a6329ef5a88131da15d..96eec98c86a60e1727046502dd63e306ca048bb0 100644 --- a/include/asterisk/features.h +++ b/include/asterisk/features.h @@ -146,25 +146,13 @@ int ast_masq_park_call(struct ast_channel *park_me, struct ast_channel *parker, */ int ast_masq_park_call_exten(struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout); -/*! +/*! * \brief Determine if parking extension exists in a given context * \retval 0 if extension does not exist * \retval 1 if extension does exist */ int ast_parking_ext_valid(const char *exten_str, struct ast_channel *chan, const char *context); -/*! - * \brief Simulate a DTMF end on a broken bridge channel. - * - * \param chan Channel sending DTMF that has not ended. - * \param digit DTMF digit to stop. - * \param start DTMF digit start time. - * \param why Reason bridge broken. - * - * \return Nothing - */ -void ast_bridge_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why); - /*! \brief Bridge a call, optionally allowing redirection */ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config); diff --git a/main/bridging.c b/main/bridging.c index fe38d4cac662abfc6fa2683d54573430ee5c56e1..5f001d1f5ebadd85e5eac9e643a52dd4fa20f714 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -18,7 +18,7 @@ /*! \file * - * \brief Channel Bridging API + * \brief Bridging API * * \author Joshua Colp <jcolp@digium.com> */ @@ -31,8 +31,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") -#include <signal.h> - #include "asterisk/logger.h" #include "asterisk/channel.h" #include "asterisk/options.h" @@ -42,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/bridging.h" #include "asterisk/bridging_basic.h" #include "asterisk/bridging_technology.h" +#include "asterisk/bridging_channel.h" #include "asterisk/stasis_bridging.h" #include "asterisk/stasis_channels.h" #include "asterisk/app.h" @@ -62,8 +61,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/parking.h" #include "asterisk/core_local.h" #include "asterisk/core_unreal.h" -#include "asterisk/features_config.h" #include "asterisk/bridging_internal.h" +#include "asterisk/bridging_channel_internal.h" /*! All bridges container. */ static struct ao2_container *bridges; @@ -204,62 +203,6 @@ int ast_bridge_technology_unregister(struct ast_bridge_technology *technology) return current ? 0 : -1; } -void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel) -{ - struct ast_bridge *bridge; - - for (;;) { - /* Safely get the bridge pointer */ - ast_bridge_channel_lock(bridge_channel); - bridge = bridge_channel->bridge; - ao2_ref(bridge, +1); - ast_bridge_channel_unlock(bridge_channel); - - /* Lock the bridge and see if it is still the bridge we need to lock. */ - ast_bridge_lock(bridge); - if (bridge == bridge_channel->bridge) { - ao2_ref(bridge, -1); - return; - } - ast_bridge_unlock(bridge); - ao2_ref(bridge, -1); - } -} - -static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel) -{ - if (!pthread_equal(pthread_self(), bridge_channel->thread)) { - while (bridge_channel->waiting) { - pthread_kill(bridge_channel->thread, SIGURG); - sched_yield(); - } - } -} - -void ast_bridge_change_state_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state) -{ -/* BUGBUG need cause code for the bridge_channel leaving the bridge. */ - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { - return; - } - - ast_debug(1, "Setting %p(%s) state from:%d to:%d\n", - bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state, - new_state); - - /* Change the state on the bridge channel */ - bridge_channel->state = new_state; - - bridge_channel_poke(bridge_channel); -} - -void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state) -{ - ast_bridge_channel_lock(bridge_channel); - ast_bridge_change_state_nolock(bridge_channel, new_state); - ast_bridge_channel_unlock(bridge_channel); -} - /*! * \internal * \brief Put an action onto the specified bridge. Don't dup the action frame. @@ -293,198 +236,6 @@ int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action) return 0; } -void ast_bridge_update_accountcodes(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) -{ - struct ast_bridge_channel *other = NULL; - - AST_LIST_TRAVERSE(&bridge->channels, other, entry) { - if (other == swap) { - continue; - } - - if (!ast_strlen_zero(ast_channel_accountcode(bridge_channel->chan)) && ast_strlen_zero(ast_channel_peeraccount(other->chan))) { - ast_debug(1, "Setting peeraccount to %s for %s from data on channel %s\n", - ast_channel_accountcode(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan)); - ast_channel_peeraccount_set(other->chan, ast_channel_accountcode(bridge_channel->chan)); - } - if (!ast_strlen_zero(ast_channel_accountcode(other->chan)) && ast_strlen_zero(ast_channel_peeraccount(bridge_channel->chan))) { - ast_debug(1, "Setting peeraccount to %s for %s from data on channel %s\n", - ast_channel_accountcode(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan)); - ast_channel_peeraccount_set(bridge_channel->chan, ast_channel_accountcode(other->chan)); - } - if (!ast_strlen_zero(ast_channel_peeraccount(bridge_channel->chan)) && ast_strlen_zero(ast_channel_accountcode(other->chan))) { - ast_debug(1, "Setting accountcode to %s for %s from data on channel %s\n", - ast_channel_peeraccount(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan)); - ast_channel_accountcode_set(other->chan, ast_channel_peeraccount(bridge_channel->chan)); - } - if (!ast_strlen_zero(ast_channel_peeraccount(other->chan)) && ast_strlen_zero(ast_channel_accountcode(bridge_channel->chan))) { - ast_debug(1, "Setting accountcode to %s for %s from data on channel %s\n", - ast_channel_peeraccount(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan)); - ast_channel_accountcode_set(bridge_channel->chan, ast_channel_peeraccount(other->chan)); - } - if (bridge->num_channels == 2) { - if (strcmp(ast_channel_accountcode(bridge_channel->chan), ast_channel_peeraccount(other->chan))) { - ast_debug(1, "Changing peeraccount from %s to %s on %s to match channel %s\n", - ast_channel_peeraccount(other->chan), ast_channel_peeraccount(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan)); - ast_channel_peeraccount_set(other->chan, ast_channel_accountcode(bridge_channel->chan)); - } - if (strcmp(ast_channel_accountcode(other->chan), ast_channel_peeraccount(bridge_channel->chan))) { - ast_debug(1, "Changing peeraccount from %s to %s on %s to match channel %s\n", - ast_channel_peeraccount(bridge_channel->chan), ast_channel_peeraccount(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan)); - ast_channel_peeraccount_set(bridge_channel->chan, ast_channel_accountcode(other->chan)); - } - } - } -} - -void ast_bridge_update_linkedids(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) -{ - struct ast_bridge_channel *other = NULL; - const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan); - - AST_LIST_TRAVERSE(&bridge->channels, other, entry) { - if (other == swap) { - continue; - } - oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan)); - } - - if (ast_strlen_zero(oldest_linkedid)) { - return; - } - - ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid); - AST_LIST_TRAVERSE(&bridge->channels, other, entry) { - if (other == swap) { - continue; - } - ast_channel_linkedid_set(other->chan, oldest_linkedid); - } -} - -int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr) -{ - struct ast_frame *dup; - char nudge = 0; - - if (bridge_channel->suspended - /* Also defer DTMF frames. */ - && fr->frametype != AST_FRAME_DTMF_BEGIN - && fr->frametype != AST_FRAME_DTMF_END - && !ast_is_deferrable_frame(fr)) { - /* Drop non-deferable frames when suspended. */ - return 0; - } - if (fr->frametype == AST_FRAME_NULL) { - /* "Accept" the frame and discard it. */ - return 0; - } - - dup = ast_frdup(fr); - if (!dup) { - return -1; - } - - ast_bridge_channel_lock(bridge_channel); - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { - /* Drop frames on channels leaving the bridge. */ - ast_bridge_channel_unlock(bridge_channel); - ast_frfree(dup); - return 0; - } - - AST_LIST_INSERT_TAIL(&bridge_channel->wr_queue, dup, frame_list); - if (write(bridge_channel->alert_pipe[1], &nudge, sizeof(nudge)) != sizeof(nudge)) { - ast_log(LOG_ERROR, "We couldn't write alert pipe for %p(%s)... something is VERY wrong\n", - bridge_channel, ast_channel_name(bridge_channel->chan)); - } - ast_bridge_channel_unlock(bridge_channel); - return 0; -} - -int ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen) -{ - struct ast_frame frame = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = action, - .datalen = datalen, - .data.ptr = (void *) data, - }; - - return ast_bridge_channel_queue_frame(bridge_channel, &frame); -} - -int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen) -{ - struct ast_frame frame = { - .frametype = AST_FRAME_CONTROL, - .subclass.integer = control, - .datalen = datalen, - .data.ptr = (void *) data, - }; - - return ast_bridge_channel_queue_frame(bridge_channel, &frame); -} - -int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) -{ - struct ast_bridge_channel *cur; - int not_written = -1; - - if (frame->frametype == AST_FRAME_NULL) { - /* "Accept" the frame and discard it. */ - return 0; - } - - AST_LIST_TRAVERSE(&bridge->channels, cur, entry) { - if (cur == bridge_channel) { - continue; - } - if (!ast_bridge_channel_queue_frame(cur, frame)) { - not_written = 0; - } - } - return not_written; -} - -void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel) -{ - /* Restore original formats of the channel as they came in */ - if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) { - ast_debug(1, "Bridge is returning %p(%s) to read format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->read_format)); - if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) { - ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->read_format)); - } - } - if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) { - ast_debug(1, "Bridge is returning %p(%s) to write format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->write_format)); - if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) { - ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->write_format)); - } - } -} - -struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan) -{ - struct ast_bridge_channel *bridge_channel; - - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - if (bridge_channel->chan == chan) { - break; - } - } - - return bridge_channel; -} - /*! * \internal * \brief Dissolve the bridge. @@ -500,7 +251,7 @@ struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct * * \return Nothing */ -static void bridge_dissolve(struct ast_bridge *bridge) +void bridge_dissolve(struct ast_bridge *bridge) { struct ast_bridge_channel *bridge_channel; struct ast_frame action = { @@ -524,49 +275,6 @@ static void bridge_dissolve(struct ast_bridge *bridge) ast_bridge_queue_action(bridge, &action); } -/*! - * \internal - * \brief Check if a bridge should dissolve and do it. - * \since 12.0.0 - * - * \param bridge_channel Channel causing the check. - * - * \note On entry, bridge_channel->bridge is already locked. - * - * \return Nothing - */ -static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel) -{ - struct ast_bridge *bridge = bridge_channel->bridge; - - if (bridge->dissolved) { - return; - } - - if (!bridge->num_channels - && ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY)) { - /* Last channel leaving the bridge turns off the lights. */ - bridge_dissolve(bridge); - return; - } - - switch (bridge_channel->state) { - case AST_BRIDGE_CHANNEL_STATE_END: - /* Do we need to dissolve the bridge because this channel hung up? */ - if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP) - || (bridge_channel->features->usable - && ast_test_flag(&bridge_channel->features->feature_flags, - AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP))) { - bridge_dissolve(bridge); - return; - } - break; - default: - break; - } -/* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */ -} - /*! * \internal * \brief Check if a bridge should dissolve because of a stolen channel and do it. @@ -651,2487 +359,1032 @@ static void bridge_reconfigured_connected_line_update(struct ast_bridge *bridge) /*! * \internal - * \brief Pull the bridge channel out of its current bridge. + * \brief Complete joining a channel to the bridge. * \since 12.0.0 * - * \param bridge_channel Channel to pull. + * \param bridge What to operate upon. + * \param bridge_channel What is joining the bridge technology. * - * \note On entry, bridge_channel->bridge is already locked. + * \note On entry, bridge is already locked. * * \return Nothing */ -static void bridge_channel_pull(struct ast_bridge_channel *bridge_channel) +static void bridge_channel_complete_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { - struct ast_bridge *bridge = bridge_channel->bridge; - - if (!bridge_channel->in_bridge) { - return; - } - bridge_channel->in_bridge = 0; - - ast_debug(1, "Bridge %s: pulling %p(%s)\n", - bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan)); - - ast_verb(3, "Channel %s left '%s' %s-bridge <%s>\n", - ast_channel_name(bridge_channel->chan), - bridge->technology->name, - bridge->v_table->name, - bridge->uniqueid); + /* Make the channel compatible with the bridge */ + bridge_make_compatible(bridge, bridge_channel); -/* BUGBUG This is where incoming HOLD/UNHOLD memory should write UNHOLD into bridge. (if not local optimizing) */ -/* BUGBUG This is where incoming DTMF begin/end memory should write DTMF end into bridge. (if not local optimizing) */ - if (!bridge_channel->just_joined) { - /* Tell the bridge technology we are leaving so they tear us down */ - ast_debug(1, "Bridge %s: %p(%s) is leaving %s technology\n", + /* Tell the bridge technology we are joining so they set us up */ + ast_debug(1, "Bridge %s: %p(%s) is joining %s technology\n", + bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan), + bridge->technology->name); + if (bridge->technology->join + && bridge->technology->join(bridge, bridge_channel)) { + ast_debug(1, "Bridge %s: %p(%s) failed to join %s technology\n", bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan), bridge->technology->name); - if (bridge->technology->leave) { - bridge->technology->leave(bridge, bridge_channel); - } - } - - /* Remove channel from the bridge */ - if (!bridge_channel->suspended) { - --bridge->num_active; - } - --bridge->num_channels; - AST_LIST_REMOVE(&bridge->channels, bridge_channel, entry); - bridge->v_table->pull(bridge, bridge_channel); - - ast_bridge_channel_clear_roles(bridge_channel); - - /* If we are not going to be hung up after leaving a bridge, and we were an - * outgoing channel, clear the outgoing flag. - */ - if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING) - && (ast_channel_softhangup_internal_flag(bridge_channel->chan) & - (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE))) { - ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING); + bridge_channel->just_joined = 1; + return; } - bridge_dissolve_check(bridge_channel); - - bridge->reconfigured = 1; - ast_bridge_publish_leave(bridge, bridge_channel->chan); + bridge_channel->just_joined = 0; } /*! * \internal - * \brief Push the bridge channel into its specified bridge. + * \brief Complete joining new channels to the bridge. * \since 12.0.0 * - * \param bridge_channel Channel to push. + * \param bridge Check for new channels on this bridge. * - * \note On entry, bridge_channel->bridge is already locked. + * \note On entry, bridge is already locked. * - * \retval 0 on success. - * \retval -1 on failure. The channel did not get pushed. + * \return Nothing */ -static int bridge_channel_push(struct ast_bridge_channel *bridge_channel) +static void bridge_complete_join(struct ast_bridge *bridge) { - struct ast_bridge *bridge = bridge_channel->bridge; - struct ast_bridge_channel *swap; - - ast_assert(!bridge_channel->in_bridge); - - swap = find_bridge_channel(bridge, bridge_channel->swap); - bridge_channel->swap = NULL; + struct ast_bridge_channel *bridge_channel; - if (swap) { - ast_debug(1, "Bridge %s: pushing %p(%s) by swapping with %p(%s)\n", - bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan), - swap, ast_channel_name(swap->chan)); - } else { - ast_debug(1, "Bridge %s: pushing %p(%s)\n", - bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan)); + if (bridge->dissolved) { + /* + * No sense in completing the join on channels for a dissolved + * bridge. They are just going to be removed soon anyway. + * However, we do have reason to abort here because the bridge + * technology may not be able to handle the number of channels + * still in the bridge. + */ + return; } - /* Add channel to the bridge */ - if (bridge->dissolved - || bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT - || (swap && swap->state != AST_BRIDGE_CHANNEL_STATE_WAIT) - || bridge->v_table->push(bridge, bridge_channel, swap) - || ast_bridge_channel_establish_roles(bridge_channel)) { - ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n", - bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan)); - ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL); - return -1; - } - bridge_channel->in_bridge = 1; - bridge_channel->just_joined = 1; - AST_LIST_INSERT_TAIL(&bridge->channels, bridge_channel, entry); - ++bridge->num_channels; - if (!bridge_channel->suspended) { - ++bridge->num_active; + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + if (!bridge_channel->just_joined) { + continue; + } + bridge_channel_complete_join(bridge, bridge_channel); } +} - ast_verb(3, "Channel %s %s%s%s '%s' %s-bridge <%s>\n", - ast_channel_name(bridge_channel->chan), - swap ? "swapped with " : "joined", - swap ? ast_channel_name(swap->chan) : "", - swap ? " into" : "", - bridge->technology->name, - bridge->v_table->name, - bridge->uniqueid); +/*! \brief Helper function used to find the "best" bridge technology given specified capabilities */ +static struct ast_bridge_technology *find_best_technology(uint32_t capabilities, struct ast_bridge *bridge) +{ + struct ast_bridge_technology *current; + struct ast_bridge_technology *best = NULL; - ast_bridge_publish_enter(bridge, bridge_channel->chan); - if (swap) { - ast_bridge_change_state(swap, AST_BRIDGE_CHANNEL_STATE_HANGUP); - bridge_channel_pull(swap); + AST_RWLIST_RDLOCK(&bridge_technologies); + AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) { + if (current->suspended) { + ast_debug(1, "Bridge technology %s is suspended. Skipping.\n", + current->name); + continue; + } + if (!(current->capabilities & capabilities)) { + ast_debug(1, "Bridge technology %s does not have any capabilities we want.\n", + current->name); + continue; + } + if (best && current->preference <= best->preference) { + ast_debug(1, "Bridge technology %s has less preference than %s (%d <= %d). Skipping.\n", + current->name, best->name, current->preference, best->preference); + continue; + } + if (current->compatible && !current->compatible(bridge)) { + ast_debug(1, "Bridge technology %s is not compatible with properties of existing bridge.\n", + current->name); + continue; + } + best = current; } - /* Clear any BLINDTRANSFER and ATTENDEDTRANSFER since the transfer has completed. */ - pbx_builtin_setvar_helper(bridge_channel->chan, "BLINDTRANSFER", NULL); - pbx_builtin_setvar_helper(bridge_channel->chan, "ATTENDEDTRANSFER", NULL); + if (best) { + /* Increment it's module reference count if present so it does not get unloaded while in use */ + ast_module_ref(best->mod); + ast_debug(1, "Chose bridge technology %s\n", best->name); + } - bridge->reconfigured = 1; - return 0; + AST_RWLIST_UNLOCK(&bridge_technologies); + + return best; } -/*! \brief Internal function to handle DTMF from a channel */ -static struct ast_frame *bridge_handle_dtmf(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) -{ - struct ast_bridge_features *features = bridge_channel->features; - struct ast_bridge_hook *hook; - char dtmf[2]; - -/* BUGBUG the feature hook matching needs to be done here. Any matching feature hook needs to be queued onto the bridge_channel. Also the feature hook digit timeout needs to be handled. */ -/* BUGBUG the AMI atxfer action just sends DTMF end events to initiate DTMF atxfer and dial the extension. Another reason the DTMF hook matching needs rework. */ - /* See if this DTMF matches the beginnings of any feature hooks, if so we switch to the feature state to either execute the feature or collect more DTMF */ - dtmf[0] = frame->subclass.integer; - dtmf[1] = '\0'; - hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY); - if (hook) { - struct ast_frame action = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = AST_BRIDGE_ACTION_FEATURE, - }; +struct tech_deferred_destroy { + struct ast_bridge_technology *tech; + void *tech_pvt; +}; - ast_frfree(frame); - frame = NULL; - ast_bridge_channel_queue_frame(bridge_channel, &action); - ao2_ref(hook, -1); - } +/*! + * \internal + * \brief Deferred destruction of bridge tech private structure. + * \since 12.0.0 + * + * \param bridge What to execute the action on. + * \param action Deferred bridge tech destruction. + * + * \note On entry, bridge must not be locked. + * + * \return Nothing + */ +static void bridge_tech_deferred_destroy(struct ast_bridge *bridge, struct ast_frame *action) +{ + struct tech_deferred_destroy *deferred = action->data.ptr; + struct ast_bridge dummy_bridge = { + .technology = deferred->tech, + .tech_pvt = deferred->tech_pvt, + }; - return frame; + ast_copy_string(dummy_bridge.uniqueid, bridge->uniqueid, sizeof(dummy_bridge.uniqueid)); + ast_debug(1, "Bridge %s: calling %s technology destructor (deferred, dummy)\n", + dummy_bridge.uniqueid, dummy_bridge.technology->name); + dummy_bridge.technology->destroy(&dummy_bridge); + ast_module_unref(dummy_bridge.technology->mod); } /*! * \internal - * \brief Handle bridge hangup event. + * \brief Handle bridge action frame. * \since 12.0.0 * - * \param bridge_channel Which channel is hanging up. + * \param bridge What to execute the action on. + * \param action What to do. + * + * \note On entry, bridge is already locked. + * \note Can be called by the bridge destructor. * * \return Nothing */ -static void bridge_handle_hangup(struct ast_bridge_channel *bridge_channel) +static void bridge_action_bridge(struct ast_bridge *bridge, struct ast_frame *action) { - struct ast_bridge_features *features = bridge_channel->features; - struct ast_bridge_hook *hook; - struct ao2_iterator iter; - - /* Run any hangup hooks. */ - iter = ao2_iterator_init(features->hangup_hooks, 0); - for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) { - int remove_me; +#if 0 /* In case we need to know when the destructor is calling us. */ + int in_destructor = !ao2_ref(bridge, 0); +#endif - remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); - if (remove_me) { - ast_debug(1, "Hangup hook %p is being removed from %p(%s)\n", - hook, bridge_channel, ast_channel_name(bridge_channel->chan)); - ao2_unlink(features->hangup_hooks, hook); - } + switch (action->subclass.integer) { + case AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY: + ast_bridge_unlock(bridge); + bridge_tech_deferred_destroy(bridge, action); + ast_bridge_lock(bridge); + break; + case AST_BRIDGE_ACTION_DEFERRED_DISSOLVING: + ast_bridge_unlock(bridge); + bridge->v_table->dissolving(bridge); + ast_bridge_lock(bridge); + break; + default: + /* Unexpected deferred action type. Should never happen. */ + ast_assert(0); + break; } - ao2_iterator_destroy(&iter); - - /* Default hangup action. */ - ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); } -static int bridge_channel_interval_ready(struct ast_bridge_channel *bridge_channel) +/*! + * \internal + * \brief Do any pending bridge actions. + * \since 12.0.0 + * + * \param bridge What to do actions on. + * + * \note On entry, bridge is already locked. + * \note Can be called by the bridge destructor. + * + * \return Nothing + */ +static void bridge_handle_actions(struct ast_bridge *bridge) { - struct ast_bridge_features *features = bridge_channel->features; - struct ast_bridge_hook *hook; - int ready; - - ast_heap_wrlock(features->interval_hooks); - hook = ast_heap_peek(features->interval_hooks, 1); - ready = hook && ast_tvdiff_ms(hook->parms.timer.trip_time, ast_tvnow()) <= 0; - ast_heap_unlock(features->interval_hooks); + struct ast_frame *action; - return ready; + while ((action = AST_LIST_REMOVE_HEAD(&bridge->action_queue, frame_list))) { + switch (action->frametype) { + case AST_FRAME_BRIDGE_ACTION: + bridge_action_bridge(bridge, action); + break; + default: + /* Unexpected deferred frame type. Should never happen. */ + ast_assert(0); + break; + } + ast_frfree(action); + } } -int ast_bridge_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking) +static struct stasis_message *create_bridge_snapshot_message(struct ast_bridge *bridge) { - struct ast_frame action = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = started_talking - ? AST_BRIDGE_ACTION_TALKING_START : AST_BRIDGE_ACTION_TALKING_STOP, - }; + RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup); - return ast_bridge_channel_queue_frame(bridge_channel, &action); + snapshot = ast_bridge_snapshot_create(bridge); + if (!snapshot) { + return NULL; + } + + return stasis_message_create(ast_bridge_snapshot_type(), snapshot); } -static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +static void destroy_bridge(void *obj) { - ast_bridge_channel_lock_bridge(bridge_channel); -/* - * BUGBUG need to implement a deferred write queue for when there is no peer channel in the bridge (yet or it was kicked). - * - * The tech decides if a frame needs to be pushed back for deferral. - * simple_bridge/native_bridge are likely the only techs that will do this. - */ - bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame); - ast_bridge_unlock(bridge_channel->bridge); + struct ast_bridge *bridge = obj; - /* - * Claim successful write to bridge. If deferred frame - * support is added, claim successfully deferred. - */ - return 0; -} + ast_debug(1, "Bridge %s: actually destroying %s bridge, nobody wants it anymore\n", + bridge->uniqueid, bridge->v_table->name); -int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen) -{ - struct ast_frame frame = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = action, - .datalen = datalen, - .data.ptr = (void *) data, - }; + if (bridge->construction_completed) { + RAII_VAR(struct stasis_message *, clear_msg, NULL, ao2_cleanup); - return bridge_channel_write_frame(bridge_channel, &frame); -} + clear_msg = create_bridge_snapshot_message(bridge); + if (clear_msg) { + RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); -int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen) -{ - struct ast_frame frame = { - .frametype = AST_FRAME_CONTROL, - .subclass.integer = control, - .datalen = datalen, - .data.ptr = (void *) data, - }; + msg = stasis_cache_clear_create(clear_msg); + if (msg) { + stasis_publish(ast_bridge_topic(bridge), msg); + } + } + } - return bridge_channel_write_frame(bridge_channel, &frame); -} + /* Do any pending actions in the context of destruction. */ + ast_bridge_lock(bridge); + bridge_handle_actions(bridge); + ast_bridge_unlock(bridge); -int ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class) -{ - RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); - size_t datalen; + /* There should not be any channels left in the bridge. */ + ast_assert(AST_LIST_EMPTY(&bridge->channels)); - if (!ast_strlen_zero(moh_class)) { - datalen = strlen(moh_class) + 1; + ast_debug(1, "Bridge %s: calling %s bridge destructor\n", + bridge->uniqueid, bridge->v_table->name); + bridge->v_table->destroy(bridge); - blob = ast_json_pack("{s: s}", - "musicclass", moh_class); - } else { - moh_class = NULL; - datalen = 0; + /* Pass off the bridge to the technology to destroy if needed */ + if (bridge->technology) { + ast_debug(1, "Bridge %s: calling %s technology stop\n", + bridge->uniqueid, bridge->technology->name); + if (bridge->technology->stop) { + ast_bridge_lock(bridge); + bridge->technology->stop(bridge); + ast_bridge_unlock(bridge); + } + ast_debug(1, "Bridge %s: calling %s technology destructor\n", + bridge->uniqueid, bridge->technology->name); + if (bridge->technology->destroy) { + bridge->technology->destroy(bridge); + } + ast_module_unref(bridge->technology->mod); + bridge->technology = NULL; } - ast_channel_publish_blob(bridge_channel->chan, ast_channel_hold_type(), blob); - return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD, - moh_class, datalen); + if (bridge->callid) { + bridge->callid = ast_callid_unref(bridge->callid); + } + + cleanup_video_mode(bridge); } -int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel) +struct ast_bridge *bridge_register(struct ast_bridge *bridge) { - ast_channel_publish_blob(bridge_channel->chan, ast_channel_unhold_type(), NULL); - return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, NULL, 0); + if (bridge) { + bridge->construction_completed = 1; + ast_bridge_publish_state(bridge); + if (!ao2_link(bridges, bridge)) { + ast_bridge_destroy(bridge); + bridge = NULL; + } + } + return bridge; } -static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args) +struct ast_bridge *bridge_alloc(size_t size, const struct ast_bridge_methods *v_table) { - int res = 0; + struct ast_bridge *bridge; - if (!strcasecmp("Gosub", app_name)) { - ast_app_exec_sub(NULL, chan, app_args, 0); - } else if (!strcasecmp("Macro", app_name)) { - ast_app_exec_macro(NULL, chan, app_args); - } else { - struct ast_app *app; + /* Check v_table that all methods are present. */ + if (!v_table + || !v_table->name + || !v_table->destroy + || !v_table->dissolving + || !v_table->push + || !v_table->pull + || !v_table->notify_masquerade + || !v_table->get_merge_priority) { + ast_log(LOG_ERROR, "Virtual method table for bridge class %s not complete.\n", + v_table && v_table->name ? v_table->name : "<unknown>"); + ast_assert(0); + return NULL; + } - app = pbx_findapp(app_name); - if (!app) { - ast_log(LOG_WARNING, "Could not find application (%s)\n", app_name); - } else { - res = pbx_exec(chan, app, app_args); - } + bridge = ao2_alloc(size, destroy_bridge); + if (bridge) { + bridge->v_table = v_table; } - return res; + return bridge; } -void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) +struct ast_bridge *bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags) { - if (moh_class) { - ast_bridge_channel_write_hold(bridge_channel, moh_class); + if (!self) { + return NULL; } - if (run_app_helper(bridge_channel->chan, app_name, S_OR(app_args, ""))) { - /* Break the bridge if the app returns non-zero. */ - bridge_handle_hangup(bridge_channel); + + ast_uuid_generate_str(self->uniqueid, sizeof(self->uniqueid)); + ast_set_flag(&self->feature_flags, flags); + self->allowed_capabilities = capabilities; + + /* Use our helper function to find the "best" bridge technology. */ + self->technology = find_best_technology(capabilities, self); + if (!self->technology) { + ast_log(LOG_WARNING, "Bridge %s: Could not create class %s. No technology to support it.\n", + self->uniqueid, self->v_table->name); + ao2_ref(self, -1); + return NULL; } - if (moh_class) { - ast_bridge_channel_write_unhold(bridge_channel); + + /* Pass off the bridge to the technology to manipulate if needed */ + ast_debug(1, "Bridge %s: calling %s technology constructor\n", + self->uniqueid, self->technology->name); + if (self->technology->create && self->technology->create(self)) { + ast_log(LOG_WARNING, "Bridge %s: failed to setup bridge technology %s\n", + self->uniqueid, self->technology->name); + ao2_ref(self, -1); + return NULL; + } + ast_debug(1, "Bridge %s: calling %s technology start\n", + self->uniqueid, self->technology->name); + if (self->technology->start && self->technology->start(self)) { + ast_log(LOG_WARNING, "Bridge %s: failed to start bridge technology %s\n", + self->uniqueid, self->technology->name); + ao2_ref(self, -1); + return NULL; } -} -struct bridge_run_app { - /*! Offset into app_name[] where the MOH class name starts. (zero if no MOH) */ - int moh_offset; - /*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */ - int app_args_offset; - /*! Application name to run. */ - char app_name[0]; -}; + if (!ast_bridge_topic(self)) { + ao2_ref(self, -1); + return NULL; + } + + return self; +} /*! * \internal - * \brief Handle the run application bridge action. + * \brief ast_bridge base class destructor. * \since 12.0.0 * - * \param bridge_channel Which channel to run the application on. - * \param data Action frame data to run the application. + * \param self Bridge to operate upon. + * + * \note Stub because of nothing to do. * * \return Nothing */ -static void bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, struct bridge_run_app *data) +static void bridge_base_destroy(struct ast_bridge *self) { - ast_bridge_channel_run_app(bridge_channel, data->app_name, - data->app_args_offset ? &data->app_name[data->app_args_offset] : NULL, - data->moh_offset ? &data->app_name[data->moh_offset] : NULL); } -static int payload_helper_app(ast_bridge_channel_post_action_data post_it, - struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) +/*! + * \internal + * \brief The bridge is being dissolved. + * \since 12.0.0 + * + * \param self Bridge to operate upon. + * + * \return Nothing + */ +static void bridge_base_dissolving(struct ast_bridge *self) { - struct bridge_run_app *app_data; - size_t len_name = strlen(app_name) + 1; - size_t len_args = ast_strlen_zero(app_args) ? 0 : strlen(app_args) + 1; - size_t len_moh = !moh_class ? 0 : strlen(moh_class) + 1; - size_t len_data = sizeof(*app_data) + len_name + len_args + len_moh; + ao2_unlink(bridges, self); +} - /* Fill in application run frame data. */ - app_data = alloca(len_data); - app_data->app_args_offset = len_args ? len_name : 0; - app_data->moh_offset = len_moh ? len_name + len_args : 0; - strcpy(app_data->app_name, app_name);/* Safe */ - if (len_args) { - strcpy(&app_data->app_name[app_data->app_args_offset], app_args);/* Safe */ - } - if (moh_class) { - strcpy(&app_data->app_name[app_data->moh_offset], moh_class);/* Safe */ - } - - return post_it(bridge_channel, AST_BRIDGE_ACTION_RUN_APP, app_data, len_data); -} - -int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) -{ - return payload_helper_app(ast_bridge_channel_write_action_data, - bridge_channel, app_name, app_args, moh_class); -} - -int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) -{ - return payload_helper_app(ast_bridge_channel_queue_action_data, - bridge_channel, app_name, app_args, moh_class); -} - -void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) +/*! + * \internal + * \brief ast_bridge base push method. + * \since 12.0.0 + * + * \param self Bridge to operate upon. + * \param bridge_channel Bridge channel to push. + * \param swap Bridge channel to swap places with if not NULL. + * + * \note On entry, self is already locked. + * \note Stub because of nothing to do. + * + * \retval 0 on success + * \retval -1 on failure + */ +static int bridge_base_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) { - if (moh_class) { - ast_bridge_channel_write_hold(bridge_channel, moh_class); - } - if (custom_play) { - custom_play(bridge_channel, playfile); - } else { - ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE); - } - if (moh_class) { - ast_bridge_channel_write_unhold(bridge_channel); - } - - /* - * It may be necessary to resume music on hold after we finish - * playing the announcment. - * - * XXX We have no idea what MOH class was in use before playing - * the file. This method also fails to restore ringing indications. - * the proposed solution is to create a resume_entertainment callback - * for the bridge technology and execute it here. - */ - if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_MOH)) { - ast_moh_start(bridge_channel->chan, NULL, NULL); - } + return 0; } -struct bridge_playfile { - /*! Call this function to play the playfile. (NULL if normal sound file to play) */ - ast_bridge_custom_play_fn custom_play; - /*! Offset into playfile[] where the MOH class name starts. (zero if no MOH)*/ - int moh_offset; - /*! Filename to play. */ - char playfile[0]; -}; - /*! * \internal - * \brief Handle the playfile bridge action. + * \brief ast_bridge base pull method. * \since 12.0.0 * - * \param bridge_channel Which channel to play a file on. - * \param payload Action frame payload to play a file. + * \param self Bridge to operate upon. + * \param bridge_channel Bridge channel to pull. + * + * \note On entry, self is already locked. * * \return Nothing */ -static void bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, struct bridge_playfile *payload) -{ - ast_bridge_channel_playfile(bridge_channel, payload->custom_play, payload->playfile, - payload->moh_offset ? &payload->playfile[payload->moh_offset] : NULL); -} - -static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it, - struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) -{ - struct bridge_playfile *payload; - size_t len_name = strlen(playfile) + 1; - size_t len_moh = !moh_class ? 0 : strlen(moh_class) + 1; - size_t len_payload = sizeof(*payload) + len_name + len_moh; - - /* Fill in play file frame data. */ - payload = alloca(len_payload); - payload->custom_play = custom_play; - payload->moh_offset = len_moh ? len_name : 0; - strcpy(payload->playfile, playfile);/* Safe */ - if (moh_class) { - strcpy(&payload->playfile[payload->moh_offset], moh_class);/* Safe */ - } - - return post_it(bridge_channel, AST_BRIDGE_ACTION_PLAY_FILE, payload, len_payload); -} - -int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) -{ - return payload_helper_playfile(ast_bridge_channel_write_action_data, - bridge_channel, custom_play, playfile, moh_class); -} - -int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) +static void bridge_base_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel) { - return payload_helper_playfile(ast_bridge_channel_queue_action_data, - bridge_channel, custom_play, playfile, moh_class); + ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL); } -struct bridge_custom_callback { - /*! Call this function on the bridge channel thread. */ - ast_bridge_custom_callback_fn callback; - /*! Size of the payload if it exists. A number otherwise. */ - size_t payload_size; - /*! Nonzero if the payload exists. */ - char payload_exists; - /*! Payload to give to callback. */ - char payload[0]; -}; - /*! * \internal - * \brief Handle the do custom callback bridge action. + * \brief ast_bridge base notify_masquerade method. * \since 12.0.0 * - * \param bridge_channel Which channel to run the application on. - * \param data Action frame data to run the application. + * \param self Bridge to operate upon. + * \param bridge_channel Bridge channel that was masqueraded. + * + * \note On entry, self is already locked. * * \return Nothing */ -static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel, struct bridge_custom_callback *data) -{ - data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size); -} - -static int payload_helper_cb(ast_bridge_channel_post_action_data post_it, - struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) -{ - struct bridge_custom_callback *cb_data; - size_t len_data = sizeof(*cb_data) + (payload ? payload_size : 0); - - /* Sanity check. */ - if (!callback) { - ast_assert(0); - return -1; - } - - /* Fill in custom callback frame data. */ - cb_data = alloca(len_data); - cb_data->callback = callback; - cb_data->payload_size = payload_size; - cb_data->payload_exists = payload && payload_size; - if (cb_data->payload_exists) { - memcpy(cb_data->payload, payload, payload_size);/* Safe */ - } - - return post_it(bridge_channel, AST_BRIDGE_ACTION_CALLBACK, cb_data, len_data); -} - -int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) +static void bridge_base_notify_masquerade(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel) { - return payload_helper_cb(ast_bridge_channel_write_action_data, - bridge_channel, callback, payload, payload_size); + self->reconfigured = 1; } -int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) +/*! + * \internal + * \brief Get the merge priority of this bridge. + * \since 12.0.0 + * + * \param self Bridge to operate upon. + * + * \note On entry, self is already locked. + * + * \return Merge priority + */ +static int bridge_base_get_merge_priority(struct ast_bridge *self) { - return payload_helper_cb(ast_bridge_channel_queue_action_data, - bridge_channel, callback, payload, payload_size); + return 0; } -struct bridge_park { - int parker_uuid_offset; - int app_data_offset; - /* buffer used for holding those strings */ - char parkee_uuid[0]; +struct ast_bridge_methods ast_bridge_base_v_table = { + .name = "base", + .destroy = bridge_base_destroy, + .dissolving = bridge_base_dissolving, + .push = bridge_base_push, + .pull = bridge_base_pull, + .notify_masquerade = bridge_base_notify_masquerade, + .get_merge_priority = bridge_base_get_merge_priority, }; -static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload) +struct ast_bridge *ast_bridge_base_new(uint32_t capabilities, unsigned int flags) { - ast_bridge_channel_park(bridge_channel, payload->parkee_uuid, - &payload->parkee_uuid[payload->parker_uuid_offset], - payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL); + void *bridge; + + bridge = bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_base_v_table); + bridge = bridge_base_init(bridge, capabilities, flags); + bridge = bridge_register(bridge); + return bridge; } -static int payload_helper_park(ast_bridge_channel_post_action_data post_it, - struct ast_bridge_channel *bridge_channel, - const char *parkee_uuid, - const char *parker_uuid, - const char *app_data) +int ast_bridge_destroy(struct ast_bridge *bridge) { - struct bridge_park *payload; - size_t len_parkee_uuid = strlen(parkee_uuid) + 1; - size_t len_parker_uuid = strlen(parker_uuid) + 1; - size_t len_app_data = !app_data ? 0 : strlen(app_data) + 1; - size_t len_payload = sizeof(*payload) + len_parker_uuid + len_parkee_uuid + len_app_data; + ast_debug(1, "Bridge %s: telling all channels to leave the party\n", bridge->uniqueid); + ast_bridge_lock(bridge); + bridge_dissolve(bridge); + ast_bridge_unlock(bridge); - payload = alloca(len_payload); - payload->app_data_offset = len_app_data ? len_parkee_uuid + len_parker_uuid : 0; - payload->parker_uuid_offset = len_parkee_uuid; - strcpy(payload->parkee_uuid, parkee_uuid); - strcpy(&payload->parkee_uuid[payload->parker_uuid_offset], parker_uuid); - if (app_data) { - strcpy(&payload->parkee_uuid[payload->app_data_offset], app_data); - } + ao2_ref(bridge, -1); - return post_it(bridge_channel, AST_BRIDGE_ACTION_PARK, payload, len_payload); + return 0; } -int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data) +static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { - return payload_helper_park(ast_bridge_channel_write_action_data, - bridge_channel, parkee_uuid, parker_uuid, app_data); -} + struct ast_format read_format; + struct ast_format write_format; + struct ast_format best_format; + char codec_buf[512]; -/*! - * \internal - * \brief Feed notification that a frame is waiting on a channel into the bridging core - * - * \param bridge_channel Bridge channel the notification was received on - */ -static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel) -{ - struct ast_frame *frame; + ast_format_copy(&read_format, ast_channel_readformat(bridge_channel->chan)); + ast_format_copy(&write_format, ast_channel_writeformat(bridge_channel->chan)); - if (bridge_channel->features->mute) { - frame = ast_read_noaudio(bridge_channel->chan); - } else { - frame = ast_read(bridge_channel->chan); - } + /* Are the formats currently in use something this bridge can handle? */ + if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, ast_channel_readformat(bridge_channel->chan))) { + ast_best_codec(bridge->technology->format_capabilities, &best_format); - if (!frame) { - bridge_handle_hangup(bridge_channel); - return; - } - switch (frame->frametype) { - case AST_FRAME_CONTROL: - switch (frame->subclass.integer) { - case AST_CONTROL_HANGUP: - bridge_handle_hangup(bridge_channel); - ast_frfree(frame); - return; -/* BUGBUG This is where incoming HOLD/UNHOLD memory should register. Write UNHOLD into bridge when this channel is pulled. */ - default: - break; - } - break; - case AST_FRAME_DTMF_BEGIN: - frame = bridge_handle_dtmf(bridge_channel, frame); - if (!frame) { - return; - } - /* Fall through */ - case AST_FRAME_DTMF_END: - if (!bridge_channel->features->dtmf_passthrough) { - ast_frfree(frame); - return; + /* Read format is a no go... */ + ast_debug(1, "Bridge technology %s wants to read any of formats %s but channel has %s\n", + bridge->technology->name, + ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->format_capabilities), + ast_getformatname(&read_format)); + + /* Switch read format to the best one chosen */ + if (ast_set_read_format(bridge_channel->chan, &best_format)) { + ast_log(LOG_WARNING, "Failed to set channel %s to read format %s\n", + ast_channel_name(bridge_channel->chan), ast_getformatname(&best_format)); + return -1; } -/* BUGBUG This is where incoming DTMF begin/end memory should register. Write DTMF end into bridge when this channel is pulled. */ - break; - default: - break; + ast_debug(1, "Bridge %s put channel %s into read format %s\n", + bridge->uniqueid, ast_channel_name(bridge_channel->chan), + ast_getformatname(&best_format)); + } else { + ast_debug(1, "Bridge %s is happy that channel %s already has read format %s\n", + bridge->uniqueid, ast_channel_name(bridge_channel->chan), + ast_getformatname(&read_format)); } - /* Simply write the frame out to the bridge technology. */ -/* BUGBUG The tech is where AST_CONTROL_ANSWER hook should go. (early bridge) */ -/* BUGBUG The tech is where incoming BUSY/CONGESTION hangup should happen? (early bridge) */ - bridge_channel_write_frame(bridge_channel, frame); - ast_frfree(frame); -} + if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, &write_format)) { + ast_best_codec(bridge->technology->format_capabilities, &best_format); -/*! - * \internal - * \brief Complete joining a channel to the bridge. - * \since 12.0.0 - * - * \param bridge What to operate upon. - * \param bridge_channel What is joining the bridge technology. - * - * \note On entry, bridge is already locked. - * - * \return Nothing - */ -static void bridge_channel_complete_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) -{ - /* Make the channel compatible with the bridge */ - bridge_make_compatible(bridge, bridge_channel); + /* Write format is a no go... */ + ast_debug(1, "Bridge technology %s wants to write any of formats %s but channel has %s\n", + bridge->technology->name, + ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->format_capabilities), + ast_getformatname(&write_format)); - /* Tell the bridge technology we are joining so they set us up */ - ast_debug(1, "Bridge %s: %p(%s) is joining %s technology\n", - bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan), - bridge->technology->name); - if (bridge->technology->join - && bridge->technology->join(bridge, bridge_channel)) { - ast_debug(1, "Bridge %s: %p(%s) failed to join %s technology\n", - bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan), - bridge->technology->name); - bridge_channel->just_joined = 1; - return; + /* Switch write format to the best one chosen */ + if (ast_set_write_format(bridge_channel->chan, &best_format)) { + ast_log(LOG_WARNING, "Failed to set channel %s to write format %s\n", + ast_channel_name(bridge_channel->chan), ast_getformatname(&best_format)); + return -1; + } + ast_debug(1, "Bridge %s put channel %s into write format %s\n", + bridge->uniqueid, ast_channel_name(bridge_channel->chan), + ast_getformatname(&best_format)); + } else { + ast_debug(1, "Bridge %s is happy that channel %s already has write format %s\n", + bridge->uniqueid, ast_channel_name(bridge_channel->chan), + ast_getformatname(&write_format)); } - bridge_channel->just_joined = 0; + return 0; } /*! * \internal - * \brief Complete joining new channels to the bridge. + * \brief Perform the smart bridge operation. * \since 12.0.0 * - * \param bridge Check for new channels on this bridge. + * \param bridge Work on this bridge. + * + * \details + * Basically see if a new bridge technology should be used instead + * of the current one. * * \note On entry, bridge is already locked. * - * \return Nothing + * \retval 0 on success. + * \retval -1 on error. */ -static void bridge_complete_join(struct ast_bridge *bridge) +static int smart_bridge_operation(struct ast_bridge *bridge) { + uint32_t new_capabilities; + struct ast_bridge_technology *new_technology; + struct ast_bridge_technology *old_technology = bridge->technology; struct ast_bridge_channel *bridge_channel; + struct ast_frame *deferred_action; + struct ast_bridge dummy_bridge = { + .technology = bridge->technology, + .tech_pvt = bridge->tech_pvt, + }; if (bridge->dissolved) { - /* - * No sense in completing the join on channels for a dissolved - * bridge. They are just going to be removed soon anyway. - * However, we do have reason to abort here because the bridge - * technology may not be able to handle the number of channels - * still in the bridge. - */ - return; - } - - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - if (!bridge_channel->just_joined) { - continue; - } - bridge_channel_complete_join(bridge, bridge_channel); + ast_debug(1, "Bridge %s is dissolved, not performing smart bridge operation.\n", + bridge->uniqueid); + return 0; } -} -/*! \brief Helper function used to find the "best" bridge technology given specified capabilities */ -static struct ast_bridge_technology *find_best_technology(uint32_t capabilities, struct ast_bridge *bridge) -{ - struct ast_bridge_technology *current; - struct ast_bridge_technology *best = NULL; - - AST_RWLIST_RDLOCK(&bridge_technologies); - AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) { - if (current->suspended) { - ast_debug(1, "Bridge technology %s is suspended. Skipping.\n", - current->name); - continue; - } - if (!(current->capabilities & capabilities)) { - ast_debug(1, "Bridge technology %s does not have any capabilities we want.\n", - current->name); - continue; - } - if (best && current->preference <= best->preference) { - ast_debug(1, "Bridge technology %s has less preference than %s (%d <= %d). Skipping.\n", - current->name, best->name, current->preference, best->preference); - continue; - } - if (current->compatible && !current->compatible(bridge)) { - ast_debug(1, "Bridge technology %s is not compatible with properties of existing bridge.\n", - current->name); - continue; - } - best = current; - } - - if (best) { - /* Increment it's module reference count if present so it does not get unloaded while in use */ - ast_module_ref(best->mod); - ast_debug(1, "Chose bridge technology %s\n", best->name); - } - - AST_RWLIST_UNLOCK(&bridge_technologies); - - return best; -} - -struct tech_deferred_destroy { - struct ast_bridge_technology *tech; - void *tech_pvt; -}; - -/*! - * \internal - * \brief Deferred destruction of bridge tech private structure. - * \since 12.0.0 - * - * \param bridge What to execute the action on. - * \param action Deferred bridge tech destruction. - * - * \note On entry, bridge must not be locked. - * - * \return Nothing - */ -static void bridge_tech_deferred_destroy(struct ast_bridge *bridge, struct ast_frame *action) -{ - struct tech_deferred_destroy *deferred = action->data.ptr; - struct ast_bridge dummy_bridge = { - .technology = deferred->tech, - .tech_pvt = deferred->tech_pvt, - }; - - ast_copy_string(dummy_bridge.uniqueid, bridge->uniqueid, sizeof(dummy_bridge.uniqueid)); - ast_debug(1, "Bridge %s: calling %s technology destructor (deferred, dummy)\n", - dummy_bridge.uniqueid, dummy_bridge.technology->name); - dummy_bridge.technology->destroy(&dummy_bridge); - ast_module_unref(dummy_bridge.technology->mod); -} - -/*! - * \internal - * \brief Handle bridge action frame. - * \since 12.0.0 - * - * \param bridge What to execute the action on. - * \param action What to do. - * - * \note On entry, bridge is already locked. - * \note Can be called by the bridge destructor. - * - * \return Nothing - */ -static void bridge_action_bridge(struct ast_bridge *bridge, struct ast_frame *action) -{ -#if 0 /* In case we need to know when the destructor is calling us. */ - int in_destructor = !ao2_ref(bridge, 0); -#endif - - switch (action->subclass.integer) { - case AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY: - ast_bridge_unlock(bridge); - bridge_tech_deferred_destroy(bridge, action); - ast_bridge_lock(bridge); - break; - case AST_BRIDGE_ACTION_DEFERRED_DISSOLVING: - ast_bridge_unlock(bridge); - bridge->v_table->dissolving(bridge); - ast_bridge_lock(bridge); - break; - default: - /* Unexpected deferred action type. Should never happen. */ - ast_assert(0); - break; - } -} - -/*! - * \internal - * \brief Do any pending bridge actions. - * \since 12.0.0 - * - * \param bridge What to do actions on. - * - * \note On entry, bridge is already locked. - * \note Can be called by the bridge destructor. - * - * \return Nothing - */ -static void bridge_handle_actions(struct ast_bridge *bridge) -{ - struct ast_frame *action; - - while ((action = AST_LIST_REMOVE_HEAD(&bridge->action_queue, frame_list))) { - switch (action->frametype) { - case AST_FRAME_BRIDGE_ACTION: - bridge_action_bridge(bridge, action); - break; - default: - /* Unexpected deferred frame type. Should never happen. */ - ast_assert(0); - break; - } - ast_frfree(action); - } -} - -static struct stasis_message *create_bridge_snapshot_message(struct ast_bridge *bridge) -{ - RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup); - - snapshot = ast_bridge_snapshot_create(bridge); - if (!snapshot) { - return NULL; - } - - return stasis_message_create(ast_bridge_snapshot_type(), snapshot); -} - -static void destroy_bridge(void *obj) -{ - struct ast_bridge *bridge = obj; - - ast_debug(1, "Bridge %s: actually destroying %s bridge, nobody wants it anymore\n", - bridge->uniqueid, bridge->v_table->name); - - if (bridge->construction_completed) { - RAII_VAR(struct stasis_message *, clear_msg, NULL, ao2_cleanup); - - clear_msg = create_bridge_snapshot_message(bridge); - if (clear_msg) { - RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); - - msg = stasis_cache_clear_create(clear_msg); - if (msg) { - stasis_publish(ast_bridge_topic(bridge), msg); - } - } - } - - /* Do any pending actions in the context of destruction. */ - ast_bridge_lock(bridge); - bridge_handle_actions(bridge); - ast_bridge_unlock(bridge); - - /* There should not be any channels left in the bridge. */ - ast_assert(AST_LIST_EMPTY(&bridge->channels)); - - ast_debug(1, "Bridge %s: calling %s bridge destructor\n", - bridge->uniqueid, bridge->v_table->name); - bridge->v_table->destroy(bridge); - - /* Pass off the bridge to the technology to destroy if needed */ - if (bridge->technology) { - ast_debug(1, "Bridge %s: calling %s technology stop\n", - bridge->uniqueid, bridge->technology->name); - if (bridge->technology->stop) { - ast_bridge_lock(bridge); - bridge->technology->stop(bridge); - ast_bridge_unlock(bridge); - } - ast_debug(1, "Bridge %s: calling %s technology destructor\n", - bridge->uniqueid, bridge->technology->name); - if (bridge->technology->destroy) { - bridge->technology->destroy(bridge); - } - ast_module_unref(bridge->technology->mod); - bridge->technology = NULL; - } - - if (bridge->callid) { - bridge->callid = ast_callid_unref(bridge->callid); - } - - cleanup_video_mode(bridge); -} - -struct ast_bridge *ast_bridge_register(struct ast_bridge *bridge) -{ - if (bridge) { - bridge->construction_completed = 1; - ast_bridge_publish_state(bridge); - if (!ao2_link(bridges, bridge)) { - ast_bridge_destroy(bridge); - bridge = NULL; - } - } - return bridge; -} - -struct ast_bridge *ast_bridge_alloc(size_t size, const struct ast_bridge_methods *v_table) -{ - struct ast_bridge *bridge; - - /* Check v_table that all methods are present. */ - if (!v_table - || !v_table->name - || !v_table->destroy - || !v_table->dissolving - || !v_table->push - || !v_table->pull - || !v_table->notify_masquerade - || !v_table->get_merge_priority) { - ast_log(LOG_ERROR, "Virtual method table for bridge class %s not complete.\n", - v_table && v_table->name ? v_table->name : "<unknown>"); - ast_assert(0); - return NULL; - } - - bridge = ao2_alloc(size, destroy_bridge); - if (bridge) { - bridge->v_table = v_table; - } - return bridge; -} - -struct ast_bridge *ast_bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags) -{ - if (!self) { - return NULL; - } - - ast_uuid_generate_str(self->uniqueid, sizeof(self->uniqueid)); - ast_set_flag(&self->feature_flags, flags); - self->allowed_capabilities = capabilities; - - /* Use our helper function to find the "best" bridge technology. */ - self->technology = find_best_technology(capabilities, self); - if (!self->technology) { - ast_log(LOG_WARNING, "Bridge %s: Could not create class %s. No technology to support it.\n", - self->uniqueid, self->v_table->name); - ao2_ref(self, -1); - return NULL; - } - - /* Pass off the bridge to the technology to manipulate if needed */ - ast_debug(1, "Bridge %s: calling %s technology constructor\n", - self->uniqueid, self->technology->name); - if (self->technology->create && self->technology->create(self)) { - ast_log(LOG_WARNING, "Bridge %s: failed to setup bridge technology %s\n", - self->uniqueid, self->technology->name); - ao2_ref(self, -1); - return NULL; - } - ast_debug(1, "Bridge %s: calling %s technology start\n", - self->uniqueid, self->technology->name); - if (self->technology->start && self->technology->start(self)) { - ast_log(LOG_WARNING, "Bridge %s: failed to start bridge technology %s\n", - self->uniqueid, self->technology->name); - ao2_ref(self, -1); - return NULL; - } - - if (!ast_bridge_topic(self)) { - ao2_ref(self, -1); - return NULL; - } - - return self; -} - -/*! - * \internal - * \brief ast_bridge base class destructor. - * \since 12.0.0 - * - * \param self Bridge to operate upon. - * - * \note Stub because of nothing to do. - * - * \return Nothing - */ -static void bridge_base_destroy(struct ast_bridge *self) -{ -} - -/*! - * \internal - * \brief The bridge is being dissolved. - * \since 12.0.0 - * - * \param self Bridge to operate upon. - * - * \return Nothing - */ -static void bridge_base_dissolving(struct ast_bridge *self) -{ - ao2_unlink(bridges, self); -} - -/*! - * \internal - * \brief ast_bridge base push method. - * \since 12.0.0 - * - * \param self Bridge to operate upon. - * \param bridge_channel Bridge channel to push. - * \param swap Bridge channel to swap places with if not NULL. - * - * \note On entry, self is already locked. - * \note Stub because of nothing to do. - * - * \retval 0 on success - * \retval -1 on failure - */ -static int bridge_base_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) -{ - return 0; -} - -/*! - * \internal - * \brief ast_bridge base pull method. - * \since 12.0.0 - * - * \param self Bridge to operate upon. - * \param bridge_channel Bridge channel to pull. - * - * \note On entry, self is already locked. - * - * \return Nothing - */ -static void bridge_base_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel) -{ - ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL); -} - -/*! - * \internal - * \brief ast_bridge base notify_masquerade method. - * \since 12.0.0 - * - * \param self Bridge to operate upon. - * \param bridge_channel Bridge channel that was masqueraded. - * - * \note On entry, self is already locked. - * - * \return Nothing - */ -static void bridge_base_notify_masquerade(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel) -{ - self->reconfigured = 1; -} - -/*! - * \internal - * \brief Get the merge priority of this bridge. - * \since 12.0.0 - * - * \param self Bridge to operate upon. - * - * \note On entry, self is already locked. - * - * \return Merge priority - */ -static int bridge_base_get_merge_priority(struct ast_bridge *self) -{ - return 0; -} - -struct ast_bridge_methods ast_bridge_base_v_table = { - .name = "base", - .destroy = bridge_base_destroy, - .dissolving = bridge_base_dissolving, - .push = bridge_base_push, - .pull = bridge_base_pull, - .notify_masquerade = bridge_base_notify_masquerade, - .get_merge_priority = bridge_base_get_merge_priority, -}; - -struct ast_bridge *ast_bridge_base_new(uint32_t capabilities, unsigned int flags) -{ - void *bridge; - - bridge = ast_bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_base_v_table); - bridge = ast_bridge_base_init(bridge, capabilities, flags); - bridge = ast_bridge_register(bridge); - return bridge; -} - -int ast_bridge_destroy(struct ast_bridge *bridge) -{ - ast_debug(1, "Bridge %s: telling all channels to leave the party\n", bridge->uniqueid); - ast_bridge_lock(bridge); - bridge_dissolve(bridge); - ast_bridge_unlock(bridge); - - ao2_ref(bridge, -1); - - return 0; -} - -static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) -{ - struct ast_format read_format; - struct ast_format write_format; - struct ast_format best_format; - char codec_buf[512]; - - ast_format_copy(&read_format, ast_channel_readformat(bridge_channel->chan)); - ast_format_copy(&write_format, ast_channel_writeformat(bridge_channel->chan)); - - /* Are the formats currently in use something this bridge can handle? */ - if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, ast_channel_readformat(bridge_channel->chan))) { - ast_best_codec(bridge->technology->format_capabilities, &best_format); - - /* Read format is a no go... */ - ast_debug(1, "Bridge technology %s wants to read any of formats %s but channel has %s\n", - bridge->technology->name, - ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->format_capabilities), - ast_getformatname(&read_format)); - - /* Switch read format to the best one chosen */ - if (ast_set_read_format(bridge_channel->chan, &best_format)) { - ast_log(LOG_WARNING, "Failed to set channel %s to read format %s\n", - ast_channel_name(bridge_channel->chan), ast_getformatname(&best_format)); - return -1; - } - ast_debug(1, "Bridge %s put channel %s into read format %s\n", - bridge->uniqueid, ast_channel_name(bridge_channel->chan), - ast_getformatname(&best_format)); - } else { - ast_debug(1, "Bridge %s is happy that channel %s already has read format %s\n", - bridge->uniqueid, ast_channel_name(bridge_channel->chan), - ast_getformatname(&read_format)); - } - - if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, &write_format)) { - ast_best_codec(bridge->technology->format_capabilities, &best_format); - - /* Write format is a no go... */ - ast_debug(1, "Bridge technology %s wants to write any of formats %s but channel has %s\n", - bridge->technology->name, - ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->format_capabilities), - ast_getformatname(&write_format)); - - /* Switch write format to the best one chosen */ - if (ast_set_write_format(bridge_channel->chan, &best_format)) { - ast_log(LOG_WARNING, "Failed to set channel %s to write format %s\n", - ast_channel_name(bridge_channel->chan), ast_getformatname(&best_format)); - return -1; - } - ast_debug(1, "Bridge %s put channel %s into write format %s\n", - bridge->uniqueid, ast_channel_name(bridge_channel->chan), - ast_getformatname(&best_format)); - } else { - ast_debug(1, "Bridge %s is happy that channel %s already has write format %s\n", - bridge->uniqueid, ast_channel_name(bridge_channel->chan), - ast_getformatname(&write_format)); - } - - return 0; -} - -/*! - * \internal - * \brief Perform the smart bridge operation. - * \since 12.0.0 - * - * \param bridge Work on this bridge. - * - * \details - * Basically see if a new bridge technology should be used instead - * of the current one. - * - * \note On entry, bridge is already locked. - * - * \retval 0 on success. - * \retval -1 on error. - */ -static int smart_bridge_operation(struct ast_bridge *bridge) -{ - uint32_t new_capabilities; - struct ast_bridge_technology *new_technology; - struct ast_bridge_technology *old_technology = bridge->technology; - struct ast_bridge_channel *bridge_channel; - struct ast_frame *deferred_action; - struct ast_bridge dummy_bridge = { - .technology = bridge->technology, - .tech_pvt = bridge->tech_pvt, - }; - - if (bridge->dissolved) { - ast_debug(1, "Bridge %s is dissolved, not performing smart bridge operation.\n", - bridge->uniqueid); - return 0; - } - - /* Determine new bridge technology capabilities needed. */ - if (2 < bridge->num_channels) { - new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX; - new_capabilities &= bridge->allowed_capabilities; - } else { - new_capabilities = AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX; - new_capabilities &= bridge->allowed_capabilities; - if (!new_capabilities - && (bridge->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)) { - /* Allow switching between different multimix bridge technologies. */ - new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX; - } - } - - /* Find a bridge technology to satisfy the new capabilities. */ - new_technology = find_best_technology(new_capabilities, bridge); - if (!new_technology) { - int is_compatible = 0; - - if (old_technology->compatible) { - is_compatible = old_technology->compatible(bridge); - } else if (old_technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) { - is_compatible = 1; - } else if (bridge->num_channels <= 2 - && (old_technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX)) { - is_compatible = 1; - } - - if (is_compatible) { - ast_debug(1, "Bridge %s could not get a new technology, staying with old technology.\n", - bridge->uniqueid); - return 0; - } - ast_log(LOG_WARNING, "Bridge %s has no technology available to support it.\n", - bridge->uniqueid); - return -1; - } - if (new_technology == old_technology) { - ast_debug(1, "Bridge %s is already using the new technology.\n", - bridge->uniqueid); - ast_module_unref(old_technology->mod); - return 0; - } - - ast_copy_string(dummy_bridge.uniqueid, bridge->uniqueid, sizeof(dummy_bridge.uniqueid)); - - if (old_technology->destroy) { - struct tech_deferred_destroy deferred_tech_destroy = { - .tech = dummy_bridge.technology, - .tech_pvt = dummy_bridge.tech_pvt, - }; - struct ast_frame action = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY, - .data.ptr = &deferred_tech_destroy, - .datalen = sizeof(deferred_tech_destroy), - }; - - /* - * We need to defer the bridge technology destroy callback - * because we have the bridge locked. - */ - deferred_action = ast_frdup(&action); - if (!deferred_action) { - ast_module_unref(new_technology->mod); - return -1; - } - } else { - deferred_action = NULL; - } - - /* - * 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_verb(4, "Bridge %s: switching from %s technology to %s\n", - bridge->uniqueid, old_technology->name, new_technology->name); - - /* - * Since we are soon going to pass this bridge to a new - * technology we need to NULL out the tech_pvt pointer but - * don't worry as it still exists in dummy_bridge, ditto for the - * old technology. - */ - bridge->tech_pvt = NULL; - bridge->technology = new_technology; - - /* Setup the new bridge technology. */ - ast_debug(1, "Bridge %s: calling %s technology constructor\n", - bridge->uniqueid, new_technology->name); - if (new_technology->create && new_technology->create(bridge)) { - ast_log(LOG_WARNING, "Bridge %s: failed to setup bridge technology %s\n", - bridge->uniqueid, new_technology->name); - bridge->tech_pvt = dummy_bridge.tech_pvt; - bridge->technology = dummy_bridge.technology; - ast_module_unref(new_technology->mod); - return -1; - } - - ast_debug(1, "Bridge %s: calling %s technology stop\n", - dummy_bridge.uniqueid, old_technology->name); - if (old_technology->stop) { - old_technology->stop(&dummy_bridge); - } - - /* - * Move existing channels over to the new technology and - * complete joining any new channels to the bridge. - */ - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - if (!bridge_channel->just_joined) { - /* Take existing channel from the old technology. */ - ast_debug(1, "Bridge %s: %p(%s) is leaving %s technology (dummy)\n", - dummy_bridge.uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan), - old_technology->name); - if (old_technology->leave) { - old_technology->leave(&dummy_bridge, bridge_channel); - } - } - - /* Add any new channels or re-add an existing channel to the bridge. */ - bridge_channel_complete_join(bridge, bridge_channel); - } - - ast_debug(1, "Bridge %s: calling %s technology start\n", - bridge->uniqueid, new_technology->name); - if (new_technology->start && new_technology->start(bridge)) { - ast_log(LOG_WARNING, "Bridge %s: failed to start bridge technology %s\n", - bridge->uniqueid, new_technology->name); - } - - /* - * 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. - */ - if (old_technology->destroy) { - ast_debug(1, "Bridge %s: deferring %s technology destructor\n", - dummy_bridge.uniqueid, old_technology->name); - bridge_queue_action_nodup(bridge, deferred_action); - } else { - ast_debug(1, "Bridge %s: calling %s technology destructor\n", - dummy_bridge.uniqueid, old_technology->name); - ast_module_unref(old_technology->mod); - } - - return 0; -} - -/*! - * \internal - * \brief Bridge channel to check if a BRIDGE_PLAY_SOUND needs to be played. - * \since 12.0.0 - * - * \param bridge_channel What to check. - * - * \return Nothing - */ -static void check_bridge_play_sound(struct ast_bridge_channel *bridge_channel) -{ - const char *play_file; - - ast_channel_lock(bridge_channel->chan); - play_file = pbx_builtin_getvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND"); - if (!ast_strlen_zero(play_file)) { - play_file = ast_strdupa(play_file); - pbx_builtin_setvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND", NULL); - } else { - play_file = NULL; - } - ast_channel_unlock(bridge_channel->chan); - - if (play_file) { - ast_bridge_channel_queue_playfile(bridge_channel, NULL, play_file, NULL); - } -} - -/*! - * \internal - * \brief Check for any BRIDGE_PLAY_SOUND channel variables in the bridge. - * \since 12.0.0 - * - * \param bridge What to operate on. - * - * \note On entry, the bridge is already locked. - * - * \return Nothing - */ -static void check_bridge_play_sounds(struct ast_bridge *bridge) -{ - struct ast_bridge_channel *bridge_channel; - - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - check_bridge_play_sound(bridge_channel); - } -} - -static void update_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid) -{ - pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name); - pbx_builtin_setvar_helper(chan, "BRIDGEPVTCALLID", pvtid); -} - -/*! - * \internal - * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a 2 party bridge. - * \since 12.0.0 - * - * \param c0 Party of the first part. - * \param c1 Party of the second part. - * - * \note On entry, the bridge is already locked. - * \note The bridge is expected to have exactly two parties. - * - * \return Nothing - */ -static void set_bridge_peer_vars_2party(struct ast_channel *c0, struct ast_channel *c1) -{ - const char *c0_name; - const char *c1_name; - const char *c0_pvtid = NULL; - const char *c1_pvtid = NULL; -#define UPDATE_BRIDGE_VARS_GET(chan, name, pvtid) \ - do { \ - name = ast_strdupa(ast_channel_name(chan)); \ - if (ast_channel_tech(chan)->get_pvt_uniqueid) { \ - pvtid = ast_strdupa(ast_channel_tech(chan)->get_pvt_uniqueid(chan)); \ - } \ - } while (0) - - ast_channel_lock(c1); - UPDATE_BRIDGE_VARS_GET(c1, c1_name, c1_pvtid); - ast_channel_unlock(c1); - - ast_channel_lock(c0); - update_bridge_vars_set(c0, c1_name, c1_pvtid); - UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid); - ast_channel_unlock(c0); - - ast_channel_lock(c1); - update_bridge_vars_set(c1, c0_name, c0_pvtid); - ast_channel_unlock(c1); -} - -/*! - * \internal - * \brief Fill the BRIDGEPEER value buffer with a comma separated list of channel names. - * \since 12.0.0 - * - * \param buf Buffer to fill. The caller must guarantee the buffer is large enough. - * \param cur_idx Which index into names[] to skip. - * \param names Channel names to put in the buffer. - * \param num_names Number of names in the array. - * - * \return Nothing - */ -static void fill_bridgepeer_buf(char *buf, unsigned int cur_idx, const char *names[], unsigned int num_names) -{ - int need_separator = 0; - unsigned int idx; - const char *src; - char *pos; - - pos = buf; - for (idx = 0; idx < num_names; ++idx) { - if (idx == cur_idx) { - continue; - } - - if (need_separator) { - *pos++ = ','; - } - need_separator = 1; - - /* Copy name into buffer. */ - src = names[idx]; - while (*src) { - *pos++ = *src++; - } - } - *pos = '\0'; -} - -/*! - * \internal - * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a multi-party bridge. - * \since 12.0.0 - * - * \param bridge What to operate on. - * - * \note On entry, the bridge is already locked. - * \note The bridge is expected to have more than two parties. - * - * \return Nothing - */ -static void set_bridge_peer_vars_multiparty(struct ast_bridge *bridge) -{ -/* - * Set a maximum number of channel names for the BRIDGEPEER - * list. The plus one is for the current channel which is not - * put in the list. - */ -#define MAX_BRIDGEPEER_CHANS (10 + 1) - - unsigned int idx; - unsigned int num_names; - unsigned int len; - const char **names; - char *buf; - struct ast_bridge_channel *bridge_channel; - - /* Get first MAX_BRIDGEPEER_CHANS channel names. */ - num_names = MIN(bridge->num_channels, MAX_BRIDGEPEER_CHANS); - names = ast_alloca(num_names * sizeof(*names)); - idx = 0; - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - if (num_names <= idx) { - break; - } - ast_channel_lock(bridge_channel->chan); - names[idx++] = ast_strdupa(ast_channel_name(bridge_channel->chan)); - ast_channel_unlock(bridge_channel->chan); - } - - /* Determine maximum buf size needed. */ - len = num_names; - for (idx = 0; idx < num_names; ++idx) { - len += strlen(names[idx]); - } - buf = ast_alloca(len); - - /* Set the bridge channel variables. */ - idx = 0; - buf[0] = '\0'; - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - if (idx < num_names) { - fill_bridgepeer_buf(buf, idx, names, num_names); - } - ++idx; - - ast_channel_lock(bridge_channel->chan); - update_bridge_vars_set(bridge_channel->chan, buf, NULL); - ast_channel_unlock(bridge_channel->chan); - } -} - -/*! - * \internal - * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a holding bridge. - * \since 12.0.0 - * - * \param bridge What to operate on. - * - * \note On entry, the bridge is already locked. - * - * \return Nothing - */ -static void set_bridge_peer_vars_holding(struct ast_bridge *bridge) -{ - struct ast_bridge_channel *bridge_channel; - - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - ast_channel_lock(bridge_channel->chan); - update_bridge_vars_set(bridge_channel->chan, NULL, NULL); - ast_channel_unlock(bridge_channel->chan); - } -} - -/*! - * \internal - * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in the bridge. - * \since 12.0.0 - * - * \param bridge What to operate on. - * - * \note On entry, the bridge is already locked. - * - * \return Nothing - */ -static void set_bridge_peer_vars(struct ast_bridge *bridge) -{ - if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_HOLDING) { - set_bridge_peer_vars_holding(bridge); - return; - } - if (bridge->num_channels < 2) { - return; - } - if (bridge->num_channels == 2) { - set_bridge_peer_vars_2party(AST_LIST_FIRST(&bridge->channels)->chan, - AST_LIST_LAST(&bridge->channels)->chan); - } else { - set_bridge_peer_vars_multiparty(bridge); - } -} - -/*! - * \internal - * \brief Notify the bridge that it has been reconfigured. - * \since 12.0.0 - * - * \param bridge Reconfigured bridge. - * \param colp_update Whether to perform COLP updates. - * - * \details - * After a series of bridge_channel_push and - * bridge_channel_pull calls, you need to call this function - * to cause the bridge to complete restructuring for the change - * in the channel makeup of the bridge. - * - * \note On entry, the bridge is already locked. - * - * \return Nothing - */ -static void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update) -{ - if (!bridge->reconfigured) { - return; - } - bridge->reconfigured = 0; - if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART) - && smart_bridge_operation(bridge)) { - /* Smart bridge failed. */ - bridge_dissolve(bridge); - return; - } - bridge_complete_join(bridge); - - if (bridge->dissolved) { - return; - } - check_bridge_play_sounds(bridge); - set_bridge_peer_vars(bridge); - ast_bridge_publish_state(bridge); - - if (colp_update) { - bridge_reconfigured_connected_line_update(bridge); - } -} - -/*! - * \internal - * \brief Suspend a channel from a bridge. - * - * \param bridge_channel Channel to suspend. - * - * \note This function assumes bridge_channel->bridge is locked. - * - * \return Nothing - */ -static void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel) -{ - bridge_channel->suspended = 1; - if (bridge_channel->in_bridge) { - --bridge_channel->bridge->num_active; - } - - /* Get technology bridge threads off of the channel. */ - if (bridge_channel->bridge->technology->suspend) { - bridge_channel->bridge->technology->suspend(bridge_channel->bridge, bridge_channel); - } -} - -/*! - * \internal - * \brief Suspend a channel from a bridge. - * - * \param bridge_channel Channel to suspend. - * - * \return Nothing - */ -static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel) -{ - ast_bridge_channel_lock_bridge(bridge_channel); - bridge_channel_suspend_nolock(bridge_channel); - ast_bridge_unlock(bridge_channel->bridge); -} - -/*! - * \internal - * \brief Unsuspend a channel from a bridge. - * - * \param bridge_channel Channel to unsuspend. - * - * \note This function assumes bridge_channel->bridge is locked. - * - * \return Nothing - */ -static void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel) -{ - bridge_channel->suspended = 0; - if (bridge_channel->in_bridge) { - ++bridge_channel->bridge->num_active; - } - - /* Wake technology bridge threads to take care of channel again. */ - if (bridge_channel->bridge->technology->unsuspend) { - bridge_channel->bridge->technology->unsuspend(bridge_channel->bridge, bridge_channel); - } - - /* Wake suspended channel. */ - ast_bridge_channel_lock(bridge_channel); - ast_cond_signal(&bridge_channel->cond); - ast_bridge_channel_unlock(bridge_channel); -} - -/*! - * \internal - * \brief Unsuspend a channel from a bridge. - * - * \param bridge_channel Channel to unsuspend. - * - * \return Nothing - */ -static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel) -{ - ast_bridge_channel_lock_bridge(bridge_channel); - bridge_channel_unsuspend_nolock(bridge_channel); - ast_bridge_unlock(bridge_channel->bridge); -} - -/*! \brief Internal function that activates interval hooks on a bridge channel */ -static void bridge_channel_interval(struct ast_bridge_channel *bridge_channel) -{ - struct ast_bridge_hook *hook; - struct timeval start; + /* Determine new bridge technology capabilities needed. */ + if (2 < bridge->num_channels) { + new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX; + new_capabilities &= bridge->allowed_capabilities; + } else { + new_capabilities = AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX; + new_capabilities &= bridge->allowed_capabilities; + if (!new_capabilities + && (bridge->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)) { + /* Allow switching between different multimix bridge technologies. */ + new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX; + } + } - ast_heap_wrlock(bridge_channel->features->interval_hooks); - start = ast_tvnow(); - while ((hook = ast_heap_peek(bridge_channel->features->interval_hooks, 1))) { - int interval; - unsigned int execution_time; + /* Find a bridge technology to satisfy the new capabilities. */ + new_technology = find_best_technology(new_capabilities, bridge); + if (!new_technology) { + int is_compatible = 0; - if (ast_tvdiff_ms(hook->parms.timer.trip_time, start) > 0) { - ast_debug(1, "Hook %p on %p(%s) wants to happen in the future, stopping our traversal\n", - hook, bridge_channel, ast_channel_name(bridge_channel->chan)); - break; - } - ao2_ref(hook, +1); - ast_heap_unlock(bridge_channel->features->interval_hooks); - - ast_debug(1, "Executing hook %p on %p(%s)\n", - hook, bridge_channel, ast_channel_name(bridge_channel->chan)); - interval = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); - - ast_heap_wrlock(bridge_channel->features->interval_hooks); - if (ast_heap_peek(bridge_channel->features->interval_hooks, - hook->parms.timer.heap_index) != hook - || !ast_heap_remove(bridge_channel->features->interval_hooks, hook)) { - /* Interval hook is already removed from the bridge_channel. */ - ao2_ref(hook, -1); - continue; + if (old_technology->compatible) { + is_compatible = old_technology->compatible(bridge); + } else if (old_technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) { + is_compatible = 1; + } else if (bridge->num_channels <= 2 + && (old_technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX)) { + is_compatible = 1; } - ao2_ref(hook, -1); - if (interval < 0) { - ast_debug(1, "Removed interval hook %p from %p(%s)\n", - hook, bridge_channel, ast_channel_name(bridge_channel->chan)); - ao2_ref(hook, -1); - continue; - } - if (interval) { - /* Set new interval for the hook. */ - hook->parms.timer.interval = interval; + if (is_compatible) { + ast_debug(1, "Bridge %s could not get a new technology, staying with old technology.\n", + bridge->uniqueid); + return 0; } + ast_log(LOG_WARNING, "Bridge %s has no technology available to support it.\n", + bridge->uniqueid); + return -1; + } + if (new_technology == old_technology) { + ast_debug(1, "Bridge %s is already using the new technology.\n", + bridge->uniqueid); + ast_module_unref(old_technology->mod); + return 0; + } - ast_debug(1, "Updating interval hook %p with interval %u on %p(%s)\n", - hook, hook->parms.timer.interval, bridge_channel, - ast_channel_name(bridge_channel->chan)); + ast_copy_string(dummy_bridge.uniqueid, bridge->uniqueid, sizeof(dummy_bridge.uniqueid)); - /* resetting start */ - start = ast_tvnow(); + if (old_technology->destroy) { + struct tech_deferred_destroy deferred_tech_destroy = { + .tech = dummy_bridge.technology, + .tech_pvt = dummy_bridge.tech_pvt, + }; + struct ast_frame action = { + .frametype = AST_FRAME_BRIDGE_ACTION, + .subclass.integer = AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY, + .data.ptr = &deferred_tech_destroy, + .datalen = sizeof(deferred_tech_destroy), + }; /* - * Resetup the interval hook for the next interval. We may need - * to skip over any missed intervals because the hook was - * delayed or took too long. + * We need to defer the bridge technology destroy callback + * because we have the bridge locked. */ - execution_time = ast_tvdiff_ms(start, hook->parms.timer.trip_time); - while (hook->parms.timer.interval < execution_time) { - execution_time -= hook->parms.timer.interval; - } - hook->parms.timer.trip_time = ast_tvadd(start, ast_samp2tv(hook->parms.timer.interval - execution_time, 1000)); - hook->parms.timer.seqno = ast_atomic_fetchadd_int((int *) &bridge_channel->features->interval_sequence, +1); - - if (ast_heap_push(bridge_channel->features->interval_hooks, hook)) { - /* Could not push the hook back onto the heap. */ - ao2_ref(hook, -1); + deferred_action = ast_frdup(&action); + if (!deferred_action) { + ast_module_unref(new_technology->mod); + return -1; } + } else { + deferred_action = NULL; } - ast_heap_unlock(bridge_channel->features->interval_hooks); -} -static int bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf) -{ - return ast_bridge_channel_write_action_data(bridge_channel, - AST_BRIDGE_ACTION_DTMF_STREAM, dtmf, strlen(dtmf) + 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_verb(4, "Bridge %s: switching from %s technology to %s\n", + bridge->uniqueid, old_technology->name, new_technology->name); -/*! - * \brief Internal function that executes a feature on a bridge channel - * \note Neither the bridge nor the bridge_channel locks should be held when entering - * this function. - */ -static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel) -{ - struct ast_bridge_features *features = bridge_channel->features; - struct ast_bridge_hook *hook = NULL; - char dtmf[MAXIMUM_DTMF_FEATURE_STRING] = ""; - size_t dtmf_len = 0; - unsigned int digit_timeout; - RAII_VAR(struct ast_features_general_config *, gen_cfg, NULL, ao2_cleanup); + /* + * Since we are soon going to pass this bridge to a new + * technology we need to NULL out the tech_pvt pointer but + * don't worry as it still exists in dummy_bridge, ditto for the + * old technology. + */ + bridge->tech_pvt = NULL; + bridge->technology = new_technology; - ast_channel_lock(bridge_channel->chan); - gen_cfg = ast_get_chan_features_general_config(bridge_channel->chan); - if (!gen_cfg) { - ast_log(LOG_ERROR, "Unable to retrieve features configuration.\n"); - ast_channel_unlock(bridge_channel->chan); - return; + /* Setup the new bridge technology. */ + ast_debug(1, "Bridge %s: calling %s technology constructor\n", + bridge->uniqueid, new_technology->name); + if (new_technology->create && new_technology->create(bridge)) { + ast_log(LOG_WARNING, "Bridge %s: failed to setup bridge technology %s\n", + bridge->uniqueid, new_technology->name); + bridge->tech_pvt = dummy_bridge.tech_pvt; + bridge->technology = dummy_bridge.technology; + ast_module_unref(new_technology->mod); + return -1; } - digit_timeout = gen_cfg->featuredigittimeout; - ast_channel_unlock(bridge_channel->chan); - - /* The channel is now under our control and we don't really want any begin frames to do our DTMF matching so disable 'em at the core level */ - ast_set_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_END_DTMF_ONLY); - - /* Wait for DTMF on the channel and put it into a buffer. If the buffer matches any feature hook execute the hook. */ - do { - int res; - - /* If the above timed out simply exit */ - res = ast_waitfordigit(bridge_channel->chan, digit_timeout); - if (!res) { - ast_debug(1, "DTMF feature string collection on %p(%s) timed out\n", - bridge_channel, ast_channel_name(bridge_channel->chan)); - break; - } - if (res < 0) { - ast_debug(1, "DTMF feature string collection failed on %p(%s) for some reason\n", - bridge_channel, ast_channel_name(bridge_channel->chan)); - break; - } - -/* BUGBUG need to record the duration of DTMF digits so when the string is played back, they are reproduced. */ - /* Add the above DTMF into the DTMF string so we can do our matching */ - dtmf[dtmf_len++] = res; - ast_debug(1, "DTMF feature string on %p(%s) is now '%s'\n", - bridge_channel, ast_channel_name(bridge_channel->chan), dtmf); - - /* See if a DTMF feature hook matches or can match */ - hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY); - if (!hook) { - ast_debug(1, "No DTMF feature hooks on %p(%s) match '%s'\n", - bridge_channel, ast_channel_name(bridge_channel->chan), dtmf); - break; - } - if (strlen(hook->parms.dtmf.code) == dtmf_len) { - ast_debug(1, "DTMF feature hook %p matched DTMF string '%s' on %p(%s)\n", - hook, dtmf, bridge_channel, ast_channel_name(bridge_channel->chan)); - break; - } - ao2_ref(hook, -1); - hook = NULL; - - /* Stop if we have reached the maximum length of a DTMF feature string. */ - } while (dtmf_len < ARRAY_LEN(dtmf) - 1); - /* Since we are done bringing DTMF in return to using both begin and end frames */ - ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_END_DTMF_ONLY); - - /* If a hook was actually matched execute it on this channel, otherwise stream up the DTMF to the other channels */ - if (hook) { - int remove_me; + ast_debug(1, "Bridge %s: calling %s technology stop\n", + dummy_bridge.uniqueid, old_technology->name); + if (old_technology->stop) { + old_technology->stop(&dummy_bridge); + } - remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); - if (remove_me) { - ast_debug(1, "DTMF hook %p is being removed from %p(%s)\n", - hook, bridge_channel, ast_channel_name(bridge_channel->chan)); - ao2_unlink(features->dtmf_hooks, hook); + /* + * Move existing channels over to the new technology and + * complete joining any new channels to the bridge. + */ + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + if (!bridge_channel->just_joined) { + /* Take existing channel from the old technology. */ + ast_debug(1, "Bridge %s: %p(%s) is leaving %s technology (dummy)\n", + dummy_bridge.uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan), + old_technology->name); + if (old_technology->leave) { + old_technology->leave(&dummy_bridge, bridge_channel); + } } - ao2_ref(hook, -1); - /* - * If we are handing the channel off to an external hook for - * ownership, we are not guaranteed what kind of state it will - * come back in. If the channel hungup, we need to detect that - * here if the hook did not already change the state. - */ - if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) { - bridge_handle_hangup(bridge_channel); - } - } else if (features->dtmf_passthrough) { - bridge_channel_write_dtmf_stream(bridge_channel, dtmf); + /* Add any new channels or re-add an existing channel to the bridge. */ + bridge_channel_complete_join(bridge, bridge_channel); } -} - -static void bridge_channel_talking(struct ast_bridge_channel *bridge_channel, int talking) -{ - struct ast_bridge_features *features = bridge_channel->features; - if (features->talker_cb) { - features->talker_cb(bridge_channel, features->talker_pvt_data, talking); + ast_debug(1, "Bridge %s: calling %s technology start\n", + bridge->uniqueid, new_technology->name); + if (new_technology->start && new_technology->start(bridge)) { + ast_log(LOG_WARNING, "Bridge %s: failed to start bridge technology %s\n", + bridge->uniqueid, new_technology->name); } -} - -/*! \brief Internal function that plays back DTMF on a bridge channel */ -static void bridge_channel_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf) -{ - ast_debug(1, "Playing DTMF stream '%s' out to %p(%s)\n", - dtmf, bridge_channel, ast_channel_name(bridge_channel->chan)); - ast_dtmf_stream(bridge_channel->chan, NULL, dtmf, 0, 0); -} -struct blind_transfer_data { - char exten[AST_MAX_EXTENSION]; - char context[AST_MAX_CONTEXT]; -}; + /* + * 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. + */ + if (old_technology->destroy) { + ast_debug(1, "Bridge %s: deferring %s technology destructor\n", + dummy_bridge.uniqueid, old_technology->name); + bridge_queue_action_nodup(bridge, deferred_action); + } else { + ast_debug(1, "Bridge %s: calling %s technology destructor\n", + dummy_bridge.uniqueid, old_technology->name); + ast_module_unref(old_technology->mod); + } -static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_channel, - struct blind_transfer_data *blind_data) -{ - ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1); - bridge_handle_hangup(bridge_channel); + return 0; } -static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *data) +/*! + * \internal + * \brief Bridge channel to check if a BRIDGE_PLAY_SOUND needs to be played. + * \since 12.0.0 + * + * \param bridge_channel What to check. + * + * \return Nothing + */ +static void check_bridge_play_sound(struct ast_bridge_channel *bridge_channel) { - RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup); - struct ast_party_connected_line connected_target; - unsigned char connected_line_data[1024]; - int payload_size; - - ast_party_connected_line_init(&connected_target); - - ast_channel_lock(chan_target); - ast_party_connected_line_copy(&connected_target, ast_channel_connected(chan_target)); - ast_channel_unlock(chan_target); - ast_party_id_reset(&connected_target.priv); + const char *play_file; - if (ast_channel_move(chan_target, chan_bridged)) { - ast_softhangup(chan_target, AST_SOFTHANGUP_DEV); - ast_party_connected_line_free(&connected_target); - return; + ast_channel_lock(bridge_channel->chan); + play_file = pbx_builtin_getvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND"); + if (!ast_strlen_zero(play_file)) { + play_file = ast_strdupa(play_file); + pbx_builtin_setvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND", NULL); + } else { + play_file = NULL; } + ast_channel_unlock(bridge_channel->chan); - if ((payload_size = ast_connected_line_build_data(connected_line_data, - sizeof(connected_line_data), &connected_target, NULL)) != -1) { - struct ast_control_read_action_payload *frame_payload; - int frame_size; - - frame_size = payload_size + sizeof(*frame_payload); - frame_payload = ast_alloca(frame_size); - frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO; - frame_payload->payload_size = payload_size; - memcpy(frame_payload->payload, connected_line_data, payload_size); - ast_queue_control_data(chan_target, AST_CONTROL_READ_ACTION, frame_payload, frame_size); + if (play_file) { + ast_bridge_channel_queue_playfile(bridge_channel, NULL, play_file, NULL); } - - ast_party_connected_line_free(&connected_target); } -static void after_bridge_move_channel_fail(enum ast_after_bridge_cb_reason reason, void *data) +/*! + * \internal + * \brief Check for any BRIDGE_PLAY_SOUND channel variables in the bridge. + * \since 12.0.0 + * + * \param bridge What to operate on. + * + * \note On entry, the bridge is already locked. + * + * \return Nothing + */ +static void check_bridge_play_sounds(struct ast_bridge *bridge) { - RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup); + struct ast_bridge_channel *bridge_channel; - ast_log(LOG_WARNING, "Unable to complete transfer: %s\n", - ast_after_bridge_cb_reason_string(reason)); - ast_softhangup(chan_target, AST_SOFTHANGUP_DEV); + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + check_bridge_play_sound(bridge_channel); + } } -static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_channel, - const char *target_chan_name) +static void update_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid) { - RAII_VAR(struct ast_channel *, chan_target, NULL, ao2_cleanup); - RAII_VAR(struct ast_channel *, chan_bridged, NULL, ao2_cleanup); - - chan_target = ast_channel_get_by_name(target_chan_name); - if (!chan_target) { - /* Dang, it disappeared somehow */ - bridge_handle_hangup(bridge_channel); - return; - } - - ast_bridge_channel_lock(bridge_channel); - chan_bridged = bridge_channel->chan; - ast_assert(chan_bridged != NULL); - ao2_ref(chan_bridged, +1); - ast_bridge_channel_unlock(bridge_channel); - - if (ast_after_bridge_callback_set(chan_bridged, after_bridge_move_channel, - after_bridge_move_channel_fail, ast_channel_ref(chan_target))) { - ast_softhangup(chan_target, AST_SOFTHANGUP_DEV); - - /* Release the ref we tried to pass to ast_after_bridge_callback_set(). */ - ast_channel_unref(chan_target); - } - bridge_handle_hangup(bridge_channel); + pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name); + pbx_builtin_setvar_helper(chan, "BRIDGEPVTCALLID", pvtid); } /*! * \internal - * \brief Handle bridge channel bridge action frame. + * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a 2 party bridge. * \since 12.0.0 * - * \param bridge_channel Channel to execute the action on. - * \param action What to do. + * \param c0 Party of the first part. + * \param c1 Party of the second part. + * + * \note On entry, the bridge is already locked. + * \note The bridge is expected to have exactly two parties. * * \return Nothing */ -static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_channel, struct ast_frame *action) +static void set_bridge_peer_vars_2party(struct ast_channel *c0, struct ast_channel *c1) { - switch (action->subclass.integer) { - case AST_BRIDGE_ACTION_INTERVAL: - bridge_channel_suspend(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_interval(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_unsuspend(bridge_channel); - break; - case AST_BRIDGE_ACTION_FEATURE: - bridge_channel_suspend(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_feature(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_unsuspend(bridge_channel); - break; - case AST_BRIDGE_ACTION_DTMF_STREAM: - bridge_channel_suspend(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_dtmf_stream(bridge_channel, action->data.ptr); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_unsuspend(bridge_channel); - break; - case AST_BRIDGE_ACTION_TALKING_START: - case AST_BRIDGE_ACTION_TALKING_STOP: - bridge_channel_talking(bridge_channel, - action->subclass.integer == AST_BRIDGE_ACTION_TALKING_START); - break; - case AST_BRIDGE_ACTION_PLAY_FILE: - bridge_channel_suspend(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_playfile(bridge_channel, action->data.ptr); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_unsuspend(bridge_channel); - break; - case AST_BRIDGE_ACTION_RUN_APP: - bridge_channel_suspend(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_run_app(bridge_channel, action->data.ptr); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_unsuspend(bridge_channel); - break; - case AST_BRIDGE_ACTION_CALLBACK: - bridge_channel_suspend(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_do_callback(bridge_channel, action->data.ptr); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_unsuspend(bridge_channel); - break; - case AST_BRIDGE_ACTION_PARK: - bridge_channel_suspend(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_park(bridge_channel, action->data.ptr); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_unsuspend(bridge_channel); - break; - case AST_BRIDGE_ACTION_BLIND_TRANSFER: - bridge_channel_blind_transfer(bridge_channel, action->data.ptr); - break; - case AST_BRIDGE_ACTION_ATTENDED_TRANSFER: - bridge_channel_attended_transfer(bridge_channel, action->data.ptr); - break; - default: - break; - } + const char *c0_name; + const char *c1_name; + const char *c0_pvtid = NULL; + const char *c1_pvtid = NULL; +#define UPDATE_BRIDGE_VARS_GET(chan, name, pvtid) \ + do { \ + name = ast_strdupa(ast_channel_name(chan)); \ + if (ast_channel_tech(chan)->get_pvt_uniqueid) { \ + pvtid = ast_strdupa(ast_channel_tech(chan)->get_pvt_uniqueid(chan)); \ + } \ + } while (0) + + ast_channel_lock(c1); + UPDATE_BRIDGE_VARS_GET(c1, c1_name, c1_pvtid); + ast_channel_unlock(c1); + + ast_channel_lock(c0); + update_bridge_vars_set(c0, c1_name, c1_pvtid); + UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid); + ast_channel_unlock(c0); + + ast_channel_lock(c1); + update_bridge_vars_set(c1, c0_name, c0_pvtid); + ast_channel_unlock(c1); } /*! * \internal - * \brief Handle bridge channel control frame action. + * \brief Fill the BRIDGEPEER value buffer with a comma separated list of channel names. * \since 12.0.0 * - * \param bridge_channel Channel to execute the control frame action on. - * \param fr Control frame to handle. + * \param buf Buffer to fill. The caller must guarantee the buffer is large enough. + * \param cur_idx Which index into names[] to skip. + * \param names Channel names to put in the buffer. + * \param num_names Number of names in the array. * * \return Nothing */ -static void bridge_channel_handle_control(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr) +static void fill_bridgepeer_buf(char *buf, unsigned int cur_idx, const char *names[], unsigned int num_names) { - struct ast_channel *chan; - struct ast_option_header *aoh; - int is_caller; - int intercept_failed; + int need_separator = 0; + unsigned int idx; + const char *src; + char *pos; - chan = bridge_channel->chan; - switch (fr->subclass.integer) { - case AST_CONTROL_REDIRECTING: - is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING); - bridge_channel_suspend(bridge_channel); - intercept_failed = ast_channel_redirecting_sub(NULL, chan, fr, 1) - && ast_channel_redirecting_macro(NULL, chan, fr, is_caller, 1); - bridge_channel_unsuspend(bridge_channel); - if (intercept_failed) { - ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen); - } - break; - case AST_CONTROL_CONNECTED_LINE: - is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING); - bridge_channel_suspend(bridge_channel); - intercept_failed = ast_channel_connected_line_sub(NULL, chan, fr, 1) - && ast_channel_connected_line_macro(NULL, chan, fr, is_caller, 1); - bridge_channel_unsuspend(bridge_channel); - if (intercept_failed) { - ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen); + pos = buf; + for (idx = 0; idx < num_names; ++idx) { + if (idx == cur_idx) { + continue; } - break; - case AST_CONTROL_HOLD: - case AST_CONTROL_UNHOLD: -/* - * BUGBUG bridge_channels should remember sending/receiving an outstanding HOLD to/from the bridge - * - * When the sending channel is pulled from the bridge it needs to write into the bridge an UNHOLD before being pulled. - * When the receiving channel is pulled from the bridge it needs to generate its own UNHOLD. - * Something similar needs to be done for DTMF begin/end. - */ - case AST_CONTROL_VIDUPDATE: - case AST_CONTROL_SRCUPDATE: - case AST_CONTROL_SRCCHANGE: - case AST_CONTROL_T38_PARAMETERS: -/* BUGBUG may have to do something with a jitter buffer for these. */ - ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen); - break; - case AST_CONTROL_OPTION: - /* - * Forward option Requests, but only ones we know are safe These - * are ONLY sent by chan_iax2 and I'm not convinced that they - * are useful. I haven't deleted them entirely because I just am - * not sure of the ramifications of removing them. - */ - aoh = fr->data.ptr; - if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) { - switch (ntohs(aoh->option)) { - case AST_OPTION_TONE_VERIFY: - case AST_OPTION_TDD: - case AST_OPTION_RELAXDTMF: - case AST_OPTION_AUDIO_MODE: - case AST_OPTION_DIGIT_DETECT: - case AST_OPTION_FAX_DETECT: - ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, - fr->datalen - sizeof(*aoh), 0); - break; - default: - break; - } + + if (need_separator) { + *pos++ = ','; } - break; - case AST_CONTROL_ANSWER: - if (ast_channel_state(chan) != AST_STATE_UP) { - ast_answer(chan); - } else { - ast_indicate(chan, -1); + need_separator = 1; + + /* Copy name into buffer. */ + src = names[idx]; + while (*src) { + *pos++ = *src++; } - break; - default: - ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen); - break; } + *pos = '\0'; } /*! * \internal - * \brief Handle bridge channel write frame to channel. + * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a multi-party bridge. * \since 12.0.0 * - * \param bridge_channel Channel to write outgoing frame. + * \param bridge What to operate on. + * + * \note On entry, the bridge is already locked. + * \note The bridge is expected to have more than two parties. * * \return Nothing */ -static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channel) +static void set_bridge_peer_vars_multiparty(struct ast_bridge *bridge) { - struct ast_frame *fr; - char nudge; +/* + * Set a maximum number of channel names for the BRIDGEPEER + * list. The plus one is for the current channel which is not + * put in the list. + */ +#define MAX_BRIDGEPEER_CHANS (10 + 1) - ast_bridge_channel_lock(bridge_channel); - if (read(bridge_channel->alert_pipe[0], &nudge, sizeof(nudge)) < 0) { - if (errno != EINTR && errno != EAGAIN) { - ast_log(LOG_WARNING, "read() failed for alert pipe on %p(%s): %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), strerror(errno)); + unsigned int idx; + unsigned int num_names; + unsigned int len; + const char **names; + char *buf; + struct ast_bridge_channel *bridge_channel; + + /* Get first MAX_BRIDGEPEER_CHANS channel names. */ + num_names = MIN(bridge->num_channels, MAX_BRIDGEPEER_CHANS); + names = ast_alloca(num_names * sizeof(*names)); + idx = 0; + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + if (num_names <= idx) { + break; } + ast_channel_lock(bridge_channel->chan); + names[idx++] = ast_strdupa(ast_channel_name(bridge_channel->chan)); + ast_channel_unlock(bridge_channel->chan); } - fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list); - ast_bridge_channel_unlock(bridge_channel); - if (!fr) { - return; - } - switch (fr->frametype) { - case AST_FRAME_BRIDGE_ACTION: - bridge_channel_handle_action(bridge_channel, fr); - break; - case AST_FRAME_CONTROL: - bridge_channel_handle_control(bridge_channel, fr); - break; - case AST_FRAME_NULL: - break; - default: - /* Write the frame to the channel. */ - bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_SIMPLE; - ast_write(bridge_channel->chan, fr); - break; + + /* Determine maximum buf size needed. */ + len = num_names; + for (idx = 0; idx < num_names; ++idx) { + len += strlen(names[idx]); } - ast_frfree(fr); -} + buf = ast_alloca(len); -/*! - * \internal - * \brief Handle bridge channel interval expiration. - * \since 12.0.0 - * - * \param bridge_channel Channel to check interval on. - * - * \return Nothing - */ -static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_channel) -{ - struct ast_timer *interval_timer; - - interval_timer = bridge_channel->features->interval_timer; - if (interval_timer) { - if (ast_wait_for_input(ast_timer_fd(interval_timer), 0) == 1) { - ast_timer_ack(interval_timer, 1); - if (bridge_channel_interval_ready(bridge_channel)) { -/* BUGBUG since this is now only run by the channel thread, there is no need to queue the action once this intervals become a first class wait item in bridge_channel_wait(). */ - struct ast_frame interval_action = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = AST_BRIDGE_ACTION_INTERVAL, - }; - - ast_bridge_channel_queue_frame(bridge_channel, &interval_action); - } + /* Set the bridge channel variables. */ + idx = 0; + buf[0] = '\0'; + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + if (idx < num_names) { + fill_bridgepeer_buf(buf, idx, names, num_names); } + ++idx; + + ast_channel_lock(bridge_channel->chan); + update_bridge_vars_set(bridge_channel->chan, buf, NULL); + ast_channel_unlock(bridge_channel->chan); } } /*! * \internal - * \brief Wait for something to happen on the bridge channel and handle it. + * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a holding bridge. * \since 12.0.0 * - * \param bridge_channel Channel to wait. + * \param bridge What to operate on. * - * \note Each channel does writing/reading in their own thread. + * \note On entry, the bridge is already locked. * * \return Nothing */ -static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel) +static void set_bridge_peer_vars_holding(struct ast_bridge *bridge) { - int ms = -1; - int outfd; - struct ast_channel *chan; + struct ast_bridge_channel *bridge_channel; - /* Wait for data to either come from the channel or us to be signaled */ - ast_bridge_channel_lock(bridge_channel); - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { - } else if (bridge_channel->suspended) { -/* BUGBUG the external party use of suspended will go away as will these references because this is the bridge channel thread */ - ast_debug(1, "Bridge %s: %p(%s) is going into a signal wait\n", - bridge_channel->bridge->uniqueid, bridge_channel, - ast_channel_name(bridge_channel->chan)); - ast_cond_wait(&bridge_channel->cond, ao2_object_get_lockaddr(bridge_channel)); - } else { - ast_debug(10, "Bridge %s: %p(%s) is going into a waitfor\n", - bridge_channel->bridge->uniqueid, bridge_channel, - ast_channel_name(bridge_channel->chan)); - bridge_channel->waiting = 1; - ast_bridge_channel_unlock(bridge_channel); - outfd = -1; -/* BUGBUG need to make the next expiring active interval setup ms timeout rather than holding up the chan reads. */ - chan = ast_waitfor_nandfds(&bridge_channel->chan, 1, - &bridge_channel->alert_pipe[0], 1, NULL, &outfd, &ms); - bridge_channel->waiting = 0; - if (ast_channel_softhangup_internal_flag(bridge_channel->chan) & AST_SOFTHANGUP_UNBRIDGE) { - ast_channel_clear_softhangup(bridge_channel->chan, AST_SOFTHANGUP_UNBRIDGE); - ast_bridge_channel_lock_bridge(bridge_channel); - bridge_channel->bridge->reconfigured = 1; - bridge_reconfigured(bridge_channel->bridge, 0); - ast_bridge_unlock(bridge_channel->bridge); - } - ast_bridge_channel_lock(bridge_channel); - bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_FRAME; - ast_bridge_channel_unlock(bridge_channel); - if (!bridge_channel->suspended - && bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { - if (chan) { - bridge_channel_handle_interval(bridge_channel); - bridge_handle_trip(bridge_channel); - } else if (-1 < outfd) { - bridge_channel_handle_write(bridge_channel); - } - } - bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_IDLE; - return; + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + ast_channel_lock(bridge_channel->chan); + update_bridge_vars_set(bridge_channel->chan, NULL, NULL); + ast_channel_unlock(bridge_channel->chan); } - ast_bridge_channel_unlock(bridge_channel); } /*! * \internal - * \brief Handle bridge channel join event. + * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in the bridge. * \since 12.0.0 * - * \param bridge_channel Which channel is joining. + * \param bridge What to operate on. + * + * \note On entry, the bridge is already locked. * * \return Nothing */ -static void bridge_channel_handle_join(struct ast_bridge_channel *bridge_channel) +static void set_bridge_peer_vars(struct ast_bridge *bridge) { - struct ast_bridge_features *features = bridge_channel->features; - struct ast_bridge_hook *hook; - struct ao2_iterator iter; - - /* Run any join hooks. */ - iter = ao2_iterator_init(features->join_hooks, AO2_ITERATOR_UNLINK); - hook = ao2_iterator_next(&iter); - if (hook) { - bridge_channel_suspend(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - do { - hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); - ao2_ref(hook, -1); - } while ((hook = ao2_iterator_next(&iter))); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_unsuspend(bridge_channel); + if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_HOLDING) { + set_bridge_peer_vars_holding(bridge); + return; + } + if (bridge->num_channels < 2) { + return; + } + if (bridge->num_channels == 2) { + set_bridge_peer_vars_2party(AST_LIST_FIRST(&bridge->channels)->chan, + AST_LIST_LAST(&bridge->channels)->chan); + } else { + set_bridge_peer_vars_multiparty(bridge); } - ao2_iterator_destroy(&iter); } /*! * \internal - * \brief Handle bridge channel leave event. + * \brief Notify the bridge that it has been reconfigured. * \since 12.0.0 * - * \param bridge_channel Which channel is leaving. + * \param bridge Reconfigured bridge. + * \param colp_update Whether to perform COLP updates. + * + * \details + * After a series of bridge_channel_push and + * bridge_channel_pull calls, you need to call this function + * to cause the bridge to complete restructuring for the change + * in the channel makeup of the bridge. + * + * \note On entry, the bridge is already locked. * * \return Nothing */ -static void bridge_channel_handle_leave(struct ast_bridge_channel *bridge_channel) -{ - struct ast_bridge_features *features = bridge_channel->features; - struct ast_bridge_hook *hook; - struct ao2_iterator iter; - - /* Run any leave hooks. */ - iter = ao2_iterator_init(features->leave_hooks, AO2_ITERATOR_UNLINK); - hook = ao2_iterator_next(&iter); - if (hook) { - bridge_channel_suspend(bridge_channel); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - do { - hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); - ao2_ref(hook, -1); - } while ((hook = ao2_iterator_next(&iter))); - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_unsuspend(bridge_channel); - } - ao2_iterator_destroy(&iter); -} - -/*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */ -static void bridge_channel_join(struct ast_bridge_channel *bridge_channel) +void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update) { - ast_format_copy(&bridge_channel->read_format, ast_channel_readformat(bridge_channel->chan)); - ast_format_copy(&bridge_channel->write_format, ast_channel_writeformat(bridge_channel->chan)); - - ast_debug(1, "Bridge %s: %p(%s) is joining\n", - bridge_channel->bridge->uniqueid, - bridge_channel, ast_channel_name(bridge_channel->chan)); - - /* - * Get "in the bridge" before pushing the channel for any - * masquerades on the channel to happen before bridging. - */ - ast_channel_lock(bridge_channel->chan); - ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge); - ast_channel_unlock(bridge_channel->chan); - - /* Add the jitterbuffer if the channel requires it */ - ast_jb_enable_for_channel(bridge_channel->chan); - - /* - * Directly locking the bridge is safe here because nobody else - * knows about this bridge_channel yet. - */ - ast_bridge_lock(bridge_channel->bridge); - - if (!bridge_channel->bridge->callid) { - bridge_channel->bridge->callid = ast_read_threadstorage_callid(); - } - - if (bridge_channel_push(bridge_channel)) { - ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); + if (!bridge->reconfigured) { + return; } - bridge_reconfigured(bridge_channel->bridge, 1); - - if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { - /* - * Indicate a source change since this channel is entering the - * bridge system only if the bridge technology is not MULTIMIX - * capable. The MULTIMIX technology has already done it. - */ - if (!(bridge_channel->bridge->technology->capabilities - & AST_BRIDGE_CAPABILITY_MULTIMIX)) { - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE); - } - - ast_bridge_unlock(bridge_channel->bridge); - bridge_channel_handle_join(bridge_channel); - while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { - /* Wait for something to do. */ - bridge_channel_wait(bridge_channel); - } - bridge_channel_handle_leave(bridge_channel); - ast_bridge_channel_lock_bridge(bridge_channel); + bridge->reconfigured = 0; + if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART) + && smart_bridge_operation(bridge)) { + /* Smart bridge failed. */ + bridge_dissolve(bridge); + return; } + bridge_complete_join(bridge); - bridge_channel_pull(bridge_channel); - bridge_reconfigured(bridge_channel->bridge, 1); - - ast_bridge_unlock(bridge_channel->bridge); - - /* Indicate a source change since this channel is leaving the bridge system. */ - ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE); - -/* BUGBUG Revisit in regards to moving channels between bridges and local channel optimization. */ -/* BUGBUG This is where outgoing HOLD/UNHOLD memory should write UNHOLD to channel. */ - /* Complete any partial DTMF digit before exiting the bridge. */ - if (ast_channel_sending_dtmf_digit(bridge_channel->chan)) { - ast_bridge_end_dtmf(bridge_channel->chan, - ast_channel_sending_dtmf_digit(bridge_channel->chan), - ast_channel_sending_dtmf_tv(bridge_channel->chan), "bridge end"); + if (bridge->dissolved) { + return; } + check_bridge_play_sounds(bridge); + set_bridge_peer_vars(bridge); + ast_bridge_publish_state(bridge); - /* - * Wait for any dual redirect to complete. - * - * Must be done while "still in the bridge" for ast_async_goto() - * to work right. - */ - while (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT)) { - sched_yield(); + if (colp_update) { + bridge_reconfigured_connected_line_update(bridge); } - ast_channel_lock(bridge_channel->chan); - ast_channel_internal_bridge_set(bridge_channel->chan, NULL); - ast_channel_unlock(bridge_channel->chan); - - ast_bridge_channel_restore_formats(bridge_channel); } /*! @@ -3872,7 +2125,7 @@ void ast_bridge_notify_masquerade(struct ast_channel *chan) ast_bridge_channel_lock_bridge(bridge_channel); bridge = bridge_channel->bridge; - if (bridge_channel == find_bridge_channel(bridge, chan)) { + if (bridge_channel == bridge_find_channel(bridge, chan)) { /* BUGBUG this needs more work. The channels need to be made compatible again if the formats change. The bridge_channel thread needs to monitor for this case. */ /* The channel we want to notify is still in a bridge. */ bridge->v_table->notify_masquerade(bridge, bridge_channel); @@ -4120,7 +2373,7 @@ int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan) ast_bridge_lock(bridge); /* Try to find the channel that we want to remove */ - if (!(bridge_channel = find_bridge_channel(bridge, chan))) { + if (!(bridge_channel = bridge_find_channel(bridge, chan))) { ast_bridge_unlock(bridge); return -1; } @@ -4157,7 +2410,7 @@ static void bridge_channel_change_bridge(struct ast_bridge_channel *bridge_chann ao2_ref(old_bridge, -1); } -void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick, +void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick, unsigned int optimized) { struct ast_bridge_channel *bridge_channel; @@ -4382,9 +2635,9 @@ static int bridge_merge_locked(struct ast_bridge *dst_bridge, struct ast_bridge kick_them = ast_alloca(num_kick * sizeof(*kick_them)); for (idx = 0; idx < num_kick; ++idx) { - kick_them[num_to_kick] = find_bridge_channel(merge.src, kick_me[idx]); + kick_them[num_to_kick] = bridge_find_channel(merge.src, kick_me[idx]); if (!kick_them[num_to_kick]) { - kick_them[num_to_kick] = find_bridge_channel(merge.dest, kick_me[idx]); + kick_them[num_to_kick] = bridge_find_channel(merge.dest, kick_me[idx]); } if (kick_them[num_to_kick]) { ++num_to_kick; @@ -4398,7 +2651,7 @@ static int bridge_merge_locked(struct ast_bridge *dst_bridge, struct ast_bridge } } - bridge_merge_do(merge.dest, merge.src, kick_them, num_kick, 0); + bridge_do_merge(merge.dest, merge.src, kick_them, num_kick, 0); return 0; } @@ -4416,7 +2669,7 @@ int ast_bridge_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg return res; } -int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery, +int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery, unsigned int optimized) { struct ast_bridge *orig_bridge; @@ -4508,7 +2761,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge * return -1; } - bridge_channel = find_bridge_channel(src_bridge, chan); + bridge_channel = bridge_find_channel(src_bridge, chan); if (!bridge_channel) { ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, channel not in bridge.\n", ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid); @@ -4529,7 +2782,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge * if (swap) { struct ast_bridge_channel *bridge_channel_swap; - bridge_channel_swap = find_bridge_channel(dst_bridge, swap); + bridge_channel_swap = bridge_find_channel(dst_bridge, swap); if (!bridge_channel_swap) { ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, swap channel %s not in bridge.\n", ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid, @@ -4545,7 +2798,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge * } bridge_channel->swap = swap; - return bridge_move_do(dst_bridge, bridge_channel, attempt_recovery, 0); + return bridge_do_move(dst_bridge, bridge_channel, attempt_recovery, 0); } int ast_bridge_move(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_channel *chan, struct ast_channel *swap, int attempt_recovery) @@ -4573,7 +2826,7 @@ int ast_bridge_add_channel(struct ast_bridge *bridge, struct ast_channel *chan, RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup); ast_bridge_lock_both(bridge, chan_bridge); - bridge_channel = find_bridge_channel(chan_bridge, chan); + bridge_channel = bridge_find_channel(chan_bridge, chan); if (bridge_move_locked(bridge, chan_bridge, chan, NULL, 1)) { ast_bridge_unlock(chan_bridge); ast_bridge_unlock(bridge); @@ -4634,22 +2887,6 @@ int ast_bridge_add_channel(struct ast_bridge *bridge, struct ast_channel *chan, return 0; } -struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel) -{ - struct ast_bridge *bridge = bridge_channel->bridge; - struct ast_bridge_channel *other = NULL; - - if (bridge_channel->in_bridge && bridge->num_channels == 2) { - AST_LIST_TRAVERSE(&bridge->channels, other, entry) { - if (other != bridge_channel) { - break; - } - } - } - - return other; -} - static int bridge_allows_optimization(struct ast_bridge *bridge) { return !(bridge->inhibit_merge @@ -4873,7 +3110,7 @@ static int try_swap_optimize_out(struct ast_bridge *chan_bridge, ast_set_flag(pvt, AST_UNREAL_OPTIMIZE_BEGUN); } other->swap = dst_bridge_channel->chan; - if (!bridge_move_do(dst_bridge, other, 1, 1)) { + if (!bridge_do_move(dst_bridge, other, 1, 1)) { ast_bridge_change_state(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); res = -1; if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) { @@ -4984,7 +3221,7 @@ static int try_merge_optimize_out(struct ast_bridge *chan_bridge, pvt->callbacks->optimization_started(pvt); ast_set_flag(pvt, AST_UNREAL_OPTIMIZE_BEGUN); } - bridge_merge_do(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me), 1); + bridge_do_merge(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me), 1); if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) { pvt->callbacks->optimization_finished(pvt); } @@ -5076,7 +3313,7 @@ enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *c * * \return Nothing */ -static void bridge_merge_inhibit_nolock(struct ast_bridge *bridge, int request) +void bridge_merge_inhibit_nolock(struct ast_bridge *bridge, int request) { int new_request; @@ -5092,18 +3329,6 @@ void ast_bridge_merge_inhibit(struct ast_bridge *bridge, int request) ast_bridge_unlock(bridge); } -struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request) -{ - struct ast_bridge *bridge; - - ast_bridge_channel_lock_bridge(bridge_channel); - bridge = bridge_channel->bridge; - ao2_ref(bridge, +1); - bridge_merge_inhibit_nolock(bridge, request); - ast_bridge_unlock(bridge); - return bridge; -} - int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan) { struct ast_bridge_channel *bridge_channel; @@ -5113,7 +3338,7 @@ int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan) ast_bridge_lock(bridge); - if (!(bridge_channel = find_bridge_channel(bridge, chan))) { + if (!(bridge_channel = bridge_find_channel(bridge, chan))) { ast_bridge_unlock(bridge); return -1; } @@ -5132,7 +3357,7 @@ int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan) ast_bridge_lock(bridge); - if (!(bridge_channel = find_bridge_channel(bridge, chan))) { + if (!(bridge_channel = bridge_find_channel(bridge, chan))) { ast_bridge_unlock(bridge); return -1; } @@ -6622,7 +4847,7 @@ static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge && !ast_test_flag(&bridged_to_source->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE)) { bridged_to_source->swap = swap_channel; - if (bridge_move_do(dest_bridge, bridged_to_source, 1, 0)) { + if (bridge_do_move(dest_bridge, bridged_to_source, 1, 0)) { return AST_BRIDGE_TRANSFER_FAIL; } /* Must kick the source channel out of its bridge. */ @@ -6678,12 +4903,12 @@ static enum ast_transfer_result two_bridge_attended_transfer(struct ast_channel goto end; case AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE: final_bridge = to_transferee_bridge; - bridge_merge_do(to_transferee_bridge, to_target_bridge, kick_me, ARRAY_LEN(kick_me), 0); + bridge_do_merge(to_transferee_bridge, to_target_bridge, kick_me, ARRAY_LEN(kick_me), 0); res = AST_BRIDGE_TRANSFER_SUCCESS; goto end; case AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE: final_bridge = to_target_bridge; - bridge_merge_do(to_target_bridge, to_transferee_bridge, kick_me, ARRAY_LEN(kick_me), 0); + bridge_do_merge(to_target_bridge, to_transferee_bridge, kick_me, ARRAY_LEN(kick_me), 0); res = AST_BRIDGE_TRANSFER_SUCCESS; goto end; case AST_BRIDGE_OPTIMIZE_PROHIBITED: diff --git a/main/bridging_basic.c b/main/bridging_basic.c index 3353066853e5f2daa25638bcb6a2e79dda184093..27cbce35ce8ff87b837eaebc761cb8c763345ced 100644 --- a/main/bridging_basic.c +++ b/main/bridging_basic.c @@ -42,6 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/file.h" #include "asterisk/app.h" #include "asterisk/bridging_internal.h" +#include "asterisk/bridging_channel_internal.h" #include "asterisk/dial.h" #include "asterisk/stasis_bridging.h" @@ -341,8 +342,8 @@ static int bridge_personality_normal_push(struct ast_bridge *self, struct ast_br return -1; } - ast_bridge_update_accountcodes(self, bridge_channel, swap); - ast_bridge_update_linkedids(self, bridge_channel, swap); + bridge_channel_update_accountcodes(bridge_channel, swap); + bridge_channel_update_linkedids(bridge_channel, swap); return 0; } @@ -1367,7 +1368,7 @@ static void bridge_unhold(struct ast_bridge *bridge) } /*! - * \brief Wrapper for \ref bridge_move_do + * \brief Wrapper for \ref bridge_do_move */ static int bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct ast_channel *channel, struct ast_channel *swap) { @@ -1386,7 +1387,7 @@ static int bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct a bridge_channel->swap = swap; ao2_unlock(bridge_channel); - res = bridge_move_do(dest, bridge_channel, 1, 0); + res = bridge_do_move(dest, bridge_channel, 1, 0); ast_bridge_unlock(dest); ast_bridge_unlock(src); @@ -1395,7 +1396,7 @@ static int bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct a } /*! - * \brief Wrapper for \ref bridge_merge_do + * \brief Wrapper for \ref bridge_do_merge */ static void bridge_merge(struct ast_bridge *dest, struct ast_bridge *src, struct ast_channel **kick_channels, unsigned int num_channels) { @@ -1409,9 +1410,9 @@ static void bridge_merge(struct ast_bridge *dest, struct ast_bridge *src, struct for (i = 0; i < num_channels; ++i) { struct ast_bridge_channel *kick_bridge_channel; - kick_bridge_channel = find_bridge_channel(src, kick_channels[i]); + kick_bridge_channel = bridge_find_channel(src, kick_channels[i]); if (!kick_bridge_channel) { - kick_bridge_channel = find_bridge_channel(dest, kick_channels[i]); + kick_bridge_channel = bridge_find_channel(dest, kick_channels[i]); } /* It's possible (and fine) for the bridge channel to be NULL at this point if the @@ -1425,7 +1426,7 @@ static void bridge_merge(struct ast_bridge *dest, struct ast_bridge *src, struct kick_bridge_channels[num_bridge_channels++] = kick_bridge_channel; } - bridge_merge_do(dest, src, kick_bridge_channels, num_bridge_channels, 0); + bridge_do_merge(dest, src, kick_bridge_channels, num_bridge_channels, 0); ast_bridge_unlock(dest); ast_bridge_unlock(src); } @@ -2658,7 +2659,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg } ast_bridge_channel_write_hold(bridge_channel, NULL); - props->transferee_bridge = ast_bridge_channel_merge_inhibit(bridge_channel, +1); + props->transferee_bridge = bridge_channel_merge_inhibit(bridge_channel, +1); /* Grab the extension to transfer to */ if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), props->context)) { @@ -2900,12 +2901,12 @@ struct ast_bridge *ast_bridge_basic_new(void) { struct ast_bridge *bridge; - bridge = ast_bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_basic_v_table); - bridge = ast_bridge_base_init(bridge, + bridge = bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_basic_v_table); + bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_MULTIMIX, NORMAL_FLAGS); bridge = bridge_basic_personality_alloc(bridge); - bridge = ast_bridge_register(bridge); + bridge = bridge_register(bridge); return bridge; } diff --git a/main/bridging_channel.c b/main/bridging_channel.c new file mode 100644 index 0000000000000000000000000000000000000000..4500dfb79dc4915bd3b8c59bc66bee0ace21e65f --- /dev/null +++ b/main/bridging_channel.c @@ -0,0 +1,1838 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007 - 2009, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Bridging Channel API + * + * \author Joshua Colp <jcolp@digium.com> + * \author Richard Mudgett <rmudgett@digium.com> + * \author Matt Jordan <mjordan@digium.com> + * + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <signal.h> + +#include "asterisk/heap.h" +#include "asterisk/astobj2.h" +#include "asterisk/stringfields.h" +#include "asterisk/app.h" +#include "asterisk/pbx.h" +#include "asterisk/channel.h" +#include "asterisk/timing.h" +#include "asterisk/bridging.h" +#include "asterisk/bridging_channel.h" +#include "asterisk/bridging_channel_internal.h" +#include "asterisk/bridging_internal.h" +#include "asterisk/stasis_bridging.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/musiconhold.h" +#include "asterisk/features_config.h" +#include "asterisk/parking.h" + + +struct ast_bridge *bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request) +{ + struct ast_bridge *bridge; + + ast_bridge_channel_lock_bridge(bridge_channel); + bridge = bridge_channel->bridge; + ao2_ref(bridge, +1); + bridge_merge_inhibit_nolock(bridge, request); + ast_bridge_unlock(bridge); + return bridge; +} + +void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge *bridge; + + for (;;) { + /* Safely get the bridge pointer */ + ast_bridge_channel_lock(bridge_channel); + bridge = bridge_channel->bridge; + ao2_ref(bridge, +1); + ast_bridge_channel_unlock(bridge_channel); + + /* Lock the bridge and see if it is still the bridge we need to lock. */ + ast_bridge_lock(bridge); + if (bridge == bridge_channel->bridge) { + ao2_ref(bridge, -1); + return; + } + ast_bridge_unlock(bridge); + ao2_ref(bridge, -1); + } +} + +static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel) +{ + if (!pthread_equal(pthread_self(), bridge_channel->thread)) { + while (bridge_channel->waiting) { + pthread_kill(bridge_channel->thread, SIGURG); + sched_yield(); + } + } +} + +void ast_bridge_change_state_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state) +{ +/* BUGBUG need cause code for the bridge_channel leaving the bridge. */ + if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + return; + } + + ast_debug(1, "Setting %p(%s) state from:%d to:%d\n", + bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state, + new_state); + + /* Change the state on the bridge channel */ + bridge_channel->state = new_state; + + bridge_channel_poke(bridge_channel); +} + +void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state) +{ + ast_bridge_channel_lock(bridge_channel); + ast_bridge_change_state_nolock(bridge_channel, new_state); + ast_bridge_channel_unlock(bridge_channel); +} + +void bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) +{ + struct ast_bridge *bridge = bridge_channel->bridge; + struct ast_bridge_channel *other = NULL; + + AST_LIST_TRAVERSE(&bridge->channels, other, entry) { + if (other == swap) { + continue; + } + + if (!ast_strlen_zero(ast_channel_accountcode(bridge_channel->chan)) && ast_strlen_zero(ast_channel_peeraccount(other->chan))) { + ast_debug(1, "Setting peeraccount to %s for %s from data on channel %s\n", + ast_channel_accountcode(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan)); + ast_channel_peeraccount_set(other->chan, ast_channel_accountcode(bridge_channel->chan)); + } + if (!ast_strlen_zero(ast_channel_accountcode(other->chan)) && ast_strlen_zero(ast_channel_peeraccount(bridge_channel->chan))) { + ast_debug(1, "Setting peeraccount to %s for %s from data on channel %s\n", + ast_channel_accountcode(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan)); + ast_channel_peeraccount_set(bridge_channel->chan, ast_channel_accountcode(other->chan)); + } + if (!ast_strlen_zero(ast_channel_peeraccount(bridge_channel->chan)) && ast_strlen_zero(ast_channel_accountcode(other->chan))) { + ast_debug(1, "Setting accountcode to %s for %s from data on channel %s\n", + ast_channel_peeraccount(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan)); + ast_channel_accountcode_set(other->chan, ast_channel_peeraccount(bridge_channel->chan)); + } + if (!ast_strlen_zero(ast_channel_peeraccount(other->chan)) && ast_strlen_zero(ast_channel_accountcode(bridge_channel->chan))) { + ast_debug(1, "Setting accountcode to %s for %s from data on channel %s\n", + ast_channel_peeraccount(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan)); + ast_channel_accountcode_set(bridge_channel->chan, ast_channel_peeraccount(other->chan)); + } + if (bridge->num_channels == 2) { + if (strcmp(ast_channel_accountcode(bridge_channel->chan), ast_channel_peeraccount(other->chan))) { + ast_debug(1, "Changing peeraccount from %s to %s on %s to match channel %s\n", + ast_channel_peeraccount(other->chan), ast_channel_peeraccount(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan)); + ast_channel_peeraccount_set(other->chan, ast_channel_accountcode(bridge_channel->chan)); + } + if (strcmp(ast_channel_accountcode(other->chan), ast_channel_peeraccount(bridge_channel->chan))) { + ast_debug(1, "Changing peeraccount from %s to %s on %s to match channel %s\n", + ast_channel_peeraccount(bridge_channel->chan), ast_channel_peeraccount(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan)); + ast_channel_peeraccount_set(bridge_channel->chan, ast_channel_accountcode(other->chan)); + } + } + } +} + +void bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) +{ + struct ast_bridge_channel *other = NULL; + struct ast_bridge *bridge = bridge_channel->bridge; + const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan); + + AST_LIST_TRAVERSE(&bridge->channels, other, entry) { + if (other == swap) { + continue; + } + oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan)); + } + + if (ast_strlen_zero(oldest_linkedid)) { + return; + } + + ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid); + AST_LIST_TRAVERSE(&bridge->channels, other, entry) { + if (other == swap) { + continue; + } + ast_channel_linkedid_set(other->chan, oldest_linkedid); + } +} + +int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr) +{ + struct ast_frame *dup; + char nudge = 0; + + if (bridge_channel->suspended + /* Also defer DTMF frames. */ + && fr->frametype != AST_FRAME_DTMF_BEGIN + && fr->frametype != AST_FRAME_DTMF_END + && !ast_is_deferrable_frame(fr)) { + /* Drop non-deferable frames when suspended. */ + return 0; + } + if (fr->frametype == AST_FRAME_NULL) { + /* "Accept" the frame and discard it. */ + return 0; + } + + dup = ast_frdup(fr); + if (!dup) { + return -1; + } + + ast_bridge_channel_lock(bridge_channel); + if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + /* Drop frames on channels leaving the bridge. */ + ast_bridge_channel_unlock(bridge_channel); + ast_frfree(dup); + return 0; + } + + AST_LIST_INSERT_TAIL(&bridge_channel->wr_queue, dup, frame_list); + if (write(bridge_channel->alert_pipe[1], &nudge, sizeof(nudge)) != sizeof(nudge)) { + ast_log(LOG_ERROR, "We couldn't write alert pipe for %p(%s)... something is VERY wrong\n", + bridge_channel, ast_channel_name(bridge_channel->chan)); + } + ast_bridge_channel_unlock(bridge_channel); + return 0; +} + +int ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen) +{ + struct ast_frame frame = { + .frametype = AST_FRAME_BRIDGE_ACTION, + .subclass.integer = action, + .datalen = datalen, + .data.ptr = (void *) data, + }; + + return ast_bridge_channel_queue_frame(bridge_channel, &frame); +} + +int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen) +{ + struct ast_frame frame = { + .frametype = AST_FRAME_CONTROL, + .subclass.integer = control, + .datalen = datalen, + .data.ptr = (void *) data, + }; + + return ast_bridge_channel_queue_frame(bridge_channel, &frame); +} + +int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +{ + struct ast_bridge_channel *cur; + int not_written = -1; + + if (frame->frametype == AST_FRAME_NULL) { + /* "Accept" the frame and discard it. */ + return 0; + } + + AST_LIST_TRAVERSE(&bridge->channels, cur, entry) { + if (cur == bridge_channel) { + continue; + } + if (!ast_bridge_channel_queue_frame(cur, frame)) { + not_written = 0; + } + } + return not_written; +} + +void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel) +{ + /* Restore original formats of the channel as they came in */ + if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) { + ast_debug(1, "Bridge is returning %p(%s) to read format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->read_format)); + if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) { + ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->read_format)); + } + } + if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) { + ast_debug(1, "Bridge is returning %p(%s) to write format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->write_format)); + if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) { + ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->write_format)); + } + } +} + +static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +{ + ast_bridge_channel_lock_bridge(bridge_channel); +/* + * BUGBUG need to implement a deferred write queue for when there is no peer channel in the bridge (yet or it was kicked). + * + * The tech decides if a frame needs to be pushed back for deferral. + * simple_bridge/native_bridge are likely the only techs that will do this. + */ + bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame); + ast_bridge_unlock(bridge_channel->bridge); + + /* + * Claim successful write to bridge. If deferred frame + * support is added, claim successfully deferred. + */ + return 0; +} + +int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen) +{ + struct ast_frame frame = { + .frametype = AST_FRAME_BRIDGE_ACTION, + .subclass.integer = action, + .datalen = datalen, + .data.ptr = (void *) data, + }; + + return bridge_channel_write_frame(bridge_channel, &frame); +} + +int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen) +{ + struct ast_frame frame = { + .frametype = AST_FRAME_CONTROL, + .subclass.integer = control, + .datalen = datalen, + .data.ptr = (void *) data, + }; + + return bridge_channel_write_frame(bridge_channel, &frame); +} + +int ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class) +{ + RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); + size_t datalen; + + if (!ast_strlen_zero(moh_class)) { + datalen = strlen(moh_class) + 1; + + blob = ast_json_pack("{s: s}", + "musicclass", moh_class); + } else { + moh_class = NULL; + datalen = 0; + } + + ast_channel_publish_blob(bridge_channel->chan, ast_channel_hold_type(), blob); + return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD, + moh_class, datalen); +} + +int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel) +{ + ast_channel_publish_blob(bridge_channel->chan, ast_channel_unhold_type(), NULL); + return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, NULL, 0); +} + +static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args) +{ + int res = 0; + + if (!strcasecmp("Gosub", app_name)) { + ast_app_exec_sub(NULL, chan, app_args, 0); + } else if (!strcasecmp("Macro", app_name)) { + ast_app_exec_macro(NULL, chan, app_args); + } else { + struct ast_app *app; + + app = pbx_findapp(app_name); + if (!app) { + ast_log(LOG_WARNING, "Could not find application (%s)\n", app_name); + } else { + res = pbx_exec(chan, app, app_args); + } + } + return res; +} + +/*! +* \internal +* \brief Handle bridge hangup event. +* \since 12.0.0 +* +* \param bridge_channel Which channel is hanging up. +* +* \return Nothing +*/ +static void bridge_channel_handle_hangup(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge_features *features = bridge_channel->features; + struct ast_bridge_hook *hook; + struct ao2_iterator iter; + + /* Run any hangup hooks. */ + iter = ao2_iterator_init(features->hangup_hooks, 0); + for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) { + int remove_me; + + remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); + if (remove_me) { + ast_debug(1, "Hangup hook %p is being removed from %p(%s)\n", + hook, bridge_channel, ast_channel_name(bridge_channel->chan)); + ao2_unlink(features->hangup_hooks, hook); + } + } + ao2_iterator_destroy(&iter); + + /* Default hangup action. */ + ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); +} + +void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) +{ + if (moh_class) { + ast_bridge_channel_write_hold(bridge_channel, moh_class); + } + if (run_app_helper(bridge_channel->chan, app_name, S_OR(app_args, ""))) { + /* Break the bridge if the app returns non-zero. */ + bridge_channel_handle_hangup(bridge_channel); + } + if (moh_class) { + ast_bridge_channel_write_unhold(bridge_channel); + } +} + + +struct bridge_run_app { + /*! Offset into app_name[] where the MOH class name starts. (zero if no MOH) */ + int moh_offset; + /*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */ + int app_args_offset; + /*! Application name to run. */ + char app_name[0]; +}; + +/*! + * \internal + * \brief Handle the run application bridge action. + * \since 12.0.0 + * + * \param bridge_channel Which channel to run the application on. + * \param data Action frame data to run the application. + * + * \return Nothing + */ +static void bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, struct bridge_run_app *data) +{ + ast_bridge_channel_run_app(bridge_channel, data->app_name, + data->app_args_offset ? &data->app_name[data->app_args_offset] : NULL, + data->moh_offset ? &data->app_name[data->moh_offset] : NULL); +} + +static int payload_helper_app(ast_bridge_channel_post_action_data post_it, + struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) +{ + struct bridge_run_app *app_data; + size_t len_name = strlen(app_name) + 1; + size_t len_args = ast_strlen_zero(app_args) ? 0 : strlen(app_args) + 1; + size_t len_moh = !moh_class ? 0 : strlen(moh_class) + 1; + size_t len_data = sizeof(*app_data) + len_name + len_args + len_moh; + + /* Fill in application run frame data. */ + app_data = alloca(len_data); + app_data->app_args_offset = len_args ? len_name : 0; + app_data->moh_offset = len_moh ? len_name + len_args : 0; + strcpy(app_data->app_name, app_name);/* Safe */ + if (len_args) { + strcpy(&app_data->app_name[app_data->app_args_offset], app_args);/* Safe */ + } + if (moh_class) { + strcpy(&app_data->app_name[app_data->moh_offset], moh_class);/* Safe */ + } + + return post_it(bridge_channel, AST_BRIDGE_ACTION_RUN_APP, app_data, len_data); +} + +int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) +{ + return payload_helper_app(ast_bridge_channel_write_action_data, + bridge_channel, app_name, app_args, moh_class); +} + +int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) +{ + return payload_helper_app(ast_bridge_channel_queue_action_data, + bridge_channel, app_name, app_args, moh_class); +} + +void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) +{ + if (moh_class) { + ast_bridge_channel_write_hold(bridge_channel, moh_class); + } + if (custom_play) { + custom_play(bridge_channel, playfile); + } else { + ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE); + } + if (moh_class) { + ast_bridge_channel_write_unhold(bridge_channel); + } + + /* + * It may be necessary to resume music on hold after we finish + * playing the announcment. + * + * XXX We have no idea what MOH class was in use before playing + * the file. This method also fails to restore ringing indications. + * the proposed solution is to create a resume_entertainment callback + * for the bridge technology and execute it here. + */ + if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_MOH)) { + ast_moh_start(bridge_channel->chan, NULL, NULL); + } +} + +struct bridge_playfile { + /*! Call this function to play the playfile. (NULL if normal sound file to play) */ + ast_bridge_custom_play_fn custom_play; + /*! Offset into playfile[] where the MOH class name starts. (zero if no MOH)*/ + int moh_offset; + /*! Filename to play. */ + char playfile[0]; +}; + +/*! + * \internal + * \brief Handle the playfile bridge action. + * \since 12.0.0 + * + * \param bridge_channel Which channel to play a file on. + * \param payload Action frame payload to play a file. + * + * \return Nothing + */ +static void bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, struct bridge_playfile *payload) +{ + ast_bridge_channel_playfile(bridge_channel, payload->custom_play, payload->playfile, + payload->moh_offset ? &payload->playfile[payload->moh_offset] : NULL); +} + +static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it, + struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) +{ + struct bridge_playfile *payload; + size_t len_name = strlen(playfile) + 1; + size_t len_moh = !moh_class ? 0 : strlen(moh_class) + 1; + size_t len_payload = sizeof(*payload) + len_name + len_moh; + + /* Fill in play file frame data. */ + payload = alloca(len_payload); + payload->custom_play = custom_play; + payload->moh_offset = len_moh ? len_name : 0; + strcpy(payload->playfile, playfile);/* Safe */ + if (moh_class) { + strcpy(&payload->playfile[payload->moh_offset], moh_class);/* Safe */ + } + + return post_it(bridge_channel, AST_BRIDGE_ACTION_PLAY_FILE, payload, len_payload); +} + +int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) +{ + return payload_helper_playfile(ast_bridge_channel_write_action_data, + bridge_channel, custom_play, playfile, moh_class); +} + +int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) +{ + return payload_helper_playfile(ast_bridge_channel_queue_action_data, + bridge_channel, custom_play, playfile, moh_class); +} + +struct bridge_custom_callback { + /*! Call this function on the bridge channel thread. */ + ast_bridge_custom_callback_fn callback; + /*! Size of the payload if it exists. A number otherwise. */ + size_t payload_size; + /*! Nonzero if the payload exists. */ + char payload_exists; + /*! Payload to give to callback. */ + char payload[0]; +}; + +/*! + * \internal + * \brief Handle the do custom callback bridge action. + * \since 12.0.0 + * + * \param bridge_channel Which channel to run the application on. + * \param data Action frame data to run the application. + * + * \return Nothing + */ +static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel, struct bridge_custom_callback *data) +{ + data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size); +} + +static int payload_helper_cb(ast_bridge_channel_post_action_data post_it, + struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) +{ + struct bridge_custom_callback *cb_data; + size_t len_data = sizeof(*cb_data) + (payload ? payload_size : 0); + + /* Sanity check. */ + if (!callback) { + ast_assert(0); + return -1; + } + + /* Fill in custom callback frame data. */ + cb_data = alloca(len_data); + cb_data->callback = callback; + cb_data->payload_size = payload_size; + cb_data->payload_exists = payload && payload_size; + if (cb_data->payload_exists) { + memcpy(cb_data->payload, payload, payload_size);/* Safe */ + } + + return post_it(bridge_channel, AST_BRIDGE_ACTION_CALLBACK, cb_data, len_data); +} + +int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) +{ + return payload_helper_cb(ast_bridge_channel_write_action_data, + bridge_channel, callback, payload, payload_size); +} + +int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) +{ + return payload_helper_cb(ast_bridge_channel_queue_action_data, + bridge_channel, callback, payload, payload_size); +} + +struct bridge_park { + int parker_uuid_offset; + int app_data_offset; + /* buffer used for holding those strings */ + char parkee_uuid[0]; +}; + +static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload) +{ + ast_bridge_channel_park(bridge_channel, payload->parkee_uuid, + &payload->parkee_uuid[payload->parker_uuid_offset], + payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL); +} + +static int payload_helper_park(ast_bridge_channel_post_action_data post_it, + struct ast_bridge_channel *bridge_channel, + const char *parkee_uuid, + const char *parker_uuid, + const char *app_data) +{ + struct bridge_park *payload; + size_t len_parkee_uuid = strlen(parkee_uuid) + 1; + size_t len_parker_uuid = strlen(parker_uuid) + 1; + size_t len_app_data = !app_data ? 0 : strlen(app_data) + 1; + size_t len_payload = sizeof(*payload) + len_parker_uuid + len_parkee_uuid + len_app_data; + + payload = alloca(len_payload); + payload->app_data_offset = len_app_data ? len_parkee_uuid + len_parker_uuid : 0; + payload->parker_uuid_offset = len_parkee_uuid; + strcpy(payload->parkee_uuid, parkee_uuid); + strcpy(&payload->parkee_uuid[payload->parker_uuid_offset], parker_uuid); + if (app_data) { + strcpy(&payload->parkee_uuid[payload->app_data_offset], app_data); + } + + return post_it(bridge_channel, AST_BRIDGE_ACTION_PARK, payload, len_payload); +} + +int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data) +{ + return payload_helper_park(ast_bridge_channel_write_action_data, + bridge_channel, parkee_uuid, parker_uuid, app_data); +} + +static int bridge_channel_interval_ready(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge_features *features = bridge_channel->features; + struct ast_bridge_hook *hook; + int ready; + + ast_heap_wrlock(features->interval_hooks); + hook = ast_heap_peek(features->interval_hooks, 1); + ready = hook && ast_tvdiff_ms(hook->parms.timer.trip_time, ast_tvnow()) <= 0; + ast_heap_unlock(features->interval_hooks); + + return ready; +} + +int ast_bridge_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking) +{ + struct ast_frame action = { + .frametype = AST_FRAME_BRIDGE_ACTION, + .subclass.integer = started_talking + ? AST_BRIDGE_ACTION_TALKING_START : AST_BRIDGE_ACTION_TALKING_STOP, + }; + + return ast_bridge_channel_queue_frame(bridge_channel, &action); +} + +struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge *bridge = bridge_channel->bridge; + struct ast_bridge_channel *other = NULL; + + if (bridge_channel->in_bridge && bridge->num_channels == 2) { + AST_LIST_TRAVERSE(&bridge->channels, other, entry) { + if (other != bridge_channel) { + break; + } + } + } + + return other; +} + +struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan) +{ + struct ast_bridge_channel *bridge_channel; + + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + if (bridge_channel->chan == chan) { + break; + } + } + + return bridge_channel; +} + +/*! + * \internal + * \brief Suspend a channel from a bridge. + * + * \param bridge_channel Channel to suspend. + * + * \note This function assumes bridge_channel->bridge is locked. + * + * \return Nothing + */ +void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel) +{ + bridge_channel->suspended = 1; + if (bridge_channel->in_bridge) { + --bridge_channel->bridge->num_active; + } + + /* Get technology bridge threads off of the channel. */ + if (bridge_channel->bridge->technology->suspend) { + bridge_channel->bridge->technology->suspend(bridge_channel->bridge, bridge_channel); + } +} + +/*! + * \internal + * \brief Suspend a channel from a bridge. + * + * \param bridge_channel Channel to suspend. + * + * \return Nothing + */ +static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel) +{ + ast_bridge_channel_lock_bridge(bridge_channel); + bridge_channel_suspend_nolock(bridge_channel); + ast_bridge_unlock(bridge_channel->bridge); +} + +/*! + * \internal + * \brief Unsuspend a channel from a bridge. + * + * \param bridge_channel Channel to unsuspend. + * + * \note This function assumes bridge_channel->bridge is locked. + * + * \return Nothing + */ +void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel) +{ + bridge_channel->suspended = 0; + if (bridge_channel->in_bridge) { + ++bridge_channel->bridge->num_active; + } + + /* Wake technology bridge threads to take care of channel again. */ + if (bridge_channel->bridge->technology->unsuspend) { + bridge_channel->bridge->technology->unsuspend(bridge_channel->bridge, bridge_channel); + } + + /* Wake suspended channel. */ + ast_bridge_channel_lock(bridge_channel); + ast_cond_signal(&bridge_channel->cond); + ast_bridge_channel_unlock(bridge_channel); +} + +/*! + * \internal + * \brief Unsuspend a channel from a bridge. + * + * \param bridge_channel Channel to unsuspend. + * + * \return Nothing + */ +static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel) +{ + ast_bridge_channel_lock_bridge(bridge_channel); + bridge_channel_unsuspend_nolock(bridge_channel); + ast_bridge_unlock(bridge_channel->bridge); +} + +/*! \brief Internal function that activates interval hooks on a bridge channel */ +static void bridge_channel_interval(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge_hook *hook; + struct timeval start; + + ast_heap_wrlock(bridge_channel->features->interval_hooks); + start = ast_tvnow(); + while ((hook = ast_heap_peek(bridge_channel->features->interval_hooks, 1))) { + int interval; + unsigned int execution_time; + + if (ast_tvdiff_ms(hook->parms.timer.trip_time, start) > 0) { + ast_debug(1, "Hook %p on %p(%s) wants to happen in the future, stopping our traversal\n", + hook, bridge_channel, ast_channel_name(bridge_channel->chan)); + break; + } + ao2_ref(hook, +1); + ast_heap_unlock(bridge_channel->features->interval_hooks); + + ast_debug(1, "Executing hook %p on %p(%s)\n", + hook, bridge_channel, ast_channel_name(bridge_channel->chan)); + interval = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); + + ast_heap_wrlock(bridge_channel->features->interval_hooks); + if (ast_heap_peek(bridge_channel->features->interval_hooks, + hook->parms.timer.heap_index) != hook + || !ast_heap_remove(bridge_channel->features->interval_hooks, hook)) { + /* Interval hook is already removed from the bridge_channel. */ + ao2_ref(hook, -1); + continue; + } + ao2_ref(hook, -1); + + if (interval < 0) { + ast_debug(1, "Removed interval hook %p from %p(%s)\n", + hook, bridge_channel, ast_channel_name(bridge_channel->chan)); + ao2_ref(hook, -1); + continue; + } + if (interval) { + /* Set new interval for the hook. */ + hook->parms.timer.interval = interval; + } + + ast_debug(1, "Updating interval hook %p with interval %u on %p(%s)\n", + hook, hook->parms.timer.interval, bridge_channel, + ast_channel_name(bridge_channel->chan)); + + /* resetting start */ + start = ast_tvnow(); + + /* + * Resetup the interval hook for the next interval. We may need + * to skip over any missed intervals because the hook was + * delayed or took too long. + */ + execution_time = ast_tvdiff_ms(start, hook->parms.timer.trip_time); + while (hook->parms.timer.interval < execution_time) { + execution_time -= hook->parms.timer.interval; + } + hook->parms.timer.trip_time = ast_tvadd(start, ast_samp2tv(hook->parms.timer.interval - execution_time, 1000)); + hook->parms.timer.seqno = ast_atomic_fetchadd_int((int *) &bridge_channel->features->interval_sequence, +1); + + if (ast_heap_push(bridge_channel->features->interval_hooks, hook)) { + /* Could not push the hook back onto the heap. */ + ao2_ref(hook, -1); + } + } + ast_heap_unlock(bridge_channel->features->interval_hooks); +} + +static int bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf) +{ + return ast_bridge_channel_write_action_data(bridge_channel, + AST_BRIDGE_ACTION_DTMF_STREAM, dtmf, strlen(dtmf) + 1); +} + +/*! + * \brief Internal function that executes a feature on a bridge channel + * \note Neither the bridge nor the bridge_channel locks should be held when entering + * this function. + */ +static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge_features *features = bridge_channel->features; + struct ast_bridge_hook *hook = NULL; + char dtmf[MAXIMUM_DTMF_FEATURE_STRING] = ""; + size_t dtmf_len = 0; + unsigned int digit_timeout; + RAII_VAR(struct ast_features_general_config *, gen_cfg, NULL, ao2_cleanup); + + ast_channel_lock(bridge_channel->chan); + gen_cfg = ast_get_chan_features_general_config(bridge_channel->chan); + if (!gen_cfg) { + ast_log(LOG_ERROR, "Unable to retrieve features configuration.\n"); + ast_channel_unlock(bridge_channel->chan); + return; + } + digit_timeout = gen_cfg->featuredigittimeout; + ast_channel_unlock(bridge_channel->chan); + + /* The channel is now under our control and we don't really want any begin frames to do our DTMF matching so disable 'em at the core level */ + ast_set_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_END_DTMF_ONLY); + + /* Wait for DTMF on the channel and put it into a buffer. If the buffer matches any feature hook execute the hook. */ + do { + int res; + + /* If the above timed out simply exit */ + res = ast_waitfordigit(bridge_channel->chan, digit_timeout); + if (!res) { + ast_debug(1, "DTMF feature string collection on %p(%s) timed out\n", + bridge_channel, ast_channel_name(bridge_channel->chan)); + break; + } + if (res < 0) { + ast_debug(1, "DTMF feature string collection failed on %p(%s) for some reason\n", + bridge_channel, ast_channel_name(bridge_channel->chan)); + break; + } + +/* BUGBUG need to record the duration of DTMF digits so when the string is played back, they are reproduced. */ + /* Add the above DTMF into the DTMF string so we can do our matching */ + dtmf[dtmf_len++] = res; + ast_debug(1, "DTMF feature string on %p(%s) is now '%s'\n", + bridge_channel, ast_channel_name(bridge_channel->chan), dtmf); + + /* See if a DTMF feature hook matches or can match */ + hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY); + if (!hook) { + ast_debug(1, "No DTMF feature hooks on %p(%s) match '%s'\n", + bridge_channel, ast_channel_name(bridge_channel->chan), dtmf); + break; + } + if (strlen(hook->parms.dtmf.code) == dtmf_len) { + ast_debug(1, "DTMF feature hook %p matched DTMF string '%s' on %p(%s)\n", + hook, dtmf, bridge_channel, ast_channel_name(bridge_channel->chan)); + break; + } + ao2_ref(hook, -1); + hook = NULL; + + /* Stop if we have reached the maximum length of a DTMF feature string. */ + } while (dtmf_len < ARRAY_LEN(dtmf) - 1); + + /* Since we are done bringing DTMF in return to using both begin and end frames */ + ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_END_DTMF_ONLY); + + /* If a hook was actually matched execute it on this channel, otherwise stream up the DTMF to the other channels */ + if (hook) { + int remove_me; + + remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); + if (remove_me) { + ast_debug(1, "DTMF hook %p is being removed from %p(%s)\n", + hook, bridge_channel, ast_channel_name(bridge_channel->chan)); + ao2_unlink(features->dtmf_hooks, hook); + } + ao2_ref(hook, -1); + + /* + * If we are handing the channel off to an external hook for + * ownership, we are not guaranteed what kind of state it will + * come back in. If the channel hungup, we need to detect that + * here if the hook did not already change the state. + */ + if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) { + bridge_channel_handle_hangup(bridge_channel); + } + } else if (features->dtmf_passthrough) { + bridge_channel_write_dtmf_stream(bridge_channel, dtmf); + } +} + +static void bridge_channel_talking(struct ast_bridge_channel *bridge_channel, int talking) +{ + struct ast_bridge_features *features = bridge_channel->features; + + if (features->talker_cb) { + features->talker_cb(bridge_channel, features->talker_pvt_data, talking); + } +} + +/*! \brief Internal function that plays back DTMF on a bridge channel */ +static void bridge_channel_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf) +{ + ast_debug(1, "Playing DTMF stream '%s' out to %p(%s)\n", + dtmf, bridge_channel, ast_channel_name(bridge_channel->chan)); + ast_dtmf_stream(bridge_channel->chan, NULL, dtmf, 0, 0); +} + +static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_channel, + struct blind_transfer_data *blind_data) +{ + ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1); + bridge_channel_handle_hangup(bridge_channel); +} + +static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *data) +{ + RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup); + struct ast_party_connected_line connected_target; + unsigned char connected_line_data[1024]; + int payload_size; + + ast_party_connected_line_init(&connected_target); + + ast_channel_lock(chan_target); + ast_party_connected_line_copy(&connected_target, ast_channel_connected(chan_target)); + ast_channel_unlock(chan_target); + ast_party_id_reset(&connected_target.priv); + + if (ast_channel_move(chan_target, chan_bridged)) { + ast_softhangup(chan_target, AST_SOFTHANGUP_DEV); + ast_party_connected_line_free(&connected_target); + return; + } + + if ((payload_size = ast_connected_line_build_data(connected_line_data, + sizeof(connected_line_data), &connected_target, NULL)) != -1) { + struct ast_control_read_action_payload *frame_payload; + int frame_size; + + frame_size = payload_size + sizeof(*frame_payload); + frame_payload = ast_alloca(frame_size); + frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO; + frame_payload->payload_size = payload_size; + memcpy(frame_payload->payload, connected_line_data, payload_size); + ast_queue_control_data(chan_target, AST_CONTROL_READ_ACTION, frame_payload, frame_size); + } + + ast_party_connected_line_free(&connected_target); +} + +static void after_bridge_move_channel_fail(enum ast_after_bridge_cb_reason reason, void *data) +{ + RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup); + + ast_log(LOG_WARNING, "Unable to complete transfer: %s\n", + ast_after_bridge_cb_reason_string(reason)); + ast_softhangup(chan_target, AST_SOFTHANGUP_DEV); +} + +static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_channel, + const char *target_chan_name) +{ + RAII_VAR(struct ast_channel *, chan_target, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel *, chan_bridged, NULL, ao2_cleanup); + + chan_target = ast_channel_get_by_name(target_chan_name); + if (!chan_target) { + /* Dang, it disappeared somehow */ + bridge_channel_handle_hangup(bridge_channel); + return; + } + + ast_bridge_channel_lock(bridge_channel); + chan_bridged = bridge_channel->chan; + ast_assert(chan_bridged != NULL); + ao2_ref(chan_bridged, +1); + ast_bridge_channel_unlock(bridge_channel); + + if (ast_after_bridge_callback_set(chan_bridged, after_bridge_move_channel, + after_bridge_move_channel_fail, ast_channel_ref(chan_target))) { + ast_softhangup(chan_target, AST_SOFTHANGUP_DEV); + + /* Release the ref we tried to pass to ast_after_bridge_callback_set(). */ + ast_channel_unref(chan_target); + } + bridge_channel_handle_hangup(bridge_channel); +} + +/*! + * \internal + * \brief Handle bridge channel bridge action frame. + * \since 12.0.0 + * + * \param bridge_channel Channel to execute the action on. + * \param action What to do. + * + * \return Nothing + */ +static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_channel, struct ast_frame *action) +{ + switch (action->subclass.integer) { + case AST_BRIDGE_ACTION_INTERVAL: + bridge_channel_suspend(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_interval(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_unsuspend(bridge_channel); + break; + case AST_BRIDGE_ACTION_FEATURE: + bridge_channel_suspend(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_feature(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_unsuspend(bridge_channel); + break; + case AST_BRIDGE_ACTION_DTMF_STREAM: + bridge_channel_suspend(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_dtmf_stream(bridge_channel, action->data.ptr); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_unsuspend(bridge_channel); + break; + case AST_BRIDGE_ACTION_TALKING_START: + case AST_BRIDGE_ACTION_TALKING_STOP: + bridge_channel_talking(bridge_channel, + action->subclass.integer == AST_BRIDGE_ACTION_TALKING_START); + break; + case AST_BRIDGE_ACTION_PLAY_FILE: + bridge_channel_suspend(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_playfile(bridge_channel, action->data.ptr); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_unsuspend(bridge_channel); + break; + case AST_BRIDGE_ACTION_RUN_APP: + bridge_channel_suspend(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_run_app(bridge_channel, action->data.ptr); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_unsuspend(bridge_channel); + break; + case AST_BRIDGE_ACTION_CALLBACK: + bridge_channel_suspend(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_do_callback(bridge_channel, action->data.ptr); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_unsuspend(bridge_channel); + break; + case AST_BRIDGE_ACTION_PARK: + bridge_channel_suspend(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_park(bridge_channel, action->data.ptr); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_unsuspend(bridge_channel); + break; + case AST_BRIDGE_ACTION_BLIND_TRANSFER: + bridge_channel_blind_transfer(bridge_channel, action->data.ptr); + break; + case AST_BRIDGE_ACTION_ATTENDED_TRANSFER: + bridge_channel_attended_transfer(bridge_channel, action->data.ptr); + break; + default: + break; + } +} + +/*! + * \internal + * \brief Check if a bridge should dissolve and do it. + * \since 12.0.0 + * + * \param bridge_channel Channel causing the check. + * + * \note On entry, bridge_channel->bridge is already locked. + * + * \return Nothing + */ +static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge *bridge = bridge_channel->bridge; + + if (bridge->dissolved) { + return; + } + + if (!bridge->num_channels + && ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY)) { + /* Last channel leaving the bridge turns off the lights. */ + bridge_dissolve(bridge); + return; + } + + switch (bridge_channel->state) { + case AST_BRIDGE_CHANNEL_STATE_END: + /* Do we need to dissolve the bridge because this channel hung up? */ + if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP) + || (bridge_channel->features->usable + && ast_test_flag(&bridge_channel->features->feature_flags, + AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP))) { + bridge_dissolve(bridge); + return; + } + break; + default: + break; + } +/* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */ +} + +/*! + * \internal + * \brief Pull the bridge channel out of its current bridge. + * \since 12.0.0 + * + * \param bridge_channel Channel to pull. + * + * \note On entry, bridge_channel->bridge is already locked. + * + * \return Nothing + */ +void bridge_channel_pull(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge *bridge = bridge_channel->bridge; + + if (!bridge_channel->in_bridge) { + return; + } + bridge_channel->in_bridge = 0; + + ast_debug(1, "Bridge %s: pulling %p(%s)\n", + bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan)); + + ast_verb(3, "Channel %s left '%s' %s-bridge <%s>\n", + ast_channel_name(bridge_channel->chan), + bridge->technology->name, + bridge->v_table->name, + bridge->uniqueid); + +/* BUGBUG This is where incoming HOLD/UNHOLD memory should write UNHOLD into bridge. (if not local optimizing) */ +/* BUGBUG This is where incoming DTMF begin/end memory should write DTMF end into bridge. (if not local optimizing) */ + if (!bridge_channel->just_joined) { + /* Tell the bridge technology we are leaving so they tear us down */ + ast_debug(1, "Bridge %s: %p(%s) is leaving %s technology\n", + bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan), + bridge->technology->name); + if (bridge->technology->leave) { + bridge->technology->leave(bridge, bridge_channel); + } + } + + /* Remove channel from the bridge */ + if (!bridge_channel->suspended) { + --bridge->num_active; + } + --bridge->num_channels; + AST_LIST_REMOVE(&bridge->channels, bridge_channel, entry); + bridge->v_table->pull(bridge, bridge_channel); + + ast_bridge_channel_clear_roles(bridge_channel); + + /* If we are not going to be hung up after leaving a bridge, and we were an + * outgoing channel, clear the outgoing flag. + */ + if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING) + && (ast_channel_softhangup_internal_flag(bridge_channel->chan) & + (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE))) { + ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING); + } + + bridge_dissolve_check(bridge_channel); + + bridge->reconfigured = 1; + ast_bridge_publish_leave(bridge, bridge_channel->chan); +} + +/*! + * \internal + * \brief Push the bridge channel into its specified bridge. + * \since 12.0.0 + * + * \param bridge_channel Channel to push. + * + * \note On entry, bridge_channel->bridge is already locked. + * + * \retval 0 on success. + * \retval -1 on failure. The channel did not get pushed. + */ +int bridge_channel_push(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge *bridge = bridge_channel->bridge; + struct ast_bridge_channel *swap; + + ast_assert(!bridge_channel->in_bridge); + + swap = bridge_find_channel(bridge, bridge_channel->swap); + bridge_channel->swap = NULL; + + if (swap) { + ast_debug(1, "Bridge %s: pushing %p(%s) by swapping with %p(%s)\n", + bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan), + swap, ast_channel_name(swap->chan)); + } else { + ast_debug(1, "Bridge %s: pushing %p(%s)\n", + bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan)); + } + + /* Add channel to the bridge */ + if (bridge->dissolved + || bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT + || (swap && swap->state != AST_BRIDGE_CHANNEL_STATE_WAIT) + || bridge->v_table->push(bridge, bridge_channel, swap) + || ast_bridge_channel_establish_roles(bridge_channel)) { + ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n", + bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan)); + ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL); + return -1; + } + bridge_channel->in_bridge = 1; + bridge_channel->just_joined = 1; + AST_LIST_INSERT_TAIL(&bridge->channels, bridge_channel, entry); + ++bridge->num_channels; + if (!bridge_channel->suspended) { + ++bridge->num_active; + } + + ast_verb(3, "Channel %s %s%s%s '%s' %s-bridge <%s>\n", + ast_channel_name(bridge_channel->chan), + swap ? "swapped with " : "joined", + swap ? ast_channel_name(swap->chan) : "", + swap ? " into" : "", + bridge->technology->name, + bridge->v_table->name, + bridge->uniqueid); + + ast_bridge_publish_enter(bridge, bridge_channel->chan); + if (swap) { + ast_bridge_change_state(swap, AST_BRIDGE_CHANNEL_STATE_HANGUP); + bridge_channel_pull(swap); + } + + /* Clear any BLINDTRANSFER and ATTENDEDTRANSFER since the transfer has completed. */ + pbx_builtin_setvar_helper(bridge_channel->chan, "BLINDTRANSFER", NULL); + pbx_builtin_setvar_helper(bridge_channel->chan, "ATTENDEDTRANSFER", NULL); + + bridge->reconfigured = 1; + return 0; +} + +/*! + * \internal + * \brief Handle bridge channel control frame action. + * \since 12.0.0 + * + * \param bridge_channel Channel to execute the control frame action on. + * \param fr Control frame to handle. + * + * \return Nothing + */ +static void bridge_channel_handle_control(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr) +{ + struct ast_channel *chan; + struct ast_option_header *aoh; + int is_caller; + int intercept_failed; + + chan = bridge_channel->chan; + switch (fr->subclass.integer) { + case AST_CONTROL_REDIRECTING: + is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING); + bridge_channel_suspend(bridge_channel); + intercept_failed = ast_channel_redirecting_sub(NULL, chan, fr, 1) + && ast_channel_redirecting_macro(NULL, chan, fr, is_caller, 1); + bridge_channel_unsuspend(bridge_channel); + if (intercept_failed) { + ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen); + } + break; + case AST_CONTROL_CONNECTED_LINE: + is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING); + bridge_channel_suspend(bridge_channel); + intercept_failed = ast_channel_connected_line_sub(NULL, chan, fr, 1) + && ast_channel_connected_line_macro(NULL, chan, fr, is_caller, 1); + bridge_channel_unsuspend(bridge_channel); + if (intercept_failed) { + ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen); + } + break; + case AST_CONTROL_HOLD: + case AST_CONTROL_UNHOLD: +/* + * BUGBUG bridge_channels should remember sending/receiving an outstanding HOLD to/from the bridge + * + * When the sending channel is pulled from the bridge it needs to write into the bridge an UNHOLD before being pulled. + * When the receiving channel is pulled from the bridge it needs to generate its own UNHOLD. + * Something similar needs to be done for DTMF begin/end. + */ + case AST_CONTROL_VIDUPDATE: + case AST_CONTROL_SRCUPDATE: + case AST_CONTROL_SRCCHANGE: + case AST_CONTROL_T38_PARAMETERS: +/* BUGBUG may have to do something with a jitter buffer for these. */ + ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen); + break; + case AST_CONTROL_OPTION: + /* + * Forward option Requests, but only ones we know are safe These + * are ONLY sent by chan_iax2 and I'm not convinced that they + * are useful. I haven't deleted them entirely because I just am + * not sure of the ramifications of removing them. + */ + aoh = fr->data.ptr; + if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) { + switch (ntohs(aoh->option)) { + case AST_OPTION_TONE_VERIFY: + case AST_OPTION_TDD: + case AST_OPTION_RELAXDTMF: + case AST_OPTION_AUDIO_MODE: + case AST_OPTION_DIGIT_DETECT: + case AST_OPTION_FAX_DETECT: + ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, + fr->datalen - sizeof(*aoh), 0); + break; + default: + break; + } + } + break; + case AST_CONTROL_ANSWER: + if (ast_channel_state(chan) != AST_STATE_UP) { + ast_answer(chan); + } else { + ast_indicate(chan, -1); + } + break; + default: + ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen); + break; + } +} + +/*! + * \internal + * \brief Handle bridge channel write frame to channel. + * \since 12.0.0 + * + * \param bridge_channel Channel to write outgoing frame. + * + * \return Nothing + */ +static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channel) +{ + struct ast_frame *fr; + char nudge; + + ast_bridge_channel_lock(bridge_channel); + if (read(bridge_channel->alert_pipe[0], &nudge, sizeof(nudge)) < 0) { + if (errno != EINTR && errno != EAGAIN) { + ast_log(LOG_WARNING, "read() failed for alert pipe on %p(%s): %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), strerror(errno)); + } + } + fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list); + ast_bridge_channel_unlock(bridge_channel); + if (!fr) { + return; + } + switch (fr->frametype) { + case AST_FRAME_BRIDGE_ACTION: + bridge_channel_handle_action(bridge_channel, fr); + break; + case AST_FRAME_CONTROL: + bridge_channel_handle_control(bridge_channel, fr); + break; + case AST_FRAME_NULL: + break; + default: + /* Write the frame to the channel. */ + bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_SIMPLE; + ast_write(bridge_channel->chan, fr); + break; + } + ast_frfree(fr); +} + +/*! + * \internal + * \brief Handle bridge channel interval expiration. + * \since 12.0.0 + * + * \param bridge_channel Channel to check interval on. + * + * \return Nothing + */ +static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_channel) +{ + struct ast_timer *interval_timer; + + interval_timer = bridge_channel->features->interval_timer; + if (interval_timer) { + if (ast_wait_for_input(ast_timer_fd(interval_timer), 0) == 1) { + ast_timer_ack(interval_timer, 1); + if (bridge_channel_interval_ready(bridge_channel)) { +/* BUGBUG since this is now only run by the channel thread, there is no need to queue the action once this intervals become a first class wait item in bridge_channel_wait(). */ + struct ast_frame interval_action = { + .frametype = AST_FRAME_BRIDGE_ACTION, + .subclass.integer = AST_BRIDGE_ACTION_INTERVAL, + }; + + ast_bridge_channel_queue_frame(bridge_channel, &interval_action); + } + } + } +} + +/*! \brief Internal function to handle DTMF from a channel */ +static struct ast_frame *bridge_handle_dtmf(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +{ + struct ast_bridge_features *features = bridge_channel->features; + struct ast_bridge_hook *hook; + char dtmf[2]; + +/* BUGBUG the feature hook matching needs to be done here. Any matching feature hook needs to be queued onto the bridge_channel. Also the feature hook digit timeout needs to be handled. */ +/* BUGBUG the AMI atxfer action just sends DTMF end events to initiate DTMF atxfer and dial the extension. Another reason the DTMF hook matching needs rework. */ + /* See if this DTMF matches the beginnings of any feature hooks, if so we switch to the feature state to either execute the feature or collect more DTMF */ + dtmf[0] = frame->subclass.integer; + dtmf[1] = '\0'; + hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY); + if (hook) { + struct ast_frame action = { + .frametype = AST_FRAME_BRIDGE_ACTION, + .subclass.integer = AST_BRIDGE_ACTION_FEATURE, + }; + + ast_frfree(frame); + frame = NULL; + ast_bridge_channel_queue_frame(bridge_channel, &action); + ao2_ref(hook, -1); + } + + return frame; +} + + +/*! + * \internal + * \brief Feed notification that a frame is waiting on a channel into the bridging core + * + * \param bridge_channel Bridge channel the notification was received on + */ +static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel) +{ + struct ast_frame *frame; + + if (bridge_channel->features->mute) { + frame = ast_read_noaudio(bridge_channel->chan); + } else { + frame = ast_read(bridge_channel->chan); + } + + if (!frame) { + bridge_channel_handle_hangup(bridge_channel); + return; + } + switch (frame->frametype) { + case AST_FRAME_CONTROL: + switch (frame->subclass.integer) { + case AST_CONTROL_HANGUP: + bridge_channel_handle_hangup(bridge_channel); + ast_frfree(frame); + return; +/* BUGBUG This is where incoming HOLD/UNHOLD memory should register. Write UNHOLD into bridge when this channel is pulled. */ + default: + break; + } + break; + case AST_FRAME_DTMF_BEGIN: + frame = bridge_handle_dtmf(bridge_channel, frame); + if (!frame) { + return; + } + /* Fall through */ + case AST_FRAME_DTMF_END: + if (!bridge_channel->features->dtmf_passthrough) { + ast_frfree(frame); + return; + } +/* BUGBUG This is where incoming DTMF begin/end memory should register. Write DTMF end into bridge when this channel is pulled. */ + break; + default: + break; + } + + /* Simply write the frame out to the bridge technology. */ +/* BUGBUG The tech is where AST_CONTROL_ANSWER hook should go. (early bridge) */ +/* BUGBUG The tech is where incoming BUSY/CONGESTION hangup should happen? (early bridge) */ + bridge_channel_write_frame(bridge_channel, frame); + ast_frfree(frame); +} + +/*! + * \internal + * \brief Wait for something to happen on the bridge channel and handle it. + * \since 12.0.0 + * + * \param bridge_channel Channel to wait. + * + * \note Each channel does writing/reading in their own thread. + * + * \return Nothing + */ +static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel) +{ + int ms = -1; + int outfd; + struct ast_channel *chan; + + /* Wait for data to either come from the channel or us to be signaled */ + ast_bridge_channel_lock(bridge_channel); + if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + } else if (bridge_channel->suspended) { +/* BUGBUG the external party use of suspended will go away as will these references because this is the bridge channel thread */ + ast_debug(1, "Bridge %s: %p(%s) is going into a signal wait\n", + bridge_channel->bridge->uniqueid, bridge_channel, + ast_channel_name(bridge_channel->chan)); + ast_cond_wait(&bridge_channel->cond, ao2_object_get_lockaddr(bridge_channel)); + } else { + ast_debug(10, "Bridge %s: %p(%s) is going into a waitfor\n", + bridge_channel->bridge->uniqueid, bridge_channel, + ast_channel_name(bridge_channel->chan)); + bridge_channel->waiting = 1; + ast_bridge_channel_unlock(bridge_channel); + outfd = -1; +/* BUGBUG need to make the next expiring active interval setup ms timeout rather than holding up the chan reads. */ + chan = ast_waitfor_nandfds(&bridge_channel->chan, 1, + &bridge_channel->alert_pipe[0], 1, NULL, &outfd, &ms); + bridge_channel->waiting = 0; + if (ast_channel_softhangup_internal_flag(bridge_channel->chan) & AST_SOFTHANGUP_UNBRIDGE) { + ast_channel_clear_softhangup(bridge_channel->chan, AST_SOFTHANGUP_UNBRIDGE); + ast_bridge_channel_lock_bridge(bridge_channel); + bridge_channel->bridge->reconfigured = 1; + bridge_reconfigured(bridge_channel->bridge, 0); + ast_bridge_unlock(bridge_channel->bridge); + } + ast_bridge_channel_lock(bridge_channel); + bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_FRAME; + ast_bridge_channel_unlock(bridge_channel); + if (!bridge_channel->suspended + && bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (chan) { + bridge_channel_handle_interval(bridge_channel); + bridge_handle_trip(bridge_channel); + } else if (-1 < outfd) { + bridge_channel_handle_write(bridge_channel); + } + } + bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_IDLE; + return; + } + ast_bridge_channel_unlock(bridge_channel); +} + +/*! + * \internal + * \brief Handle bridge channel join event. + * \since 12.0.0 + * + * \param bridge_channel Which channel is joining. + * + * \return Nothing + */ +static void bridge_channel_handle_join(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge_features *features = bridge_channel->features; + struct ast_bridge_hook *hook; + struct ao2_iterator iter; + + /* Run any join hooks. */ + iter = ao2_iterator_init(features->join_hooks, AO2_ITERATOR_UNLINK); + hook = ao2_iterator_next(&iter); + if (hook) { + bridge_channel_suspend(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + do { + hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); + ao2_ref(hook, -1); + } while ((hook = ao2_iterator_next(&iter))); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_unsuspend(bridge_channel); + } + ao2_iterator_destroy(&iter); +} + +/*! + * \internal + * \brief Handle bridge channel leave event. + * \since 12.0.0 + * + * \param bridge_channel Which channel is leaving. + * + * \return Nothing + */ +static void bridge_channel_handle_leave(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge_features *features = bridge_channel->features; + struct ast_bridge_hook *hook; + struct ao2_iterator iter; + + /* Run any leave hooks. */ + iter = ao2_iterator_init(features->leave_hooks, AO2_ITERATOR_UNLINK); + hook = ao2_iterator_next(&iter); + if (hook) { + bridge_channel_suspend(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + do { + hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); + ao2_ref(hook, -1); + } while ((hook = ao2_iterator_next(&iter))); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_unsuspend(bridge_channel); + } + ao2_iterator_destroy(&iter); +} + +/*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */ +void bridge_channel_join(struct ast_bridge_channel *bridge_channel) +{ + ast_format_copy(&bridge_channel->read_format, ast_channel_readformat(bridge_channel->chan)); + ast_format_copy(&bridge_channel->write_format, ast_channel_writeformat(bridge_channel->chan)); + + ast_debug(1, "Bridge %s: %p(%s) is joining\n", + bridge_channel->bridge->uniqueid, + bridge_channel, ast_channel_name(bridge_channel->chan)); + + /* + * Get "in the bridge" before pushing the channel for any + * masquerades on the channel to happen before bridging. + */ + ast_channel_lock(bridge_channel->chan); + ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge); + ast_channel_unlock(bridge_channel->chan); + + /* Add the jitterbuffer if the channel requires it */ + ast_jb_enable_for_channel(bridge_channel->chan); + + /* + * Directly locking the bridge is safe here because nobody else + * knows about this bridge_channel yet. + */ + ast_bridge_lock(bridge_channel->bridge); + + if (!bridge_channel->bridge->callid) { + bridge_channel->bridge->callid = ast_read_threadstorage_callid(); + } + + if (bridge_channel_push(bridge_channel)) { + ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); + } + bridge_reconfigured(bridge_channel->bridge, 1); + + if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + /* + * Indicate a source change since this channel is entering the + * bridge system only if the bridge technology is not MULTIMIX + * capable. The MULTIMIX technology has already done it. + */ + if (!(bridge_channel->bridge->technology->capabilities + & AST_BRIDGE_CAPABILITY_MULTIMIX)) { + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE); + } + + ast_bridge_unlock(bridge_channel->bridge); + bridge_channel_handle_join(bridge_channel); + while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + /* Wait for something to do. */ + bridge_channel_wait(bridge_channel); + } + bridge_channel_handle_leave(bridge_channel); + ast_bridge_channel_lock_bridge(bridge_channel); + } + + bridge_channel_pull(bridge_channel); + bridge_reconfigured(bridge_channel->bridge, 1); + + ast_bridge_unlock(bridge_channel->bridge); + + /* Indicate a source change since this channel is leaving the bridge system. */ + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE); + +/* BUGBUG Revisit in regards to moving channels between bridges and local channel optimization. */ +/* BUGBUG This is where outgoing HOLD/UNHOLD memory should write UNHOLD to channel. */ + /* Complete any partial DTMF digit before exiting the bridge. */ + if (ast_channel_sending_dtmf_digit(bridge_channel->chan)) { + ast_channel_end_dtmf(bridge_channel->chan, + ast_channel_sending_dtmf_digit(bridge_channel->chan), + ast_channel_sending_dtmf_tv(bridge_channel->chan), "bridge end"); + } + + /* + * Wait for any dual redirect to complete. + * + * Must be done while "still in the bridge" for ast_async_goto() + * to work right. + */ + while (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT)) { + sched_yield(); + } + ast_channel_lock(bridge_channel->chan); + ast_channel_internal_bridge_set(bridge_channel->chan, NULL); + ast_channel_unlock(bridge_channel->chan); + + ast_bridge_channel_restore_formats(bridge_channel); +} + +void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel) +{ + ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); +} + +void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel) +{ + ast_bridge_change_state_nolock(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); +} \ No newline at end of file diff --git a/main/channel.c b/main/channel.c index a1a3730bafa880a58348d6ed4b71eaa4d7f1e602..4738e37d47505f9045afaf38fa80706a114d7c6b 100644 --- a/main/channel.c +++ b/main/channel.c @@ -6740,7 +6740,7 @@ void ast_do_masquerade(struct ast_channel *original) * The clonechan was sending a DTMF digit that was not completed * before the masquerade. */ - ast_bridge_end_dtmf(original, clone_sending_dtmf_digit, clone_sending_dtmf_tv, + ast_channel_end_dtmf(original, clone_sending_dtmf_digit, clone_sending_dtmf_tv, "masquerade"); } @@ -10406,3 +10406,24 @@ int ast_channel_unsuppress(struct ast_channel *chan, unsigned int direction, enu return 0; } + +void ast_channel_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why) +{ + int dead; + long duration; + + ast_channel_lock(chan); + dead = ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) + || (ast_channel_softhangup_internal_flag(chan) + & ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE)); + ast_channel_unlock(chan); + if (dead) { + /* Channel is a zombie or a real hangup. */ + return; + } + + duration = ast_tvdiff_ms(ast_tvnow(), start); + ast_senddigit_end(chan, digit, duration); + ast_log(LOG_DTMF, "DTMF end '%c' simulated on %s due to %s, duration %ld ms\n", + digit, ast_channel_name(chan), why, duration); +} \ No newline at end of file diff --git a/main/features.c b/main/features.c index 1150f7eb1cbc2c2a4eb2a5a8a371a15ddef9f89e..1e578d2b9e27f938782e2ac557317958c4b8c860 100644 --- a/main/features.c +++ b/main/features.c @@ -3161,27 +3161,6 @@ static void clear_dialed_interfaces(struct ast_channel *chan) ast_channel_unlock(chan); } -void ast_bridge_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why) -{ - int dead; - long duration; - - ast_channel_lock(chan); - dead = ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) - || (ast_channel_softhangup_internal_flag(chan) - & ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE)); - ast_channel_unlock(chan); - if (dead) { - /* Channel is a zombie or a real hangup. */ - return; - } - - duration = ast_tvdiff_ms(ast_tvnow(), start); - ast_senddigit_end(chan, digit, duration); - ast_log(LOG_DTMF, "DTMF end '%c' simulated on %s due to %s, duration %ld ms\n", - digit, ast_channel_name(chan), why, duration); -} - /*! * \internal * \brief Setup bridge builtin features. diff --git a/res/parking/parking_bridge.c b/res/parking/parking_bridge.c index 75bd62bf9dccf5786ccdc7b495a0a5f62e2b785c..4c919983007e59276ea8cf8fa3ca38d7376b4c9c 100644 --- a/res/parking/parking_bridge.c +++ b/res/parking/parking_bridge.c @@ -24,12 +24,13 @@ */ #include "asterisk.h" -#include "asterisk/logger.h" #include "res_parking.h" #include "asterisk/astobj2.h" -#include "asterisk/features.h" +#include "asterisk/logger.h" #include "asterisk/say.h" #include "asterisk/term.h" +#include "asterisk/features.h" +#include "asterisk/bridging_internal.h" struct ast_bridge_parking { @@ -447,11 +448,11 @@ struct ast_bridge *bridge_parking_new(struct parking_lot *bridge_lot) { void *bridge; - bridge = ast_bridge_alloc(sizeof(struct ast_bridge_parking), &ast_bridge_parking_v_table); - bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING, + bridge = bridge_alloc(sizeof(struct ast_bridge_parking), &ast_bridge_parking_v_table); + bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING, AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM); bridge = ast_bridge_parking_init(bridge, bridge_lot); - bridge = ast_bridge_register(bridge); + bridge = bridge_register(bridge); return bridge; } diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c index aee4edbab2dce06d5f046af1a4f864ea04922b31..e8ec1809bcf6ef085d35c73b8f7ac69217efeae2 100644 --- a/res/parking/parking_bridge_features.c +++ b/res/parking/parking_bridge_features.c @@ -33,6 +33,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/logger.h" #include "asterisk/pbx.h" #include "asterisk/bridging.h" +#include "asterisk/bridging_internal.h" +#include "asterisk/bridging_channel_internal.h" +#include "asterisk/bridging_channel.h" #include "asterisk/bridging_features.h" #include "asterisk/features.h" #include "asterisk/say.h"