2013-07-24 15:38:18 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
|
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include "asterisk/heap.h"
|
2017-04-18 00:06:10 +00:00
|
|
|
#include "asterisk/alertpipe.h"
|
2013-07-24 15:38:18 +00:00
|
|
|
#include "asterisk/astobj2.h"
|
|
|
|
#include "asterisk/stringfields.h"
|
|
|
|
#include "asterisk/app.h"
|
|
|
|
#include "asterisk/pbx.h"
|
|
|
|
#include "asterisk/channel.h"
|
|
|
|
#include "asterisk/timing.h"
|
2013-07-25 04:06:32 +00:00
|
|
|
#include "asterisk/bridge.h"
|
|
|
|
#include "asterisk/bridge_channel.h"
|
|
|
|
#include "asterisk/bridge_after.h"
|
|
|
|
#include "asterisk/bridge_channel_internal.h"
|
|
|
|
#include "asterisk/bridge_internal.h"
|
|
|
|
#include "asterisk/stasis_bridges.h"
|
2013-07-24 15:38:18 +00:00
|
|
|
#include "asterisk/stasis_channels.h"
|
|
|
|
#include "asterisk/musiconhold.h"
|
|
|
|
#include "asterisk/features_config.h"
|
|
|
|
#include "asterisk/parking.h"
|
2013-08-22 21:09:52 +00:00
|
|
|
#include "asterisk/causes.h"
|
2013-08-23 21:02:26 +00:00
|
|
|
#include "asterisk/test.h"
|
2015-01-26 14:50:40 +00:00
|
|
|
#include "asterisk/sem.h"
|
2017-04-25 16:49:16 +00:00
|
|
|
#include "asterisk/stream.h"
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-07-24 19:24:09 +00:00
|
|
|
/*!
|
|
|
|
* \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 bridge_channel_action_type action, const void *data, size_t datalen);
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2014-03-17 17:22:12 +00:00
|
|
|
/*!
|
|
|
|
* \brief Counter used for assigning synchronous bridge action IDs
|
|
|
|
*/
|
|
|
|
static int sync_ids;
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Frame payload for synchronous bridge actions.
|
|
|
|
*
|
|
|
|
* The payload serves as a wrapper around the actual payload of the
|
|
|
|
* frame, with the addition of an id used to find the associated
|
|
|
|
* bridge_sync object.
|
|
|
|
*/
|
|
|
|
struct sync_payload {
|
|
|
|
/*! Unique ID for this synchronous action */
|
|
|
|
unsigned int id;
|
|
|
|
/*! Actual frame data to process */
|
|
|
|
unsigned char data[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Synchronous bridge action object.
|
|
|
|
*
|
|
|
|
* Synchronous bridge actions require the ability for one thread to wait
|
|
|
|
* and for another thread to indicate that the action has completed. This
|
|
|
|
* structure facilitates that goal by providing synchronization structures.
|
|
|
|
*/
|
|
|
|
struct bridge_sync {
|
|
|
|
/*! Unique ID of this synchronization object. Corresponds with ID in synchronous frame payload */
|
|
|
|
unsigned int id;
|
|
|
|
/*! Semaphore used for synchronization */
|
2015-01-26 14:50:40 +00:00
|
|
|
struct ast_sem sem;
|
2014-03-17 17:22:12 +00:00
|
|
|
/*! Pointer to next entry in the list */
|
|
|
|
AST_LIST_ENTRY(bridge_sync) list;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief List holding active synchronous action objects.
|
|
|
|
*/
|
|
|
|
static AST_RWLIST_HEAD_STATIC(sync_structs, bridge_sync);
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief initialize a synchronous bridge object.
|
|
|
|
*
|
|
|
|
* This both initializes the structure and adds it to the list of
|
|
|
|
* synchronization structures.
|
|
|
|
*
|
|
|
|
* \param sync_struct The synchronization object to initialize.
|
|
|
|
* \param id ID to assign to the synchronization object.
|
|
|
|
*/
|
|
|
|
static void bridge_sync_init(struct bridge_sync *sync_struct, unsigned int id)
|
|
|
|
{
|
|
|
|
memset(sync_struct, 0, sizeof(*sync_struct));
|
|
|
|
sync_struct->id = id;
|
2015-01-26 14:50:40 +00:00
|
|
|
ast_sem_init(&sync_struct->sem, 0, 0);
|
2014-03-17 17:22:12 +00:00
|
|
|
|
|
|
|
AST_RWLIST_WRLOCK(&sync_structs);
|
|
|
|
AST_RWLIST_INSERT_TAIL(&sync_structs, sync_struct, list);
|
|
|
|
AST_RWLIST_UNLOCK(&sync_structs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Clean up a syncrhonization bridge object.
|
|
|
|
*
|
|
|
|
* This frees fields within the synchronization object and removes
|
|
|
|
* it from the list of active synchronization objects.
|
|
|
|
*
|
|
|
|
* Since synchronization objects are stack-allocated, it is vital
|
|
|
|
* that this is called before the synchronization object goes
|
|
|
|
* out of scope.
|
|
|
|
*
|
|
|
|
* \param sync_struct Synchronization object to clean up.
|
|
|
|
*/
|
|
|
|
static void bridge_sync_cleanup(struct bridge_sync *sync_struct)
|
|
|
|
{
|
|
|
|
struct bridge_sync *iter;
|
|
|
|
|
|
|
|
AST_RWLIST_WRLOCK(&sync_structs);
|
|
|
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&sync_structs, iter, list) {
|
|
|
|
if (iter->id == sync_struct->id) {
|
|
|
|
AST_LIST_REMOVE_CURRENT(list);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AST_LIST_TRAVERSE_SAFE_END;
|
|
|
|
AST_RWLIST_UNLOCK(&sync_structs);
|
|
|
|
|
2015-01-26 14:50:40 +00:00
|
|
|
ast_sem_destroy(&sync_struct->sem);
|
2014-03-17 17:22:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Failsafe for synchronous bridge action waiting.
|
|
|
|
*
|
|
|
|
* When waiting for a synchronous bridge action to complete,
|
|
|
|
* if there is a frame resource leak somewhere, it is possible
|
|
|
|
* that we will never get notified that the synchronous action
|
|
|
|
* completed.
|
|
|
|
*
|
|
|
|
* If a significant amount of time passes, then we will abandon
|
|
|
|
* waiting for the synchrnous bridge action to complete.
|
|
|
|
*
|
|
|
|
* This constant represents the number of milliseconds we will
|
|
|
|
* wait for the bridge action to complete.
|
|
|
|
*/
|
|
|
|
#define PLAYBACK_TIMEOUT (600 * 1000)
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Wait for a synchronous bridge action to complete.
|
|
|
|
*
|
|
|
|
* \param sync_struct Synchronization object corresponding to the bridge action.
|
|
|
|
*/
|
|
|
|
static void bridge_sync_wait(struct bridge_sync *sync_struct)
|
|
|
|
{
|
|
|
|
struct timeval timeout_val = ast_tvadd(ast_tvnow(), ast_samp2tv(PLAYBACK_TIMEOUT, 1000));
|
|
|
|
struct timespec timeout_spec = {
|
|
|
|
.tv_sec = timeout_val.tv_sec,
|
|
|
|
.tv_nsec = timeout_val.tv_usec * 1000,
|
|
|
|
};
|
|
|
|
|
2015-01-26 14:50:40 +00:00
|
|
|
ast_sem_timedwait(&sync_struct->sem, &timeout_spec);
|
2014-03-17 17:22:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Signal that waiting for a synchronous bridge action is no longer necessary.
|
|
|
|
*
|
|
|
|
* This may occur for several reasons
|
|
|
|
* \li The synchronous bridge action has completed.
|
|
|
|
* \li The bridge channel has been removed from the bridge.
|
|
|
|
* \li The synchronous bridge action could not be queued.
|
|
|
|
*
|
|
|
|
* \param sync_struct Synchronization object corresponding to the bridge action.
|
|
|
|
*/
|
|
|
|
static void bridge_sync_signal(struct bridge_sync *sync_struct)
|
|
|
|
{
|
2015-01-26 14:50:40 +00:00
|
|
|
ast_sem_post(&sync_struct->sem);
|
2014-03-17 17:22:12 +00:00
|
|
|
}
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
int ast_bridge_channel_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking)
|
|
|
|
{
|
|
|
|
struct ast_frame action = {
|
|
|
|
.frametype = AST_FRAME_BRIDGE_ACTION,
|
|
|
|
.subclass.integer = started_talking
|
|
|
|
? BRIDGE_CHANNEL_ACTION_TALKING_START : BRIDGE_CHANNEL_ACTION_TALKING_STOP,
|
|
|
|
};
|
|
|
|
|
|
|
|
return ast_bridge_channel_queue_frame(bridge_channel, &action);
|
|
|
|
}
|
|
|
|
|
2013-08-16 17:33:21 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Poke the bridge_channel thread
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
if (!pthread_equal(pthread_self(), bridge_channel->thread)) {
|
2013-08-26 16:25:39 +00:00
|
|
|
/* Wake up the bridge channel thread. */
|
|
|
|
ast_queue_frame(bridge_channel->chan, &ast_null_frame);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-22 21:09:52 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Set actual cause on channel.
|
|
|
|
* \since 12.0.0
|
|
|
|
*
|
|
|
|
* \param chan Channel to set cause.
|
|
|
|
* \param cause Cause to set on channel.
|
|
|
|
* If cause <= 0 then use cause on channel if cause still <= 0 use AST_CAUSE_NORMAL_CLEARING.
|
|
|
|
*
|
|
|
|
* \return Actual cause set on channel.
|
|
|
|
*/
|
|
|
|
static int channel_set_cause(struct ast_channel *chan, int cause)
|
|
|
|
{
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
if (cause <= 0) {
|
|
|
|
cause = ast_channel_hangupcause(chan);
|
|
|
|
if (cause <= 0) {
|
|
|
|
cause = AST_CAUSE_NORMAL_CLEARING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast_channel_hangupcause_set(chan, cause);
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
return cause;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause)
|
2013-07-25 02:20:23 +00:00
|
|
|
{
|
|
|
|
if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-05-09 22:49:26 +00:00
|
|
|
ast_debug(1, "Setting %p(%s) state from:%u to:%u\n",
|
2013-07-25 02:20:23 +00:00
|
|
|
bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state,
|
|
|
|
new_state);
|
|
|
|
|
2013-08-22 21:09:52 +00:00
|
|
|
channel_set_cause(bridge_channel->chan, cause);
|
|
|
|
|
2015-12-01 22:11:07 +00:00
|
|
|
ast_channel_lock(bridge_channel->chan);
|
|
|
|
ast_bridge_vars_set(bridge_channel->chan, NULL, NULL);
|
|
|
|
ast_channel_unlock(bridge_channel->chan);
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/* Change the state on the bridge channel */
|
|
|
|
bridge_channel->state = new_state;
|
|
|
|
|
|
|
|
bridge_channel_poke(bridge_channel);
|
|
|
|
}
|
|
|
|
|
2013-08-22 21:09:52 +00:00
|
|
|
void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause)
|
|
|
|
{
|
|
|
|
ast_bridge_channel_lock(bridge_channel);
|
|
|
|
ast_bridge_channel_leave_bridge_nolock(bridge_channel, new_state, cause);
|
|
|
|
ast_bridge_channel_unlock(bridge_channel);
|
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
media formats: re-architect handling of media for performance improvements
In the old times media formats were represented using a bit field. This was
fast but had a few limitations.
1. Asterisk was limited in how many formats it could handle.
2. Formats, being a bit field, could not include any attribute information.
A format was strictly its type, e.g., "this is ulaw".
This was changed in Asterisk 10 (see
https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal for
notes on that work) which led to the creation of the ast_format structure.
This structure allowed Asterisk to handle attributes and bundle information
with a format.
Additionally, ast_format_cap was created to act as a container for multiple
formats that, together, formed the capability of some entity. Another
mechanism was added to allow logic to be registered which performed format
attribute negotiation. Everywhere throughout the codebase Asterisk was
changed to use this strategy.
Unfortunately, in software, there is no free lunch. These new capabilities
came at a cost.
Performance analysis and profiling showed that we spend an inordinate
amount of time comparing, copying, and generally manipulating formats and
their related structures. Basic prototyping has shown that a reasonably
large performance improvement could be made in this area. This patch is the
result of that project, which overhauled the media format architecture
and its usage in Asterisk to improve performance.
Generally, the new philosophy for handling formats is as follows:
* The ast_format structure is reference counted. This removed a large amount
of the memory allocations and copying that was done in prior versions.
* In order to prevent race conditions while keeping things performant, the
ast_format structure is immutable by convention and lock-free. Violate this
tenet at your peril!
* Because formats are reference counted, codecs are also reference counted.
The Asterisk core generally provides built-in codecs and caches the
ast_format structures created to represent them. Generally, to prevent
inordinate amounts of module reference bumping, codecs and formats can be
added at run-time but cannot be removed.
* All compatibility with the bit field representation of codecs/formats has
been moved to a compatibility API. The primary user of this representation
is chan_iax2, which must continue to maintain its bit-field usage of formats
for interoperability concerns.
* When a format is negotiated with attributes, or when a format cannot be
represented by one of the cached formats, a new format object is created or
cloned from an existing format. That format may have the same codec
underlying it, but is a different format than a version of the format with
different attributes or without attributes.
* While formats are reference counted objects, the reference count maintained
on the format should be manipulated with care. Formats are generally cached
and will persist for the lifetime of Asterisk and do not explicitly need
to have their lifetime modified. An exception to this is when the user of a
format does not know where the format came from *and* the user may outlive
the provider of the format. This occurs, for example, when a format is read
from a channel: the channel may have a format with attributes (hence,
non-cached) and the user of the format may last longer than the channel (if
the reference to the channel is released prior to the format's reference).
For more information on this work, see the API design notes:
https://wiki.asterisk.org/wiki/display/AST/Media+Format+Rewrite
Finally, this work was the culmination of a large number of developer's
efforts. Extra thanks goes to Corey Farrell, who took on a large amount of the
work in the Asterisk core, chan_sip, and was an invaluable resource in peer
reviews throughout this project.
There were a substantial number of patches contributed during this work; the
following issues/patch names simply reflect some of the work (and will cause
the release scripts to give attribution to the individuals who work on them).
Reviews:
https://reviewboard.asterisk.org/r/3814
https://reviewboard.asterisk.org/r/3808
https://reviewboard.asterisk.org/r/3805
https://reviewboard.asterisk.org/r/3803
https://reviewboard.asterisk.org/r/3801
https://reviewboard.asterisk.org/r/3798
https://reviewboard.asterisk.org/r/3800
https://reviewboard.asterisk.org/r/3794
https://reviewboard.asterisk.org/r/3793
https://reviewboard.asterisk.org/r/3792
https://reviewboard.asterisk.org/r/3791
https://reviewboard.asterisk.org/r/3790
https://reviewboard.asterisk.org/r/3789
https://reviewboard.asterisk.org/r/3788
https://reviewboard.asterisk.org/r/3787
https://reviewboard.asterisk.org/r/3786
https://reviewboard.asterisk.org/r/3784
https://reviewboard.asterisk.org/r/3783
https://reviewboard.asterisk.org/r/3778
https://reviewboard.asterisk.org/r/3774
https://reviewboard.asterisk.org/r/3775
https://reviewboard.asterisk.org/r/3772
https://reviewboard.asterisk.org/r/3761
https://reviewboard.asterisk.org/r/3754
https://reviewboard.asterisk.org/r/3753
https://reviewboard.asterisk.org/r/3751
https://reviewboard.asterisk.org/r/3750
https://reviewboard.asterisk.org/r/3748
https://reviewboard.asterisk.org/r/3747
https://reviewboard.asterisk.org/r/3746
https://reviewboard.asterisk.org/r/3742
https://reviewboard.asterisk.org/r/3740
https://reviewboard.asterisk.org/r/3739
https://reviewboard.asterisk.org/r/3738
https://reviewboard.asterisk.org/r/3737
https://reviewboard.asterisk.org/r/3736
https://reviewboard.asterisk.org/r/3734
https://reviewboard.asterisk.org/r/3722
https://reviewboard.asterisk.org/r/3713
https://reviewboard.asterisk.org/r/3703
https://reviewboard.asterisk.org/r/3689
https://reviewboard.asterisk.org/r/3687
https://reviewboard.asterisk.org/r/3674
https://reviewboard.asterisk.org/r/3671
https://reviewboard.asterisk.org/r/3667
https://reviewboard.asterisk.org/r/3665
https://reviewboard.asterisk.org/r/3625
https://reviewboard.asterisk.org/r/3602
https://reviewboard.asterisk.org/r/3519
https://reviewboard.asterisk.org/r/3518
https://reviewboard.asterisk.org/r/3516
https://reviewboard.asterisk.org/r/3515
https://reviewboard.asterisk.org/r/3512
https://reviewboard.asterisk.org/r/3506
https://reviewboard.asterisk.org/r/3413
https://reviewboard.asterisk.org/r/3410
https://reviewboard.asterisk.org/r/3387
https://reviewboard.asterisk.org/r/3388
https://reviewboard.asterisk.org/r/3389
https://reviewboard.asterisk.org/r/3390
https://reviewboard.asterisk.org/r/3321
https://reviewboard.asterisk.org/r/3320
https://reviewboard.asterisk.org/r/3319
https://reviewboard.asterisk.org/r/3318
https://reviewboard.asterisk.org/r/3266
https://reviewboard.asterisk.org/r/3265
https://reviewboard.asterisk.org/r/3234
https://reviewboard.asterisk.org/r/3178
ASTERISK-23114 #close
Reported by: mjordan
media_formats_translation_core.diff uploaded by kharwell (License 6464)
rb3506.diff uploaded by mjordan (License 6283)
media_format_app_file.diff uploaded by kharwell (License 6464)
misc-2.diff uploaded by file (License 5000)
chan_mild-3.diff uploaded by file (License 5000)
chan_obscure.diff uploaded by file (License 5000)
jingle.diff uploaded by file (License 5000)
funcs.diff uploaded by file (License 5000)
formats.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
bridges.diff uploaded by file (License 5000)
mf-codecs-2.diff uploaded by file (License 5000)
mf-app_fax.diff uploaded by file (License 5000)
mf-apps-3.diff uploaded by file (License 5000)
media-formats-3.diff uploaded by file (License 5000)
ASTERISK-23715
rb3713.patch uploaded by coreyfarrell (License 5909)
rb3689.patch uploaded by mjordan (License 6283)
ASTERISK-23957
rb3722.patch uploaded by mjordan (License 6283)
mf-attributes-3.diff uploaded by file (License 5000)
ASTERISK-23958
Tested by: jrose
rb3822.patch uploaded by coreyfarrell (License 5909)
rb3800.patch uploaded by jrose (License 6182)
chan_sip.diff uploaded by mjordan (License 6283)
rb3747.patch uploaded by jrose (License 6182)
ASTERISK-23959 #close
Tested by: sgriepentrog, mjordan, coreyfarrell
sip_cleanup.diff uploaded by opticron (License 6273)
chan_sip_caps.diff uploaded by mjordan (License 6283)
rb3751.patch uploaded by coreyfarrell (License 5909)
chan_sip-3.diff uploaded by file (License 5000)
ASTERISK-23960 #close
Tested by: opticron
direct_media.diff uploaded by opticron (License 6273)
pjsip-direct-media.diff uploaded by file (License 5000)
format_cap_remove.diff uploaded by opticron (License 6273)
media_format_fixes.diff uploaded by opticron (License 6273)
chan_pjsip-2.diff uploaded by file (License 5000)
ASTERISK-23966 #close
Tested by: rmudgett
rb3803.patch uploaded by rmudgetti (License 5621)
chan_dahdi.diff uploaded by file (License 5000)
ASTERISK-24064 #close
Tested by: coreyfarrell, mjordan, opticron, file, rmudgett, sgriepentrog, jrose
rb3814.patch uploaded by rmudgett (License 5621)
moh_cleanup.diff uploaded by opticron (License 6273)
bridge_leak.diff uploaded by opticron (License 6273)
translate.diff uploaded by file (License 5000)
rb3795.patch uploaded by rmudgett (License 5621)
tls_fix.diff uploaded by mjordan (License 6283)
fax-mf-fix-2.diff uploaded by file (License 5000)
rtp_transfer_stuff uploaded by mjordan (License 6283)
rb3787.patch uploaded by rmudgett (License 5621)
media-formats-explicit-translate-format-3.diff uploaded by file (License 5000)
format_cache_case_fix.diff uploaded by opticron (License 6273)
rb3774.patch uploaded by rmudgett (License 5621)
rb3775.patch uploaded by rmudgett (License 5621)
rtp_engine_fix.diff uploaded by opticron (License 6273)
rtp_crash_fix.diff uploaded by opticron (License 6273)
rb3753.patch uploaded by mjordan (License 6283)
rb3750.patch uploaded by mjordan (License 6283)
rb3748.patch uploaded by rmudgett (License 5621)
media_format_fixes.diff uploaded by opticron (License 6273)
rb3740.patch uploaded by mjordan (License 6283)
rb3739.patch uploaded by mjordan (License 6283)
rb3734.patch uploaded by mjordan (License 6283)
rb3689.patch uploaded by mjordan (License 6283)
rb3674.patch uploaded by coreyfarrell (License 5909)
rb3671.patch uploaded by coreyfarrell (License 5909)
rb3667.patch uploaded by coreyfarrell (License 5909)
rb3665.patch uploaded by mjordan (License 6283)
rb3625.patch uploaded by coreyfarrell (License 5909)
rb3602.patch uploaded by coreyfarrell (License 5909)
format_compatibility-2.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419044 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-20 22:06:33 +00:00
|
|
|
ast_assert(bridge_channel->read_format != NULL);
|
|
|
|
ast_assert(bridge_channel->write_format != NULL);
|
|
|
|
|
2014-08-20 22:52:44 +00:00
|
|
|
ast_channel_lock(bridge_channel->chan);
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/* Restore original formats of the channel as they came in */
|
media formats: re-architect handling of media for performance improvements
In the old times media formats were represented using a bit field. This was
fast but had a few limitations.
1. Asterisk was limited in how many formats it could handle.
2. Formats, being a bit field, could not include any attribute information.
A format was strictly its type, e.g., "this is ulaw".
This was changed in Asterisk 10 (see
https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal for
notes on that work) which led to the creation of the ast_format structure.
This structure allowed Asterisk to handle attributes and bundle information
with a format.
Additionally, ast_format_cap was created to act as a container for multiple
formats that, together, formed the capability of some entity. Another
mechanism was added to allow logic to be registered which performed format
attribute negotiation. Everywhere throughout the codebase Asterisk was
changed to use this strategy.
Unfortunately, in software, there is no free lunch. These new capabilities
came at a cost.
Performance analysis and profiling showed that we spend an inordinate
amount of time comparing, copying, and generally manipulating formats and
their related structures. Basic prototyping has shown that a reasonably
large performance improvement could be made in this area. This patch is the
result of that project, which overhauled the media format architecture
and its usage in Asterisk to improve performance.
Generally, the new philosophy for handling formats is as follows:
* The ast_format structure is reference counted. This removed a large amount
of the memory allocations and copying that was done in prior versions.
* In order to prevent race conditions while keeping things performant, the
ast_format structure is immutable by convention and lock-free. Violate this
tenet at your peril!
* Because formats are reference counted, codecs are also reference counted.
The Asterisk core generally provides built-in codecs and caches the
ast_format structures created to represent them. Generally, to prevent
inordinate amounts of module reference bumping, codecs and formats can be
added at run-time but cannot be removed.
* All compatibility with the bit field representation of codecs/formats has
been moved to a compatibility API. The primary user of this representation
is chan_iax2, which must continue to maintain its bit-field usage of formats
for interoperability concerns.
* When a format is negotiated with attributes, or when a format cannot be
represented by one of the cached formats, a new format object is created or
cloned from an existing format. That format may have the same codec
underlying it, but is a different format than a version of the format with
different attributes or without attributes.
* While formats are reference counted objects, the reference count maintained
on the format should be manipulated with care. Formats are generally cached
and will persist for the lifetime of Asterisk and do not explicitly need
to have their lifetime modified. An exception to this is when the user of a
format does not know where the format came from *and* the user may outlive
the provider of the format. This occurs, for example, when a format is read
from a channel: the channel may have a format with attributes (hence,
non-cached) and the user of the format may last longer than the channel (if
the reference to the channel is released prior to the format's reference).
For more information on this work, see the API design notes:
https://wiki.asterisk.org/wiki/display/AST/Media+Format+Rewrite
Finally, this work was the culmination of a large number of developer's
efforts. Extra thanks goes to Corey Farrell, who took on a large amount of the
work in the Asterisk core, chan_sip, and was an invaluable resource in peer
reviews throughout this project.
There were a substantial number of patches contributed during this work; the
following issues/patch names simply reflect some of the work (and will cause
the release scripts to give attribution to the individuals who work on them).
Reviews:
https://reviewboard.asterisk.org/r/3814
https://reviewboard.asterisk.org/r/3808
https://reviewboard.asterisk.org/r/3805
https://reviewboard.asterisk.org/r/3803
https://reviewboard.asterisk.org/r/3801
https://reviewboard.asterisk.org/r/3798
https://reviewboard.asterisk.org/r/3800
https://reviewboard.asterisk.org/r/3794
https://reviewboard.asterisk.org/r/3793
https://reviewboard.asterisk.org/r/3792
https://reviewboard.asterisk.org/r/3791
https://reviewboard.asterisk.org/r/3790
https://reviewboard.asterisk.org/r/3789
https://reviewboard.asterisk.org/r/3788
https://reviewboard.asterisk.org/r/3787
https://reviewboard.asterisk.org/r/3786
https://reviewboard.asterisk.org/r/3784
https://reviewboard.asterisk.org/r/3783
https://reviewboard.asterisk.org/r/3778
https://reviewboard.asterisk.org/r/3774
https://reviewboard.asterisk.org/r/3775
https://reviewboard.asterisk.org/r/3772
https://reviewboard.asterisk.org/r/3761
https://reviewboard.asterisk.org/r/3754
https://reviewboard.asterisk.org/r/3753
https://reviewboard.asterisk.org/r/3751
https://reviewboard.asterisk.org/r/3750
https://reviewboard.asterisk.org/r/3748
https://reviewboard.asterisk.org/r/3747
https://reviewboard.asterisk.org/r/3746
https://reviewboard.asterisk.org/r/3742
https://reviewboard.asterisk.org/r/3740
https://reviewboard.asterisk.org/r/3739
https://reviewboard.asterisk.org/r/3738
https://reviewboard.asterisk.org/r/3737
https://reviewboard.asterisk.org/r/3736
https://reviewboard.asterisk.org/r/3734
https://reviewboard.asterisk.org/r/3722
https://reviewboard.asterisk.org/r/3713
https://reviewboard.asterisk.org/r/3703
https://reviewboard.asterisk.org/r/3689
https://reviewboard.asterisk.org/r/3687
https://reviewboard.asterisk.org/r/3674
https://reviewboard.asterisk.org/r/3671
https://reviewboard.asterisk.org/r/3667
https://reviewboard.asterisk.org/r/3665
https://reviewboard.asterisk.org/r/3625
https://reviewboard.asterisk.org/r/3602
https://reviewboard.asterisk.org/r/3519
https://reviewboard.asterisk.org/r/3518
https://reviewboard.asterisk.org/r/3516
https://reviewboard.asterisk.org/r/3515
https://reviewboard.asterisk.org/r/3512
https://reviewboard.asterisk.org/r/3506
https://reviewboard.asterisk.org/r/3413
https://reviewboard.asterisk.org/r/3410
https://reviewboard.asterisk.org/r/3387
https://reviewboard.asterisk.org/r/3388
https://reviewboard.asterisk.org/r/3389
https://reviewboard.asterisk.org/r/3390
https://reviewboard.asterisk.org/r/3321
https://reviewboard.asterisk.org/r/3320
https://reviewboard.asterisk.org/r/3319
https://reviewboard.asterisk.org/r/3318
https://reviewboard.asterisk.org/r/3266
https://reviewboard.asterisk.org/r/3265
https://reviewboard.asterisk.org/r/3234
https://reviewboard.asterisk.org/r/3178
ASTERISK-23114 #close
Reported by: mjordan
media_formats_translation_core.diff uploaded by kharwell (License 6464)
rb3506.diff uploaded by mjordan (License 6283)
media_format_app_file.diff uploaded by kharwell (License 6464)
misc-2.diff uploaded by file (License 5000)
chan_mild-3.diff uploaded by file (License 5000)
chan_obscure.diff uploaded by file (License 5000)
jingle.diff uploaded by file (License 5000)
funcs.diff uploaded by file (License 5000)
formats.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
bridges.diff uploaded by file (License 5000)
mf-codecs-2.diff uploaded by file (License 5000)
mf-app_fax.diff uploaded by file (License 5000)
mf-apps-3.diff uploaded by file (License 5000)
media-formats-3.diff uploaded by file (License 5000)
ASTERISK-23715
rb3713.patch uploaded by coreyfarrell (License 5909)
rb3689.patch uploaded by mjordan (License 6283)
ASTERISK-23957
rb3722.patch uploaded by mjordan (License 6283)
mf-attributes-3.diff uploaded by file (License 5000)
ASTERISK-23958
Tested by: jrose
rb3822.patch uploaded by coreyfarrell (License 5909)
rb3800.patch uploaded by jrose (License 6182)
chan_sip.diff uploaded by mjordan (License 6283)
rb3747.patch uploaded by jrose (License 6182)
ASTERISK-23959 #close
Tested by: sgriepentrog, mjordan, coreyfarrell
sip_cleanup.diff uploaded by opticron (License 6273)
chan_sip_caps.diff uploaded by mjordan (License 6283)
rb3751.patch uploaded by coreyfarrell (License 5909)
chan_sip-3.diff uploaded by file (License 5000)
ASTERISK-23960 #close
Tested by: opticron
direct_media.diff uploaded by opticron (License 6273)
pjsip-direct-media.diff uploaded by file (License 5000)
format_cap_remove.diff uploaded by opticron (License 6273)
media_format_fixes.diff uploaded by opticron (License 6273)
chan_pjsip-2.diff uploaded by file (License 5000)
ASTERISK-23966 #close
Tested by: rmudgett
rb3803.patch uploaded by rmudgetti (License 5621)
chan_dahdi.diff uploaded by file (License 5000)
ASTERISK-24064 #close
Tested by: coreyfarrell, mjordan, opticron, file, rmudgett, sgriepentrog, jrose
rb3814.patch uploaded by rmudgett (License 5621)
moh_cleanup.diff uploaded by opticron (License 6273)
bridge_leak.diff uploaded by opticron (License 6273)
translate.diff uploaded by file (License 5000)
rb3795.patch uploaded by rmudgett (License 5621)
tls_fix.diff uploaded by mjordan (License 6283)
fax-mf-fix-2.diff uploaded by file (License 5000)
rtp_transfer_stuff uploaded by mjordan (License 6283)
rb3787.patch uploaded by rmudgett (License 5621)
media-formats-explicit-translate-format-3.diff uploaded by file (License 5000)
format_cache_case_fix.diff uploaded by opticron (License 6273)
rb3774.patch uploaded by rmudgett (License 5621)
rb3775.patch uploaded by rmudgett (License 5621)
rtp_engine_fix.diff uploaded by opticron (License 6273)
rtp_crash_fix.diff uploaded by opticron (License 6273)
rb3753.patch uploaded by mjordan (License 6283)
rb3750.patch uploaded by mjordan (License 6283)
rb3748.patch uploaded by rmudgett (License 5621)
media_format_fixes.diff uploaded by opticron (License 6273)
rb3740.patch uploaded by mjordan (License 6283)
rb3739.patch uploaded by mjordan (License 6283)
rb3734.patch uploaded by mjordan (License 6283)
rb3689.patch uploaded by mjordan (License 6283)
rb3674.patch uploaded by coreyfarrell (License 5909)
rb3671.patch uploaded by coreyfarrell (License 5909)
rb3667.patch uploaded by coreyfarrell (License 5909)
rb3665.patch uploaded by mjordan (License 6283)
rb3625.patch uploaded by coreyfarrell (License 5909)
rb3602.patch uploaded by coreyfarrell (License 5909)
format_compatibility-2.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419044 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-20 22:06:33 +00:00
|
|
|
if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) {
|
2013-07-25 02:20:23 +00:00
|
|
|
ast_debug(1, "Bridge is returning %p(%s) to read format %s\n",
|
|
|
|
bridge_channel, ast_channel_name(bridge_channel->chan),
|
media formats: re-architect handling of media for performance improvements
In the old times media formats were represented using a bit field. This was
fast but had a few limitations.
1. Asterisk was limited in how many formats it could handle.
2. Formats, being a bit field, could not include any attribute information.
A format was strictly its type, e.g., "this is ulaw".
This was changed in Asterisk 10 (see
https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal for
notes on that work) which led to the creation of the ast_format structure.
This structure allowed Asterisk to handle attributes and bundle information
with a format.
Additionally, ast_format_cap was created to act as a container for multiple
formats that, together, formed the capability of some entity. Another
mechanism was added to allow logic to be registered which performed format
attribute negotiation. Everywhere throughout the codebase Asterisk was
changed to use this strategy.
Unfortunately, in software, there is no free lunch. These new capabilities
came at a cost.
Performance analysis and profiling showed that we spend an inordinate
amount of time comparing, copying, and generally manipulating formats and
their related structures. Basic prototyping has shown that a reasonably
large performance improvement could be made in this area. This patch is the
result of that project, which overhauled the media format architecture
and its usage in Asterisk to improve performance.
Generally, the new philosophy for handling formats is as follows:
* The ast_format structure is reference counted. This removed a large amount
of the memory allocations and copying that was done in prior versions.
* In order to prevent race conditions while keeping things performant, the
ast_format structure is immutable by convention and lock-free. Violate this
tenet at your peril!
* Because formats are reference counted, codecs are also reference counted.
The Asterisk core generally provides built-in codecs and caches the
ast_format structures created to represent them. Generally, to prevent
inordinate amounts of module reference bumping, codecs and formats can be
added at run-time but cannot be removed.
* All compatibility with the bit field representation of codecs/formats has
been moved to a compatibility API. The primary user of this representation
is chan_iax2, which must continue to maintain its bit-field usage of formats
for interoperability concerns.
* When a format is negotiated with attributes, or when a format cannot be
represented by one of the cached formats, a new format object is created or
cloned from an existing format. That format may have the same codec
underlying it, but is a different format than a version of the format with
different attributes or without attributes.
* While formats are reference counted objects, the reference count maintained
on the format should be manipulated with care. Formats are generally cached
and will persist for the lifetime of Asterisk and do not explicitly need
to have their lifetime modified. An exception to this is when the user of a
format does not know where the format came from *and* the user may outlive
the provider of the format. This occurs, for example, when a format is read
from a channel: the channel may have a format with attributes (hence,
non-cached) and the user of the format may last longer than the channel (if
the reference to the channel is released prior to the format's reference).
For more information on this work, see the API design notes:
https://wiki.asterisk.org/wiki/display/AST/Media+Format+Rewrite
Finally, this work was the culmination of a large number of developer's
efforts. Extra thanks goes to Corey Farrell, who took on a large amount of the
work in the Asterisk core, chan_sip, and was an invaluable resource in peer
reviews throughout this project.
There were a substantial number of patches contributed during this work; the
following issues/patch names simply reflect some of the work (and will cause
the release scripts to give attribution to the individuals who work on them).
Reviews:
https://reviewboard.asterisk.org/r/3814
https://reviewboard.asterisk.org/r/3808
https://reviewboard.asterisk.org/r/3805
https://reviewboard.asterisk.org/r/3803
https://reviewboard.asterisk.org/r/3801
https://reviewboard.asterisk.org/r/3798
https://reviewboard.asterisk.org/r/3800
https://reviewboard.asterisk.org/r/3794
https://reviewboard.asterisk.org/r/3793
https://reviewboard.asterisk.org/r/3792
https://reviewboard.asterisk.org/r/3791
https://reviewboard.asterisk.org/r/3790
https://reviewboard.asterisk.org/r/3789
https://reviewboard.asterisk.org/r/3788
https://reviewboard.asterisk.org/r/3787
https://reviewboard.asterisk.org/r/3786
https://reviewboard.asterisk.org/r/3784
https://reviewboard.asterisk.org/r/3783
https://reviewboard.asterisk.org/r/3778
https://reviewboard.asterisk.org/r/3774
https://reviewboard.asterisk.org/r/3775
https://reviewboard.asterisk.org/r/3772
https://reviewboard.asterisk.org/r/3761
https://reviewboard.asterisk.org/r/3754
https://reviewboard.asterisk.org/r/3753
https://reviewboard.asterisk.org/r/3751
https://reviewboard.asterisk.org/r/3750
https://reviewboard.asterisk.org/r/3748
https://reviewboard.asterisk.org/r/3747
https://reviewboard.asterisk.org/r/3746
https://reviewboard.asterisk.org/r/3742
https://reviewboard.asterisk.org/r/3740
https://reviewboard.asterisk.org/r/3739
https://reviewboard.asterisk.org/r/3738
https://reviewboard.asterisk.org/r/3737
https://reviewboard.asterisk.org/r/3736
https://reviewboard.asterisk.org/r/3734
https://reviewboard.asterisk.org/r/3722
https://reviewboard.asterisk.org/r/3713
https://reviewboard.asterisk.org/r/3703
https://reviewboard.asterisk.org/r/3689
https://reviewboard.asterisk.org/r/3687
https://reviewboard.asterisk.org/r/3674
https://reviewboard.asterisk.org/r/3671
https://reviewboard.asterisk.org/r/3667
https://reviewboard.asterisk.org/r/3665
https://reviewboard.asterisk.org/r/3625
https://reviewboard.asterisk.org/r/3602
https://reviewboard.asterisk.org/r/3519
https://reviewboard.asterisk.org/r/3518
https://reviewboard.asterisk.org/r/3516
https://reviewboard.asterisk.org/r/3515
https://reviewboard.asterisk.org/r/3512
https://reviewboard.asterisk.org/r/3506
https://reviewboard.asterisk.org/r/3413
https://reviewboard.asterisk.org/r/3410
https://reviewboard.asterisk.org/r/3387
https://reviewboard.asterisk.org/r/3388
https://reviewboard.asterisk.org/r/3389
https://reviewboard.asterisk.org/r/3390
https://reviewboard.asterisk.org/r/3321
https://reviewboard.asterisk.org/r/3320
https://reviewboard.asterisk.org/r/3319
https://reviewboard.asterisk.org/r/3318
https://reviewboard.asterisk.org/r/3266
https://reviewboard.asterisk.org/r/3265
https://reviewboard.asterisk.org/r/3234
https://reviewboard.asterisk.org/r/3178
ASTERISK-23114 #close
Reported by: mjordan
media_formats_translation_core.diff uploaded by kharwell (License 6464)
rb3506.diff uploaded by mjordan (License 6283)
media_format_app_file.diff uploaded by kharwell (License 6464)
misc-2.diff uploaded by file (License 5000)
chan_mild-3.diff uploaded by file (License 5000)
chan_obscure.diff uploaded by file (License 5000)
jingle.diff uploaded by file (License 5000)
funcs.diff uploaded by file (License 5000)
formats.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
bridges.diff uploaded by file (License 5000)
mf-codecs-2.diff uploaded by file (License 5000)
mf-app_fax.diff uploaded by file (License 5000)
mf-apps-3.diff uploaded by file (License 5000)
media-formats-3.diff uploaded by file (License 5000)
ASTERISK-23715
rb3713.patch uploaded by coreyfarrell (License 5909)
rb3689.patch uploaded by mjordan (License 6283)
ASTERISK-23957
rb3722.patch uploaded by mjordan (License 6283)
mf-attributes-3.diff uploaded by file (License 5000)
ASTERISK-23958
Tested by: jrose
rb3822.patch uploaded by coreyfarrell (License 5909)
rb3800.patch uploaded by jrose (License 6182)
chan_sip.diff uploaded by mjordan (License 6283)
rb3747.patch uploaded by jrose (License 6182)
ASTERISK-23959 #close
Tested by: sgriepentrog, mjordan, coreyfarrell
sip_cleanup.diff uploaded by opticron (License 6273)
chan_sip_caps.diff uploaded by mjordan (License 6283)
rb3751.patch uploaded by coreyfarrell (License 5909)
chan_sip-3.diff uploaded by file (License 5000)
ASTERISK-23960 #close
Tested by: opticron
direct_media.diff uploaded by opticron (License 6273)
pjsip-direct-media.diff uploaded by file (License 5000)
format_cap_remove.diff uploaded by opticron (License 6273)
media_format_fixes.diff uploaded by opticron (License 6273)
chan_pjsip-2.diff uploaded by file (License 5000)
ASTERISK-23966 #close
Tested by: rmudgett
rb3803.patch uploaded by rmudgetti (License 5621)
chan_dahdi.diff uploaded by file (License 5000)
ASTERISK-24064 #close
Tested by: coreyfarrell, mjordan, opticron, file, rmudgett, sgriepentrog, jrose
rb3814.patch uploaded by rmudgett (License 5621)
moh_cleanup.diff uploaded by opticron (License 6273)
bridge_leak.diff uploaded by opticron (License 6273)
translate.diff uploaded by file (License 5000)
rb3795.patch uploaded by rmudgett (License 5621)
tls_fix.diff uploaded by mjordan (License 6283)
fax-mf-fix-2.diff uploaded by file (License 5000)
rtp_transfer_stuff uploaded by mjordan (License 6283)
rb3787.patch uploaded by rmudgett (License 5621)
media-formats-explicit-translate-format-3.diff uploaded by file (License 5000)
format_cache_case_fix.diff uploaded by opticron (License 6273)
rb3774.patch uploaded by rmudgett (License 5621)
rb3775.patch uploaded by rmudgett (License 5621)
rtp_engine_fix.diff uploaded by opticron (License 6273)
rtp_crash_fix.diff uploaded by opticron (License 6273)
rb3753.patch uploaded by mjordan (License 6283)
rb3750.patch uploaded by mjordan (License 6283)
rb3748.patch uploaded by rmudgett (License 5621)
media_format_fixes.diff uploaded by opticron (License 6273)
rb3740.patch uploaded by mjordan (License 6283)
rb3739.patch uploaded by mjordan (License 6283)
rb3734.patch uploaded by mjordan (License 6283)
rb3689.patch uploaded by mjordan (License 6283)
rb3674.patch uploaded by coreyfarrell (License 5909)
rb3671.patch uploaded by coreyfarrell (License 5909)
rb3667.patch uploaded by coreyfarrell (License 5909)
rb3665.patch uploaded by mjordan (License 6283)
rb3625.patch uploaded by coreyfarrell (License 5909)
rb3602.patch uploaded by coreyfarrell (License 5909)
format_compatibility-2.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419044 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-20 22:06:33 +00:00
|
|
|
ast_format_get_name(bridge_channel->read_format));
|
|
|
|
if (ast_set_read_format(bridge_channel->chan, bridge_channel->read_format)) {
|
2013-07-25 02:20:23 +00:00
|
|
|
ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n",
|
|
|
|
bridge_channel, ast_channel_name(bridge_channel->chan),
|
media formats: re-architect handling of media for performance improvements
In the old times media formats were represented using a bit field. This was
fast but had a few limitations.
1. Asterisk was limited in how many formats it could handle.
2. Formats, being a bit field, could not include any attribute information.
A format was strictly its type, e.g., "this is ulaw".
This was changed in Asterisk 10 (see
https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal for
notes on that work) which led to the creation of the ast_format structure.
This structure allowed Asterisk to handle attributes and bundle information
with a format.
Additionally, ast_format_cap was created to act as a container for multiple
formats that, together, formed the capability of some entity. Another
mechanism was added to allow logic to be registered which performed format
attribute negotiation. Everywhere throughout the codebase Asterisk was
changed to use this strategy.
Unfortunately, in software, there is no free lunch. These new capabilities
came at a cost.
Performance analysis and profiling showed that we spend an inordinate
amount of time comparing, copying, and generally manipulating formats and
their related structures. Basic prototyping has shown that a reasonably
large performance improvement could be made in this area. This patch is the
result of that project, which overhauled the media format architecture
and its usage in Asterisk to improve performance.
Generally, the new philosophy for handling formats is as follows:
* The ast_format structure is reference counted. This removed a large amount
of the memory allocations and copying that was done in prior versions.
* In order to prevent race conditions while keeping things performant, the
ast_format structure is immutable by convention and lock-free. Violate this
tenet at your peril!
* Because formats are reference counted, codecs are also reference counted.
The Asterisk core generally provides built-in codecs and caches the
ast_format structures created to represent them. Generally, to prevent
inordinate amounts of module reference bumping, codecs and formats can be
added at run-time but cannot be removed.
* All compatibility with the bit field representation of codecs/formats has
been moved to a compatibility API. The primary user of this representation
is chan_iax2, which must continue to maintain its bit-field usage of formats
for interoperability concerns.
* When a format is negotiated with attributes, or when a format cannot be
represented by one of the cached formats, a new format object is created or
cloned from an existing format. That format may have the same codec
underlying it, but is a different format than a version of the format with
different attributes or without attributes.
* While formats are reference counted objects, the reference count maintained
on the format should be manipulated with care. Formats are generally cached
and will persist for the lifetime of Asterisk and do not explicitly need
to have their lifetime modified. An exception to this is when the user of a
format does not know where the format came from *and* the user may outlive
the provider of the format. This occurs, for example, when a format is read
from a channel: the channel may have a format with attributes (hence,
non-cached) and the user of the format may last longer than the channel (if
the reference to the channel is released prior to the format's reference).
For more information on this work, see the API design notes:
https://wiki.asterisk.org/wiki/display/AST/Media+Format+Rewrite
Finally, this work was the culmination of a large number of developer's
efforts. Extra thanks goes to Corey Farrell, who took on a large amount of the
work in the Asterisk core, chan_sip, and was an invaluable resource in peer
reviews throughout this project.
There were a substantial number of patches contributed during this work; the
following issues/patch names simply reflect some of the work (and will cause
the release scripts to give attribution to the individuals who work on them).
Reviews:
https://reviewboard.asterisk.org/r/3814
https://reviewboard.asterisk.org/r/3808
https://reviewboard.asterisk.org/r/3805
https://reviewboard.asterisk.org/r/3803
https://reviewboard.asterisk.org/r/3801
https://reviewboard.asterisk.org/r/3798
https://reviewboard.asterisk.org/r/3800
https://reviewboard.asterisk.org/r/3794
https://reviewboard.asterisk.org/r/3793
https://reviewboard.asterisk.org/r/3792
https://reviewboard.asterisk.org/r/3791
https://reviewboard.asterisk.org/r/3790
https://reviewboard.asterisk.org/r/3789
https://reviewboard.asterisk.org/r/3788
https://reviewboard.asterisk.org/r/3787
https://reviewboard.asterisk.org/r/3786
https://reviewboard.asterisk.org/r/3784
https://reviewboard.asterisk.org/r/3783
https://reviewboard.asterisk.org/r/3778
https://reviewboard.asterisk.org/r/3774
https://reviewboard.asterisk.org/r/3775
https://reviewboard.asterisk.org/r/3772
https://reviewboard.asterisk.org/r/3761
https://reviewboard.asterisk.org/r/3754
https://reviewboard.asterisk.org/r/3753
https://reviewboard.asterisk.org/r/3751
https://reviewboard.asterisk.org/r/3750
https://reviewboard.asterisk.org/r/3748
https://reviewboard.asterisk.org/r/3747
https://reviewboard.asterisk.org/r/3746
https://reviewboard.asterisk.org/r/3742
https://reviewboard.asterisk.org/r/3740
https://reviewboard.asterisk.org/r/3739
https://reviewboard.asterisk.org/r/3738
https://reviewboard.asterisk.org/r/3737
https://reviewboard.asterisk.org/r/3736
https://reviewboard.asterisk.org/r/3734
https://reviewboard.asterisk.org/r/3722
https://reviewboard.asterisk.org/r/3713
https://reviewboard.asterisk.org/r/3703
https://reviewboard.asterisk.org/r/3689
https://reviewboard.asterisk.org/r/3687
https://reviewboard.asterisk.org/r/3674
https://reviewboard.asterisk.org/r/3671
https://reviewboard.asterisk.org/r/3667
https://reviewboard.asterisk.org/r/3665
https://reviewboard.asterisk.org/r/3625
https://reviewboard.asterisk.org/r/3602
https://reviewboard.asterisk.org/r/3519
https://reviewboard.asterisk.org/r/3518
https://reviewboard.asterisk.org/r/3516
https://reviewboard.asterisk.org/r/3515
https://reviewboard.asterisk.org/r/3512
https://reviewboard.asterisk.org/r/3506
https://reviewboard.asterisk.org/r/3413
https://reviewboard.asterisk.org/r/3410
https://reviewboard.asterisk.org/r/3387
https://reviewboard.asterisk.org/r/3388
https://reviewboard.asterisk.org/r/3389
https://reviewboard.asterisk.org/r/3390
https://reviewboard.asterisk.org/r/3321
https://reviewboard.asterisk.org/r/3320
https://reviewboard.asterisk.org/r/3319
https://reviewboard.asterisk.org/r/3318
https://reviewboard.asterisk.org/r/3266
https://reviewboard.asterisk.org/r/3265
https://reviewboard.asterisk.org/r/3234
https://reviewboard.asterisk.org/r/3178
ASTERISK-23114 #close
Reported by: mjordan
media_formats_translation_core.diff uploaded by kharwell (License 6464)
rb3506.diff uploaded by mjordan (License 6283)
media_format_app_file.diff uploaded by kharwell (License 6464)
misc-2.diff uploaded by file (License 5000)
chan_mild-3.diff uploaded by file (License 5000)
chan_obscure.diff uploaded by file (License 5000)
jingle.diff uploaded by file (License 5000)
funcs.diff uploaded by file (License 5000)
formats.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
bridges.diff uploaded by file (License 5000)
mf-codecs-2.diff uploaded by file (License 5000)
mf-app_fax.diff uploaded by file (License 5000)
mf-apps-3.diff uploaded by file (License 5000)
media-formats-3.diff uploaded by file (License 5000)
ASTERISK-23715
rb3713.patch uploaded by coreyfarrell (License 5909)
rb3689.patch uploaded by mjordan (License 6283)
ASTERISK-23957
rb3722.patch uploaded by mjordan (License 6283)
mf-attributes-3.diff uploaded by file (License 5000)
ASTERISK-23958
Tested by: jrose
rb3822.patch uploaded by coreyfarrell (License 5909)
rb3800.patch uploaded by jrose (License 6182)
chan_sip.diff uploaded by mjordan (License 6283)
rb3747.patch uploaded by jrose (License 6182)
ASTERISK-23959 #close
Tested by: sgriepentrog, mjordan, coreyfarrell
sip_cleanup.diff uploaded by opticron (License 6273)
chan_sip_caps.diff uploaded by mjordan (License 6283)
rb3751.patch uploaded by coreyfarrell (License 5909)
chan_sip-3.diff uploaded by file (License 5000)
ASTERISK-23960 #close
Tested by: opticron
direct_media.diff uploaded by opticron (License 6273)
pjsip-direct-media.diff uploaded by file (License 5000)
format_cap_remove.diff uploaded by opticron (License 6273)
media_format_fixes.diff uploaded by opticron (License 6273)
chan_pjsip-2.diff uploaded by file (License 5000)
ASTERISK-23966 #close
Tested by: rmudgett
rb3803.patch uploaded by rmudgetti (License 5621)
chan_dahdi.diff uploaded by file (License 5000)
ASTERISK-24064 #close
Tested by: coreyfarrell, mjordan, opticron, file, rmudgett, sgriepentrog, jrose
rb3814.patch uploaded by rmudgett (License 5621)
moh_cleanup.diff uploaded by opticron (License 6273)
bridge_leak.diff uploaded by opticron (License 6273)
translate.diff uploaded by file (License 5000)
rb3795.patch uploaded by rmudgett (License 5621)
tls_fix.diff uploaded by mjordan (License 6283)
fax-mf-fix-2.diff uploaded by file (License 5000)
rtp_transfer_stuff uploaded by mjordan (License 6283)
rb3787.patch uploaded by rmudgett (License 5621)
media-formats-explicit-translate-format-3.diff uploaded by file (License 5000)
format_cache_case_fix.diff uploaded by opticron (License 6273)
rb3774.patch uploaded by rmudgett (License 5621)
rb3775.patch uploaded by rmudgett (License 5621)
rtp_engine_fix.diff uploaded by opticron (License 6273)
rtp_crash_fix.diff uploaded by opticron (License 6273)
rb3753.patch uploaded by mjordan (License 6283)
rb3750.patch uploaded by mjordan (License 6283)
rb3748.patch uploaded by rmudgett (License 5621)
media_format_fixes.diff uploaded by opticron (License 6273)
rb3740.patch uploaded by mjordan (License 6283)
rb3739.patch uploaded by mjordan (License 6283)
rb3734.patch uploaded by mjordan (License 6283)
rb3689.patch uploaded by mjordan (License 6283)
rb3674.patch uploaded by coreyfarrell (License 5909)
rb3671.patch uploaded by coreyfarrell (License 5909)
rb3667.patch uploaded by coreyfarrell (License 5909)
rb3665.patch uploaded by mjordan (License 6283)
rb3625.patch uploaded by coreyfarrell (License 5909)
rb3602.patch uploaded by coreyfarrell (License 5909)
format_compatibility-2.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419044 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-20 22:06:33 +00:00
|
|
|
ast_format_get_name(bridge_channel->read_format));
|
2013-07-25 02:20:23 +00:00
|
|
|
}
|
|
|
|
}
|
media formats: re-architect handling of media for performance improvements
In the old times media formats were represented using a bit field. This was
fast but had a few limitations.
1. Asterisk was limited in how many formats it could handle.
2. Formats, being a bit field, could not include any attribute information.
A format was strictly its type, e.g., "this is ulaw".
This was changed in Asterisk 10 (see
https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal for
notes on that work) which led to the creation of the ast_format structure.
This structure allowed Asterisk to handle attributes and bundle information
with a format.
Additionally, ast_format_cap was created to act as a container for multiple
formats that, together, formed the capability of some entity. Another
mechanism was added to allow logic to be registered which performed format
attribute negotiation. Everywhere throughout the codebase Asterisk was
changed to use this strategy.
Unfortunately, in software, there is no free lunch. These new capabilities
came at a cost.
Performance analysis and profiling showed that we spend an inordinate
amount of time comparing, copying, and generally manipulating formats and
their related structures. Basic prototyping has shown that a reasonably
large performance improvement could be made in this area. This patch is the
result of that project, which overhauled the media format architecture
and its usage in Asterisk to improve performance.
Generally, the new philosophy for handling formats is as follows:
* The ast_format structure is reference counted. This removed a large amount
of the memory allocations and copying that was done in prior versions.
* In order to prevent race conditions while keeping things performant, the
ast_format structure is immutable by convention and lock-free. Violate this
tenet at your peril!
* Because formats are reference counted, codecs are also reference counted.
The Asterisk core generally provides built-in codecs and caches the
ast_format structures created to represent them. Generally, to prevent
inordinate amounts of module reference bumping, codecs and formats can be
added at run-time but cannot be removed.
* All compatibility with the bit field representation of codecs/formats has
been moved to a compatibility API. The primary user of this representation
is chan_iax2, which must continue to maintain its bit-field usage of formats
for interoperability concerns.
* When a format is negotiated with attributes, or when a format cannot be
represented by one of the cached formats, a new format object is created or
cloned from an existing format. That format may have the same codec
underlying it, but is a different format than a version of the format with
different attributes or without attributes.
* While formats are reference counted objects, the reference count maintained
on the format should be manipulated with care. Formats are generally cached
and will persist for the lifetime of Asterisk and do not explicitly need
to have their lifetime modified. An exception to this is when the user of a
format does not know where the format came from *and* the user may outlive
the provider of the format. This occurs, for example, when a format is read
from a channel: the channel may have a format with attributes (hence,
non-cached) and the user of the format may last longer than the channel (if
the reference to the channel is released prior to the format's reference).
For more information on this work, see the API design notes:
https://wiki.asterisk.org/wiki/display/AST/Media+Format+Rewrite
Finally, this work was the culmination of a large number of developer's
efforts. Extra thanks goes to Corey Farrell, who took on a large amount of the
work in the Asterisk core, chan_sip, and was an invaluable resource in peer
reviews throughout this project.
There were a substantial number of patches contributed during this work; the
following issues/patch names simply reflect some of the work (and will cause
the release scripts to give attribution to the individuals who work on them).
Reviews:
https://reviewboard.asterisk.org/r/3814
https://reviewboard.asterisk.org/r/3808
https://reviewboard.asterisk.org/r/3805
https://reviewboard.asterisk.org/r/3803
https://reviewboard.asterisk.org/r/3801
https://reviewboard.asterisk.org/r/3798
https://reviewboard.asterisk.org/r/3800
https://reviewboard.asterisk.org/r/3794
https://reviewboard.asterisk.org/r/3793
https://reviewboard.asterisk.org/r/3792
https://reviewboard.asterisk.org/r/3791
https://reviewboard.asterisk.org/r/3790
https://reviewboard.asterisk.org/r/3789
https://reviewboard.asterisk.org/r/3788
https://reviewboard.asterisk.org/r/3787
https://reviewboard.asterisk.org/r/3786
https://reviewboard.asterisk.org/r/3784
https://reviewboard.asterisk.org/r/3783
https://reviewboard.asterisk.org/r/3778
https://reviewboard.asterisk.org/r/3774
https://reviewboard.asterisk.org/r/3775
https://reviewboard.asterisk.org/r/3772
https://reviewboard.asterisk.org/r/3761
https://reviewboard.asterisk.org/r/3754
https://reviewboard.asterisk.org/r/3753
https://reviewboard.asterisk.org/r/3751
https://reviewboard.asterisk.org/r/3750
https://reviewboard.asterisk.org/r/3748
https://reviewboard.asterisk.org/r/3747
https://reviewboard.asterisk.org/r/3746
https://reviewboard.asterisk.org/r/3742
https://reviewboard.asterisk.org/r/3740
https://reviewboard.asterisk.org/r/3739
https://reviewboard.asterisk.org/r/3738
https://reviewboard.asterisk.org/r/3737
https://reviewboard.asterisk.org/r/3736
https://reviewboard.asterisk.org/r/3734
https://reviewboard.asterisk.org/r/3722
https://reviewboard.asterisk.org/r/3713
https://reviewboard.asterisk.org/r/3703
https://reviewboard.asterisk.org/r/3689
https://reviewboard.asterisk.org/r/3687
https://reviewboard.asterisk.org/r/3674
https://reviewboard.asterisk.org/r/3671
https://reviewboard.asterisk.org/r/3667
https://reviewboard.asterisk.org/r/3665
https://reviewboard.asterisk.org/r/3625
https://reviewboard.asterisk.org/r/3602
https://reviewboard.asterisk.org/r/3519
https://reviewboard.asterisk.org/r/3518
https://reviewboard.asterisk.org/r/3516
https://reviewboard.asterisk.org/r/3515
https://reviewboard.asterisk.org/r/3512
https://reviewboard.asterisk.org/r/3506
https://reviewboard.asterisk.org/r/3413
https://reviewboard.asterisk.org/r/3410
https://reviewboard.asterisk.org/r/3387
https://reviewboard.asterisk.org/r/3388
https://reviewboard.asterisk.org/r/3389
https://reviewboard.asterisk.org/r/3390
https://reviewboard.asterisk.org/r/3321
https://reviewboard.asterisk.org/r/3320
https://reviewboard.asterisk.org/r/3319
https://reviewboard.asterisk.org/r/3318
https://reviewboard.asterisk.org/r/3266
https://reviewboard.asterisk.org/r/3265
https://reviewboard.asterisk.org/r/3234
https://reviewboard.asterisk.org/r/3178
ASTERISK-23114 #close
Reported by: mjordan
media_formats_translation_core.diff uploaded by kharwell (License 6464)
rb3506.diff uploaded by mjordan (License 6283)
media_format_app_file.diff uploaded by kharwell (License 6464)
misc-2.diff uploaded by file (License 5000)
chan_mild-3.diff uploaded by file (License 5000)
chan_obscure.diff uploaded by file (License 5000)
jingle.diff uploaded by file (License 5000)
funcs.diff uploaded by file (License 5000)
formats.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
bridges.diff uploaded by file (License 5000)
mf-codecs-2.diff uploaded by file (License 5000)
mf-app_fax.diff uploaded by file (License 5000)
mf-apps-3.diff uploaded by file (License 5000)
media-formats-3.diff uploaded by file (License 5000)
ASTERISK-23715
rb3713.patch uploaded by coreyfarrell (License 5909)
rb3689.patch uploaded by mjordan (License 6283)
ASTERISK-23957
rb3722.patch uploaded by mjordan (License 6283)
mf-attributes-3.diff uploaded by file (License 5000)
ASTERISK-23958
Tested by: jrose
rb3822.patch uploaded by coreyfarrell (License 5909)
rb3800.patch uploaded by jrose (License 6182)
chan_sip.diff uploaded by mjordan (License 6283)
rb3747.patch uploaded by jrose (License 6182)
ASTERISK-23959 #close
Tested by: sgriepentrog, mjordan, coreyfarrell
sip_cleanup.diff uploaded by opticron (License 6273)
chan_sip_caps.diff uploaded by mjordan (License 6283)
rb3751.patch uploaded by coreyfarrell (License 5909)
chan_sip-3.diff uploaded by file (License 5000)
ASTERISK-23960 #close
Tested by: opticron
direct_media.diff uploaded by opticron (License 6273)
pjsip-direct-media.diff uploaded by file (License 5000)
format_cap_remove.diff uploaded by opticron (License 6273)
media_format_fixes.diff uploaded by opticron (License 6273)
chan_pjsip-2.diff uploaded by file (License 5000)
ASTERISK-23966 #close
Tested by: rmudgett
rb3803.patch uploaded by rmudgetti (License 5621)
chan_dahdi.diff uploaded by file (License 5000)
ASTERISK-24064 #close
Tested by: coreyfarrell, mjordan, opticron, file, rmudgett, sgriepentrog, jrose
rb3814.patch uploaded by rmudgett (License 5621)
moh_cleanup.diff uploaded by opticron (License 6273)
bridge_leak.diff uploaded by opticron (License 6273)
translate.diff uploaded by file (License 5000)
rb3795.patch uploaded by rmudgett (License 5621)
tls_fix.diff uploaded by mjordan (License 6283)
fax-mf-fix-2.diff uploaded by file (License 5000)
rtp_transfer_stuff uploaded by mjordan (License 6283)
rb3787.patch uploaded by rmudgett (License 5621)
media-formats-explicit-translate-format-3.diff uploaded by file (License 5000)
format_cache_case_fix.diff uploaded by opticron (License 6273)
rb3774.patch uploaded by rmudgett (License 5621)
rb3775.patch uploaded by rmudgett (License 5621)
rtp_engine_fix.diff uploaded by opticron (License 6273)
rtp_crash_fix.diff uploaded by opticron (License 6273)
rb3753.patch uploaded by mjordan (License 6283)
rb3750.patch uploaded by mjordan (License 6283)
rb3748.patch uploaded by rmudgett (License 5621)
media_format_fixes.diff uploaded by opticron (License 6273)
rb3740.patch uploaded by mjordan (License 6283)
rb3739.patch uploaded by mjordan (License 6283)
rb3734.patch uploaded by mjordan (License 6283)
rb3689.patch uploaded by mjordan (License 6283)
rb3674.patch uploaded by coreyfarrell (License 5909)
rb3671.patch uploaded by coreyfarrell (License 5909)
rb3667.patch uploaded by coreyfarrell (License 5909)
rb3665.patch uploaded by mjordan (License 6283)
rb3625.patch uploaded by coreyfarrell (License 5909)
rb3602.patch uploaded by coreyfarrell (License 5909)
format_compatibility-2.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419044 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-20 22:06:33 +00:00
|
|
|
if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) {
|
2013-07-25 02:20:23 +00:00
|
|
|
ast_debug(1, "Bridge is returning %p(%s) to write format %s\n",
|
|
|
|
bridge_channel, ast_channel_name(bridge_channel->chan),
|
media formats: re-architect handling of media for performance improvements
In the old times media formats were represented using a bit field. This was
fast but had a few limitations.
1. Asterisk was limited in how many formats it could handle.
2. Formats, being a bit field, could not include any attribute information.
A format was strictly its type, e.g., "this is ulaw".
This was changed in Asterisk 10 (see
https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal for
notes on that work) which led to the creation of the ast_format structure.
This structure allowed Asterisk to handle attributes and bundle information
with a format.
Additionally, ast_format_cap was created to act as a container for multiple
formats that, together, formed the capability of some entity. Another
mechanism was added to allow logic to be registered which performed format
attribute negotiation. Everywhere throughout the codebase Asterisk was
changed to use this strategy.
Unfortunately, in software, there is no free lunch. These new capabilities
came at a cost.
Performance analysis and profiling showed that we spend an inordinate
amount of time comparing, copying, and generally manipulating formats and
their related structures. Basic prototyping has shown that a reasonably
large performance improvement could be made in this area. This patch is the
result of that project, which overhauled the media format architecture
and its usage in Asterisk to improve performance.
Generally, the new philosophy for handling formats is as follows:
* The ast_format structure is reference counted. This removed a large amount
of the memory allocations and copying that was done in prior versions.
* In order to prevent race conditions while keeping things performant, the
ast_format structure is immutable by convention and lock-free. Violate this
tenet at your peril!
* Because formats are reference counted, codecs are also reference counted.
The Asterisk core generally provides built-in codecs and caches the
ast_format structures created to represent them. Generally, to prevent
inordinate amounts of module reference bumping, codecs and formats can be
added at run-time but cannot be removed.
* All compatibility with the bit field representation of codecs/formats has
been moved to a compatibility API. The primary user of this representation
is chan_iax2, which must continue to maintain its bit-field usage of formats
for interoperability concerns.
* When a format is negotiated with attributes, or when a format cannot be
represented by one of the cached formats, a new format object is created or
cloned from an existing format. That format may have the same codec
underlying it, but is a different format than a version of the format with
different attributes or without attributes.
* While formats are reference counted objects, the reference count maintained
on the format should be manipulated with care. Formats are generally cached
and will persist for the lifetime of Asterisk and do not explicitly need
to have their lifetime modified. An exception to this is when the user of a
format does not know where the format came from *and* the user may outlive
the provider of the format. This occurs, for example, when a format is read
from a channel: the channel may have a format with attributes (hence,
non-cached) and the user of the format may last longer than the channel (if
the reference to the channel is released prior to the format's reference).
For more information on this work, see the API design notes:
https://wiki.asterisk.org/wiki/display/AST/Media+Format+Rewrite
Finally, this work was the culmination of a large number of developer's
efforts. Extra thanks goes to Corey Farrell, who took on a large amount of the
work in the Asterisk core, chan_sip, and was an invaluable resource in peer
reviews throughout this project.
There were a substantial number of patches contributed during this work; the
following issues/patch names simply reflect some of the work (and will cause
the release scripts to give attribution to the individuals who work on them).
Reviews:
https://reviewboard.asterisk.org/r/3814
https://reviewboard.asterisk.org/r/3808
https://reviewboard.asterisk.org/r/3805
https://reviewboard.asterisk.org/r/3803
https://reviewboard.asterisk.org/r/3801
https://reviewboard.asterisk.org/r/3798
https://reviewboard.asterisk.org/r/3800
https://reviewboard.asterisk.org/r/3794
https://reviewboard.asterisk.org/r/3793
https://reviewboard.asterisk.org/r/3792
https://reviewboard.asterisk.org/r/3791
https://reviewboard.asterisk.org/r/3790
https://reviewboard.asterisk.org/r/3789
https://reviewboard.asterisk.org/r/3788
https://reviewboard.asterisk.org/r/3787
https://reviewboard.asterisk.org/r/3786
https://reviewboard.asterisk.org/r/3784
https://reviewboard.asterisk.org/r/3783
https://reviewboard.asterisk.org/r/3778
https://reviewboard.asterisk.org/r/3774
https://reviewboard.asterisk.org/r/3775
https://reviewboard.asterisk.org/r/3772
https://reviewboard.asterisk.org/r/3761
https://reviewboard.asterisk.org/r/3754
https://reviewboard.asterisk.org/r/3753
https://reviewboard.asterisk.org/r/3751
https://reviewboard.asterisk.org/r/3750
https://reviewboard.asterisk.org/r/3748
https://reviewboard.asterisk.org/r/3747
https://reviewboard.asterisk.org/r/3746
https://reviewboard.asterisk.org/r/3742
https://reviewboard.asterisk.org/r/3740
https://reviewboard.asterisk.org/r/3739
https://reviewboard.asterisk.org/r/3738
https://reviewboard.asterisk.org/r/3737
https://reviewboard.asterisk.org/r/3736
https://reviewboard.asterisk.org/r/3734
https://reviewboard.asterisk.org/r/3722
https://reviewboard.asterisk.org/r/3713
https://reviewboard.asterisk.org/r/3703
https://reviewboard.asterisk.org/r/3689
https://reviewboard.asterisk.org/r/3687
https://reviewboard.asterisk.org/r/3674
https://reviewboard.asterisk.org/r/3671
https://reviewboard.asterisk.org/r/3667
https://reviewboard.asterisk.org/r/3665
https://reviewboard.asterisk.org/r/3625
https://reviewboard.asterisk.org/r/3602
https://reviewboard.asterisk.org/r/3519
https://reviewboard.asterisk.org/r/3518
https://reviewboard.asterisk.org/r/3516
https://reviewboard.asterisk.org/r/3515
https://reviewboard.asterisk.org/r/3512
https://reviewboard.asterisk.org/r/3506
https://reviewboard.asterisk.org/r/3413
https://reviewboard.asterisk.org/r/3410
https://reviewboard.asterisk.org/r/3387
https://reviewboard.asterisk.org/r/3388
https://reviewboard.asterisk.org/r/3389
https://reviewboard.asterisk.org/r/3390
https://reviewboard.asterisk.org/r/3321
https://reviewboard.asterisk.org/r/3320
https://reviewboard.asterisk.org/r/3319
https://reviewboard.asterisk.org/r/3318
https://reviewboard.asterisk.org/r/3266
https://reviewboard.asterisk.org/r/3265
https://reviewboard.asterisk.org/r/3234
https://reviewboard.asterisk.org/r/3178
ASTERISK-23114 #close
Reported by: mjordan
media_formats_translation_core.diff uploaded by kharwell (License 6464)
rb3506.diff uploaded by mjordan (License 6283)
media_format_app_file.diff uploaded by kharwell (License 6464)
misc-2.diff uploaded by file (License 5000)
chan_mild-3.diff uploaded by file (License 5000)
chan_obscure.diff uploaded by file (License 5000)
jingle.diff uploaded by file (License 5000)
funcs.diff uploaded by file (License 5000)
formats.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
bridges.diff uploaded by file (License 5000)
mf-codecs-2.diff uploaded by file (License 5000)
mf-app_fax.diff uploaded by file (License 5000)
mf-apps-3.diff uploaded by file (License 5000)
media-formats-3.diff uploaded by file (License 5000)
ASTERISK-23715
rb3713.patch uploaded by coreyfarrell (License 5909)
rb3689.patch uploaded by mjordan (License 6283)
ASTERISK-23957
rb3722.patch uploaded by mjordan (License 6283)
mf-attributes-3.diff uploaded by file (License 5000)
ASTERISK-23958
Tested by: jrose
rb3822.patch uploaded by coreyfarrell (License 5909)
rb3800.patch uploaded by jrose (License 6182)
chan_sip.diff uploaded by mjordan (License 6283)
rb3747.patch uploaded by jrose (License 6182)
ASTERISK-23959 #close
Tested by: sgriepentrog, mjordan, coreyfarrell
sip_cleanup.diff uploaded by opticron (License 6273)
chan_sip_caps.diff uploaded by mjordan (License 6283)
rb3751.patch uploaded by coreyfarrell (License 5909)
chan_sip-3.diff uploaded by file (License 5000)
ASTERISK-23960 #close
Tested by: opticron
direct_media.diff uploaded by opticron (License 6273)
pjsip-direct-media.diff uploaded by file (License 5000)
format_cap_remove.diff uploaded by opticron (License 6273)
media_format_fixes.diff uploaded by opticron (License 6273)
chan_pjsip-2.diff uploaded by file (License 5000)
ASTERISK-23966 #close
Tested by: rmudgett
rb3803.patch uploaded by rmudgetti (License 5621)
chan_dahdi.diff uploaded by file (License 5000)
ASTERISK-24064 #close
Tested by: coreyfarrell, mjordan, opticron, file, rmudgett, sgriepentrog, jrose
rb3814.patch uploaded by rmudgett (License 5621)
moh_cleanup.diff uploaded by opticron (License 6273)
bridge_leak.diff uploaded by opticron (License 6273)
translate.diff uploaded by file (License 5000)
rb3795.patch uploaded by rmudgett (License 5621)
tls_fix.diff uploaded by mjordan (License 6283)
fax-mf-fix-2.diff uploaded by file (License 5000)
rtp_transfer_stuff uploaded by mjordan (License 6283)
rb3787.patch uploaded by rmudgett (License 5621)
media-formats-explicit-translate-format-3.diff uploaded by file (License 5000)
format_cache_case_fix.diff uploaded by opticron (License 6273)
rb3774.patch uploaded by rmudgett (License 5621)
rb3775.patch uploaded by rmudgett (License 5621)
rtp_engine_fix.diff uploaded by opticron (License 6273)
rtp_crash_fix.diff uploaded by opticron (License 6273)
rb3753.patch uploaded by mjordan (License 6283)
rb3750.patch uploaded by mjordan (License 6283)
rb3748.patch uploaded by rmudgett (License 5621)
media_format_fixes.diff uploaded by opticron (License 6273)
rb3740.patch uploaded by mjordan (License 6283)
rb3739.patch uploaded by mjordan (License 6283)
rb3734.patch uploaded by mjordan (License 6283)
rb3689.patch uploaded by mjordan (License 6283)
rb3674.patch uploaded by coreyfarrell (License 5909)
rb3671.patch uploaded by coreyfarrell (License 5909)
rb3667.patch uploaded by coreyfarrell (License 5909)
rb3665.patch uploaded by mjordan (License 6283)
rb3625.patch uploaded by coreyfarrell (License 5909)
rb3602.patch uploaded by coreyfarrell (License 5909)
format_compatibility-2.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419044 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-20 22:06:33 +00:00
|
|
|
ast_format_get_name(bridge_channel->write_format));
|
|
|
|
if (ast_set_write_format(bridge_channel->chan, bridge_channel->write_format)) {
|
2013-07-25 02:20:23 +00:00
|
|
|
ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n",
|
|
|
|
bridge_channel, ast_channel_name(bridge_channel->chan),
|
media formats: re-architect handling of media for performance improvements
In the old times media formats were represented using a bit field. This was
fast but had a few limitations.
1. Asterisk was limited in how many formats it could handle.
2. Formats, being a bit field, could not include any attribute information.
A format was strictly its type, e.g., "this is ulaw".
This was changed in Asterisk 10 (see
https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal for
notes on that work) which led to the creation of the ast_format structure.
This structure allowed Asterisk to handle attributes and bundle information
with a format.
Additionally, ast_format_cap was created to act as a container for multiple
formats that, together, formed the capability of some entity. Another
mechanism was added to allow logic to be registered which performed format
attribute negotiation. Everywhere throughout the codebase Asterisk was
changed to use this strategy.
Unfortunately, in software, there is no free lunch. These new capabilities
came at a cost.
Performance analysis and profiling showed that we spend an inordinate
amount of time comparing, copying, and generally manipulating formats and
their related structures. Basic prototyping has shown that a reasonably
large performance improvement could be made in this area. This patch is the
result of that project, which overhauled the media format architecture
and its usage in Asterisk to improve performance.
Generally, the new philosophy for handling formats is as follows:
* The ast_format structure is reference counted. This removed a large amount
of the memory allocations and copying that was done in prior versions.
* In order to prevent race conditions while keeping things performant, the
ast_format structure is immutable by convention and lock-free. Violate this
tenet at your peril!
* Because formats are reference counted, codecs are also reference counted.
The Asterisk core generally provides built-in codecs and caches the
ast_format structures created to represent them. Generally, to prevent
inordinate amounts of module reference bumping, codecs and formats can be
added at run-time but cannot be removed.
* All compatibility with the bit field representation of codecs/formats has
been moved to a compatibility API. The primary user of this representation
is chan_iax2, which must continue to maintain its bit-field usage of formats
for interoperability concerns.
* When a format is negotiated with attributes, or when a format cannot be
represented by one of the cached formats, a new format object is created or
cloned from an existing format. That format may have the same codec
underlying it, but is a different format than a version of the format with
different attributes or without attributes.
* While formats are reference counted objects, the reference count maintained
on the format should be manipulated with care. Formats are generally cached
and will persist for the lifetime of Asterisk and do not explicitly need
to have their lifetime modified. An exception to this is when the user of a
format does not know where the format came from *and* the user may outlive
the provider of the format. This occurs, for example, when a format is read
from a channel: the channel may have a format with attributes (hence,
non-cached) and the user of the format may last longer than the channel (if
the reference to the channel is released prior to the format's reference).
For more information on this work, see the API design notes:
https://wiki.asterisk.org/wiki/display/AST/Media+Format+Rewrite
Finally, this work was the culmination of a large number of developer's
efforts. Extra thanks goes to Corey Farrell, who took on a large amount of the
work in the Asterisk core, chan_sip, and was an invaluable resource in peer
reviews throughout this project.
There were a substantial number of patches contributed during this work; the
following issues/patch names simply reflect some of the work (and will cause
the release scripts to give attribution to the individuals who work on them).
Reviews:
https://reviewboard.asterisk.org/r/3814
https://reviewboard.asterisk.org/r/3808
https://reviewboard.asterisk.org/r/3805
https://reviewboard.asterisk.org/r/3803
https://reviewboard.asterisk.org/r/3801
https://reviewboard.asterisk.org/r/3798
https://reviewboard.asterisk.org/r/3800
https://reviewboard.asterisk.org/r/3794
https://reviewboard.asterisk.org/r/3793
https://reviewboard.asterisk.org/r/3792
https://reviewboard.asterisk.org/r/3791
https://reviewboard.asterisk.org/r/3790
https://reviewboard.asterisk.org/r/3789
https://reviewboard.asterisk.org/r/3788
https://reviewboard.asterisk.org/r/3787
https://reviewboard.asterisk.org/r/3786
https://reviewboard.asterisk.org/r/3784
https://reviewboard.asterisk.org/r/3783
https://reviewboard.asterisk.org/r/3778
https://reviewboard.asterisk.org/r/3774
https://reviewboard.asterisk.org/r/3775
https://reviewboard.asterisk.org/r/3772
https://reviewboard.asterisk.org/r/3761
https://reviewboard.asterisk.org/r/3754
https://reviewboard.asterisk.org/r/3753
https://reviewboard.asterisk.org/r/3751
https://reviewboard.asterisk.org/r/3750
https://reviewboard.asterisk.org/r/3748
https://reviewboard.asterisk.org/r/3747
https://reviewboard.asterisk.org/r/3746
https://reviewboard.asterisk.org/r/3742
https://reviewboard.asterisk.org/r/3740
https://reviewboard.asterisk.org/r/3739
https://reviewboard.asterisk.org/r/3738
https://reviewboard.asterisk.org/r/3737
https://reviewboard.asterisk.org/r/3736
https://reviewboard.asterisk.org/r/3734
https://reviewboard.asterisk.org/r/3722
https://reviewboard.asterisk.org/r/3713
https://reviewboard.asterisk.org/r/3703
https://reviewboard.asterisk.org/r/3689
https://reviewboard.asterisk.org/r/3687
https://reviewboard.asterisk.org/r/3674
https://reviewboard.asterisk.org/r/3671
https://reviewboard.asterisk.org/r/3667
https://reviewboard.asterisk.org/r/3665
https://reviewboard.asterisk.org/r/3625
https://reviewboard.asterisk.org/r/3602
https://reviewboard.asterisk.org/r/3519
https://reviewboard.asterisk.org/r/3518
https://reviewboard.asterisk.org/r/3516
https://reviewboard.asterisk.org/r/3515
https://reviewboard.asterisk.org/r/3512
https://reviewboard.asterisk.org/r/3506
https://reviewboard.asterisk.org/r/3413
https://reviewboard.asterisk.org/r/3410
https://reviewboard.asterisk.org/r/3387
https://reviewboard.asterisk.org/r/3388
https://reviewboard.asterisk.org/r/3389
https://reviewboard.asterisk.org/r/3390
https://reviewboard.asterisk.org/r/3321
https://reviewboard.asterisk.org/r/3320
https://reviewboard.asterisk.org/r/3319
https://reviewboard.asterisk.org/r/3318
https://reviewboard.asterisk.org/r/3266
https://reviewboard.asterisk.org/r/3265
https://reviewboard.asterisk.org/r/3234
https://reviewboard.asterisk.org/r/3178
ASTERISK-23114 #close
Reported by: mjordan
media_formats_translation_core.diff uploaded by kharwell (License 6464)
rb3506.diff uploaded by mjordan (License 6283)
media_format_app_file.diff uploaded by kharwell (License 6464)
misc-2.diff uploaded by file (License 5000)
chan_mild-3.diff uploaded by file (License 5000)
chan_obscure.diff uploaded by file (License 5000)
jingle.diff uploaded by file (License 5000)
funcs.diff uploaded by file (License 5000)
formats.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
bridges.diff uploaded by file (License 5000)
mf-codecs-2.diff uploaded by file (License 5000)
mf-app_fax.diff uploaded by file (License 5000)
mf-apps-3.diff uploaded by file (License 5000)
media-formats-3.diff uploaded by file (License 5000)
ASTERISK-23715
rb3713.patch uploaded by coreyfarrell (License 5909)
rb3689.patch uploaded by mjordan (License 6283)
ASTERISK-23957
rb3722.patch uploaded by mjordan (License 6283)
mf-attributes-3.diff uploaded by file (License 5000)
ASTERISK-23958
Tested by: jrose
rb3822.patch uploaded by coreyfarrell (License 5909)
rb3800.patch uploaded by jrose (License 6182)
chan_sip.diff uploaded by mjordan (License 6283)
rb3747.patch uploaded by jrose (License 6182)
ASTERISK-23959 #close
Tested by: sgriepentrog, mjordan, coreyfarrell
sip_cleanup.diff uploaded by opticron (License 6273)
chan_sip_caps.diff uploaded by mjordan (License 6283)
rb3751.patch uploaded by coreyfarrell (License 5909)
chan_sip-3.diff uploaded by file (License 5000)
ASTERISK-23960 #close
Tested by: opticron
direct_media.diff uploaded by opticron (License 6273)
pjsip-direct-media.diff uploaded by file (License 5000)
format_cap_remove.diff uploaded by opticron (License 6273)
media_format_fixes.diff uploaded by opticron (License 6273)
chan_pjsip-2.diff uploaded by file (License 5000)
ASTERISK-23966 #close
Tested by: rmudgett
rb3803.patch uploaded by rmudgetti (License 5621)
chan_dahdi.diff uploaded by file (License 5000)
ASTERISK-24064 #close
Tested by: coreyfarrell, mjordan, opticron, file, rmudgett, sgriepentrog, jrose
rb3814.patch uploaded by rmudgett (License 5621)
moh_cleanup.diff uploaded by opticron (License 6273)
bridge_leak.diff uploaded by opticron (License 6273)
translate.diff uploaded by file (License 5000)
rb3795.patch uploaded by rmudgett (License 5621)
tls_fix.diff uploaded by mjordan (License 6283)
fax-mf-fix-2.diff uploaded by file (License 5000)
rtp_transfer_stuff uploaded by mjordan (License 6283)
rb3787.patch uploaded by rmudgett (License 5621)
media-formats-explicit-translate-format-3.diff uploaded by file (License 5000)
format_cache_case_fix.diff uploaded by opticron (License 6273)
rb3774.patch uploaded by rmudgett (License 5621)
rb3775.patch uploaded by rmudgett (License 5621)
rtp_engine_fix.diff uploaded by opticron (License 6273)
rtp_crash_fix.diff uploaded by opticron (License 6273)
rb3753.patch uploaded by mjordan (License 6283)
rb3750.patch uploaded by mjordan (License 6283)
rb3748.patch uploaded by rmudgett (License 5621)
media_format_fixes.diff uploaded by opticron (License 6273)
rb3740.patch uploaded by mjordan (License 6283)
rb3739.patch uploaded by mjordan (License 6283)
rb3734.patch uploaded by mjordan (License 6283)
rb3689.patch uploaded by mjordan (License 6283)
rb3674.patch uploaded by coreyfarrell (License 5909)
rb3671.patch uploaded by coreyfarrell (License 5909)
rb3667.patch uploaded by coreyfarrell (License 5909)
rb3665.patch uploaded by mjordan (License 6283)
rb3625.patch uploaded by coreyfarrell (License 5909)
rb3602.patch uploaded by coreyfarrell (License 5909)
format_compatibility-2.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419044 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-20 22:06:33 +00:00
|
|
|
ast_format_get_name(bridge_channel->write_format));
|
2013-07-25 02:20:23 +00:00
|
|
|
}
|
|
|
|
}
|
2014-08-20 22:52:44 +00:00
|
|
|
|
|
|
|
ast_channel_unlock(bridge_channel->chan);
|
2013-07-25 02:20:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
|
|
|
|
{
|
2014-07-09 16:34:51 +00:00
|
|
|
struct ast_bridge_channel *other;
|
2013-07-25 02:20:23 +00:00
|
|
|
struct ast_bridge *bridge = bridge_channel->bridge;
|
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
|
|
|
struct ast_channel *oldest_linkedid_chan = bridge_channel->chan;
|
2013-07-25 02:20:23 +00:00
|
|
|
|
|
|
|
AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
|
|
|
|
if (other == swap) {
|
|
|
|
continue;
|
|
|
|
}
|
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
|
|
|
oldest_linkedid_chan = ast_channel_internal_oldest_linkedid(
|
|
|
|
oldest_linkedid_chan, other->chan);
|
2013-07-25 02:20:23 +00:00
|
|
|
}
|
|
|
|
|
2013-12-18 20:33:37 +00:00
|
|
|
ast_channel_lock(bridge_channel->chan);
|
2014-07-09 16:34:51 +00:00
|
|
|
ast_channel_internal_copy_linkedid(bridge_channel->chan, oldest_linkedid_chan);
|
2013-12-18 20:33:37 +00:00
|
|
|
ast_channel_unlock(bridge_channel->chan);
|
2013-07-25 02:20:23 +00:00
|
|
|
AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
|
|
|
|
if (other == swap) {
|
|
|
|
continue;
|
|
|
|
}
|
2013-12-18 20:33:37 +00:00
|
|
|
ast_channel_lock(other->chan);
|
2014-07-09 16:34:51 +00:00
|
|
|
ast_channel_internal_copy_linkedid(other->chan, oldest_linkedid_chan);
|
2013-12-18 20:33:37 +00:00
|
|
|
ast_channel_unlock(other->chan);
|
2013-07-25 02:20:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-09 16:34:51 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Set dest's empty peeraccount with the src's non-empty accountcode.
|
|
|
|
* \since 12.5.0
|
|
|
|
*
|
|
|
|
* \param dest Channel to update peeraccount.
|
|
|
|
* \param src Channel to get accountcode from.
|
|
|
|
*
|
|
|
|
* \note Both channels are already locked.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void channel_fill_empty_peeraccount(struct ast_channel *dest, struct ast_channel *src)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
2014-07-09 16:34:51 +00:00
|
|
|
if (ast_strlen_zero(ast_channel_peeraccount(dest))
|
|
|
|
&& !ast_strlen_zero(ast_channel_accountcode(src))) {
|
|
|
|
ast_debug(1, "Setting channel %s peeraccount with channel %s accountcode '%s'.\n",
|
|
|
|
ast_channel_name(dest),
|
|
|
|
ast_channel_name(src), ast_channel_accountcode(src));
|
|
|
|
ast_channel_peeraccount_set(dest, ast_channel_accountcode(src));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Set dest's empty accountcode with the src's non-empty peeraccount.
|
|
|
|
* \since 12.5.0
|
|
|
|
*
|
|
|
|
* \param dest Channel to update accountcode.
|
|
|
|
* \param src Channel to get peeraccount from.
|
|
|
|
*
|
|
|
|
* \note Both channels are already locked.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void channel_fill_empty_accountcode(struct ast_channel *dest, struct ast_channel *src)
|
|
|
|
{
|
|
|
|
if (ast_strlen_zero(ast_channel_accountcode(dest))
|
|
|
|
&& !ast_strlen_zero(ast_channel_peeraccount(src))) {
|
|
|
|
ast_debug(1, "Setting channel %s accountcode with channel %s peeraccount '%s'.\n",
|
|
|
|
ast_channel_name(dest),
|
|
|
|
ast_channel_name(src), ast_channel_peeraccount(src));
|
|
|
|
ast_channel_accountcode_set(dest, ast_channel_peeraccount(src));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Set empty peeraccount and accountcode in a channel from the other channel.
|
|
|
|
* \since 12.5.0
|
|
|
|
*
|
|
|
|
* \param c0 First bridge channel to update.
|
|
|
|
* \param c1 Second bridge channel to update.
|
|
|
|
*
|
|
|
|
* \note Both channels are already locked.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void channel_set_empty_accountcodes(struct ast_channel *c0, struct ast_channel *c1)
|
|
|
|
{
|
|
|
|
/* Set empty peeraccount from the other channel's accountcode. */
|
|
|
|
channel_fill_empty_peeraccount(c0, c1);
|
|
|
|
channel_fill_empty_peeraccount(c1, c0);
|
|
|
|
|
|
|
|
/* Set empty accountcode from the other channel's peeraccount. */
|
|
|
|
channel_fill_empty_accountcode(c0, c1);
|
|
|
|
channel_fill_empty_accountcode(c1, c0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Update dest's peeraccount with the src's different accountcode.
|
|
|
|
* \since 12.5.0
|
|
|
|
*
|
|
|
|
* \param dest Channel to update peeraccount.
|
|
|
|
* \param src Channel to get accountcode from.
|
|
|
|
*
|
|
|
|
* \note Both channels are already locked.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void channel_update_peeraccount(struct ast_channel *dest, struct ast_channel *src)
|
|
|
|
{
|
|
|
|
if (strcmp(ast_channel_accountcode(src), ast_channel_peeraccount(dest))) {
|
|
|
|
ast_debug(1, "Changing channel %s peeraccount '%s' to match channel %s accountcode '%s'.\n",
|
|
|
|
ast_channel_name(dest), ast_channel_peeraccount(dest),
|
|
|
|
ast_channel_name(src), ast_channel_accountcode(src));
|
|
|
|
ast_channel_peeraccount_set(dest, ast_channel_accountcode(src));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Update peeraccounts to match the other channel's accountcode.
|
|
|
|
* \since 12.5.0
|
|
|
|
*
|
|
|
|
* \param c0 First channel to update.
|
|
|
|
* \param c1 Second channel to update.
|
|
|
|
*
|
|
|
|
* \note Both channels are already locked.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void channel_update_peeraccounts(struct ast_channel *c0, struct ast_channel *c1)
|
|
|
|
{
|
|
|
|
channel_update_peeraccount(c0, c1);
|
|
|
|
channel_update_peeraccount(c1, c0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Update channel accountcodes because a channel is joining a bridge.
|
|
|
|
* \since 12.5.0
|
|
|
|
*
|
|
|
|
* \param joining Channel joining the bridge.
|
|
|
|
* \param swap Channel being replaced by the joining channel. May be NULL.
|
|
|
|
*
|
|
|
|
* \note The bridge must be locked prior to calling this function.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void bridge_channel_update_accountcodes_joining(struct ast_bridge_channel *joining, struct ast_bridge_channel *swap)
|
|
|
|
{
|
|
|
|
struct ast_bridge *bridge = joining->bridge;
|
|
|
|
struct ast_bridge_channel *other;
|
|
|
|
unsigned int swap_in_bridge = 0;
|
|
|
|
unsigned int will_be_two_party;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only update the peeraccount to match if the joining channel
|
|
|
|
* will make it a two party bridge.
|
|
|
|
*/
|
|
|
|
if (bridge->num_channels <= 2 && swap) {
|
|
|
|
AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
|
|
|
|
if (other == swap) {
|
|
|
|
swap_in_bridge = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
will_be_two_party = (1 == bridge->num_channels - swap_in_bridge);
|
2013-07-24 15:38:18 +00:00
|
|
|
|
|
|
|
AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
|
|
|
|
if (other == swap) {
|
|
|
|
continue;
|
|
|
|
}
|
2014-07-09 16:34:51 +00:00
|
|
|
ast_assert(joining != other);
|
|
|
|
ast_channel_lock_both(joining->chan, other->chan);
|
|
|
|
channel_set_empty_accountcodes(joining->chan, other->chan);
|
|
|
|
if (will_be_two_party) {
|
|
|
|
channel_update_peeraccounts(joining->chan, other->chan);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
2014-07-09 16:34:51 +00:00
|
|
|
ast_channel_unlock(joining->chan);
|
2013-12-18 20:33:37 +00:00
|
|
|
ast_channel_unlock(other->chan);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-09 16:34:51 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Update channel peeraccount codes because a channel has left a bridge.
|
|
|
|
* \since 12.5.0
|
|
|
|
*
|
|
|
|
* \param leaving Channel leaving the bridge. (Has already been removed actually)
|
|
|
|
*
|
|
|
|
* \note The bridge must be locked prior to calling this function.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void bridge_channel_update_accountcodes_leaving(struct ast_bridge_channel *leaving)
|
|
|
|
{
|
|
|
|
struct ast_bridge *bridge = leaving->bridge;
|
|
|
|
struct ast_bridge_channel *first;
|
|
|
|
struct ast_bridge_channel *second;
|
|
|
|
|
|
|
|
if (bridge->num_channels != 2 || bridge->dissolved) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
first = AST_LIST_FIRST(&bridge->channels);
|
|
|
|
second = AST_LIST_LAST(&bridge->channels);
|
|
|
|
ast_assert(first && first != second);
|
|
|
|
ast_channel_lock_both(first->chan, second->chan);
|
|
|
|
channel_set_empty_accountcodes(first->chan, second->chan);
|
|
|
|
channel_update_peeraccounts(first->chan, second->chan);
|
|
|
|
ast_channel_unlock(second->chan);
|
|
|
|
ast_channel_unlock(first->chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *joining, struct ast_bridge_channel *leaving)
|
|
|
|
{
|
|
|
|
if (joining) {
|
|
|
|
bridge_channel_update_accountcodes_joining(joining, leaving);
|
|
|
|
} else {
|
|
|
|
bridge_channel_update_accountcodes_leaving(leaving);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-22 21:09:52 +00:00
|
|
|
void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int cause)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
2013-07-25 02:20:23 +00:00
|
|
|
struct ast_bridge_features *features = bridge_channel->features;
|
|
|
|
struct ast_bridge_hook *hook;
|
|
|
|
struct ao2_iterator iter;
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-08-22 21:09:52 +00:00
|
|
|
ast_bridge_channel_lock(bridge_channel);
|
|
|
|
if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
|
|
|
|
channel_set_cause(bridge_channel->chan, cause);
|
|
|
|
cause = 0;
|
|
|
|
}
|
|
|
|
ast_bridge_channel_unlock(bridge_channel);
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/* Run any hangup hooks. */
|
|
|
|
iter = ao2_iterator_init(features->other_hooks, 0);
|
|
|
|
for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
|
|
|
|
int remove_me;
|
|
|
|
|
|
|
|
if (hook->type != AST_BRIDGE_HOOK_TYPE_HANGUP) {
|
2013-07-24 15:38:18 +00:00
|
|
|
continue;
|
|
|
|
}
|
2013-07-26 21:34:23 +00:00
|
|
|
remove_me = hook->callback(bridge_channel, hook->hook_pvt);
|
2013-07-25 02:20:23 +00:00
|
|
|
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->other_hooks, hook);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
2013-07-25 02:20:23 +00:00
|
|
|
ao2_iterator_destroy(&iter);
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/* Default hangup action. */
|
2013-08-22 21:09:52 +00:00
|
|
|
ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, cause);
|
2013-07-25 02:20:23 +00:00
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Write an \ref ast_frame onto the bridge channel
|
|
|
|
* \since 12.0.0
|
|
|
|
*
|
|
|
|
* \param bridge_channel Which channel to queue the frame onto.
|
|
|
|
* \param frame The frame to write onto the bridge_channel
|
|
|
|
*
|
|
|
|
* \retval 0 on success.
|
|
|
|
* \retval -1 on error.
|
|
|
|
*/
|
|
|
|
static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
|
|
|
|
{
|
2016-02-22 19:54:47 +00:00
|
|
|
const struct ast_control_t38_parameters *t38_parameters;
|
2017-06-08 19:38:51 +00:00
|
|
|
int deferred;
|
2016-02-22 19:54:47 +00:00
|
|
|
|
2014-03-17 17:22:12 +00:00
|
|
|
ast_assert(frame->frametype != AST_FRAME_BRIDGE_ACTION_SYNC);
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
ast_bridge_channel_lock_bridge(bridge_channel);
|
2017-06-08 19:38:51 +00:00
|
|
|
|
|
|
|
deferred = bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame);
|
|
|
|
if (deferred) {
|
|
|
|
struct ast_frame *dup;
|
|
|
|
|
|
|
|
dup = ast_frdup(frame);
|
|
|
|
if (dup) {
|
|
|
|
AST_LIST_INSERT_HEAD(&bridge_channel->deferred_queue, dup, frame_list);
|
|
|
|
}
|
|
|
|
}
|
2013-08-23 18:33:36 +00:00
|
|
|
|
|
|
|
/* Remember any owed events to the bridge. */
|
|
|
|
switch (frame->frametype) {
|
|
|
|
case AST_FRAME_DTMF_BEGIN:
|
|
|
|
bridge_channel->owed.dtmf_tv = ast_tvnow();
|
|
|
|
bridge_channel->owed.dtmf_digit = frame->subclass.integer;
|
|
|
|
break;
|
|
|
|
case AST_FRAME_DTMF_END:
|
|
|
|
bridge_channel->owed.dtmf_digit = '\0';
|
|
|
|
break;
|
|
|
|
case AST_FRAME_CONTROL:
|
|
|
|
/*
|
|
|
|
* We explicitly will not remember HOLD/UNHOLD frames because
|
|
|
|
* things like attended transfers will handle them.
|
|
|
|
*/
|
2016-02-22 19:54:47 +00:00
|
|
|
switch (frame->subclass.integer) {
|
|
|
|
case AST_CONTROL_T38_PARAMETERS:
|
|
|
|
t38_parameters = frame->data.ptr;
|
|
|
|
switch (t38_parameters->request_response) {
|
|
|
|
case AST_T38_REQUEST_NEGOTIATE:
|
|
|
|
case AST_T38_NEGOTIATED:
|
|
|
|
bridge_channel->owed.t38_terminate = 1;
|
|
|
|
break;
|
|
|
|
case AST_T38_REQUEST_TERMINATE:
|
|
|
|
case AST_T38_TERMINATED:
|
|
|
|
case AST_T38_REFUSED:
|
|
|
|
bridge_channel->owed.t38_terminate = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2013-08-23 18:33:36 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-07-25 02:20:23 +00:00
|
|
|
ast_bridge_unlock(bridge_channel->bridge);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Claim successful write to bridge. If deferred frame
|
|
|
|
* support is added, claim successfully deferred.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-20 01:06:14 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Cancel owed events by the channel to the bridge.
|
|
|
|
* \since 13.8.0
|
|
|
|
*
|
|
|
|
* \param bridge_channel Channel that owes events to the bridge.
|
|
|
|
*
|
|
|
|
* \note On entry, the bridge_channel->bridge is already locked.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void bridge_channel_cancel_owed_events(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
bridge_channel->owed.dtmf_digit = '\0';
|
2016-02-22 19:54:47 +00:00
|
|
|
bridge_channel->owed.t38_terminate = 0;
|
2016-02-20 01:06:14 +00:00
|
|
|
}
|
|
|
|
|
2013-08-23 18:33:36 +00:00
|
|
|
void bridge_channel_settle_owed_events(struct ast_bridge *orig_bridge, struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
if (bridge_channel->owed.dtmf_digit) {
|
|
|
|
struct ast_frame frame = {
|
|
|
|
.frametype = AST_FRAME_DTMF_END,
|
|
|
|
.subclass.integer = bridge_channel->owed.dtmf_digit,
|
|
|
|
.src = "Bridge channel owed DTMF",
|
|
|
|
};
|
|
|
|
|
|
|
|
frame.len = ast_tvdiff_ms(ast_tvnow(), bridge_channel->owed.dtmf_tv);
|
|
|
|
if (frame.len < option_dtmfminduration) {
|
|
|
|
frame.len = option_dtmfminduration;
|
|
|
|
}
|
|
|
|
ast_log(LOG_DTMF, "DTMF end '%c' simulated to bridge %s because %s left. Duration %ld ms.\n",
|
|
|
|
bridge_channel->owed.dtmf_digit, orig_bridge->uniqueid,
|
|
|
|
ast_channel_name(bridge_channel->chan), frame.len);
|
|
|
|
bridge_channel->owed.dtmf_digit = '\0';
|
|
|
|
orig_bridge->technology->write(orig_bridge, NULL, &frame);
|
|
|
|
}
|
2016-02-22 19:54:47 +00:00
|
|
|
if (bridge_channel->owed.t38_terminate) {
|
|
|
|
struct ast_control_t38_parameters t38_parameters = {
|
|
|
|
.request_response = AST_T38_TERMINATED,
|
|
|
|
};
|
|
|
|
struct ast_frame frame = {
|
|
|
|
.frametype = AST_FRAME_CONTROL,
|
|
|
|
.subclass.integer = AST_CONTROL_T38_PARAMETERS,
|
|
|
|
.data.ptr = &t38_parameters,
|
|
|
|
.datalen = sizeof(t38_parameters),
|
|
|
|
.src = "Bridge channel owed T.38 terminate",
|
|
|
|
};
|
|
|
|
|
|
|
|
ast_debug(1, "T.38 terminate simulated to bridge %s because %s left.\n",
|
|
|
|
orig_bridge->uniqueid, ast_channel_name(bridge_channel->chan));
|
|
|
|
bridge_channel->owed.t38_terminate = 0;
|
|
|
|
orig_bridge->technology->write(orig_bridge, NULL, &frame);
|
|
|
|
}
|
2013-08-23 18:33:36 +00:00
|
|
|
}
|
|
|
|
|
2017-06-08 19:38:51 +00:00
|
|
|
void bridge_channel_queue_deferred_frames(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
struct ast_frame *frame;
|
|
|
|
|
|
|
|
ast_channel_lock(bridge_channel->chan);
|
|
|
|
while ((frame = AST_LIST_REMOVE_HEAD(&bridge_channel->deferred_queue, frame_list))) {
|
|
|
|
ast_queue_frame_head(bridge_channel->chan, frame);
|
|
|
|
ast_frfree(frame);
|
|
|
|
}
|
|
|
|
ast_channel_unlock(bridge_channel->chan);
|
|
|
|
}
|
|
|
|
|
2013-08-21 15:51:19 +00:00
|
|
|
/*!
|
|
|
|
* \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_internal_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_internal_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_internal_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_internal_unsuspend_nolock(bridge_channel);
|
|
|
|
ast_bridge_unlock(bridge_channel->bridge);
|
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \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.
|
|
|
|
*/
|
2014-03-17 17:22:12 +00:00
|
|
|
static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel,
|
|
|
|
enum bridge_channel_action_type action, const void *data, size_t datalen)
|
2013-07-25 02:20:23 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-03-17 17:22:12 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Queue an action frame onto the bridge channel with data synchronously.
|
|
|
|
* \since 12.2.0
|
|
|
|
*
|
|
|
|
* The function will not return until the queued frame is freed.
|
|
|
|
*
|
|
|
|
* \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.
|
|
|
|
*/
|
|
|
|
static int bridge_channel_queue_action_data_sync(struct ast_bridge_channel *bridge_channel,
|
|
|
|
enum bridge_channel_action_type action, const void *data, size_t datalen)
|
|
|
|
{
|
|
|
|
struct sync_payload *sync_payload;
|
|
|
|
int sync_payload_len = sizeof(*sync_payload) + datalen;
|
|
|
|
struct bridge_sync sync_struct;
|
|
|
|
struct ast_frame frame = {
|
|
|
|
.frametype = AST_FRAME_BRIDGE_ACTION_SYNC,
|
|
|
|
.subclass.integer = action,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Make sure we don't end up trying to wait on ourself to deliver the frame */
|
|
|
|
ast_assert(!pthread_equal(pthread_self(), bridge_channel->thread));
|
|
|
|
|
|
|
|
sync_payload = ast_alloca(sync_payload_len);
|
|
|
|
sync_payload->id = ast_atomic_fetchadd_int(&sync_ids, +1);
|
|
|
|
memcpy(sync_payload->data, data, datalen);
|
|
|
|
|
|
|
|
frame.datalen = sync_payload_len;
|
|
|
|
frame.data.ptr = sync_payload;
|
|
|
|
|
|
|
|
bridge_sync_init(&sync_struct, sync_payload->id);
|
|
|
|
if (ast_bridge_channel_queue_frame(bridge_channel, &frame)) {
|
|
|
|
bridge_sync_cleanup(&sync_struct);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bridge_sync_wait(&sync_struct);
|
|
|
|
bridge_sync_cleanup(&sync_struct);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-07-25 02:20:23 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Write 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.
|
|
|
|
*/
|
2014-03-17 17:22:12 +00:00
|
|
|
static int bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel,
|
|
|
|
enum bridge_channel_action_type action, const void *data, size_t datalen)
|
2013-07-25 02:20:23 +00:00
|
|
|
{
|
|
|
|
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);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2014-03-17 17:22:12 +00:00
|
|
|
static void bridge_frame_free(struct ast_frame *frame)
|
|
|
|
{
|
|
|
|
if (frame->frametype == AST_FRAME_BRIDGE_ACTION_SYNC) {
|
|
|
|
struct sync_payload *sync_payload = frame->data.ptr;
|
|
|
|
struct bridge_sync *sync;
|
|
|
|
|
|
|
|
AST_RWLIST_RDLOCK(&sync_structs);
|
|
|
|
AST_RWLIST_TRAVERSE(&sync_structs, sync, list) {
|
|
|
|
if (sync->id == sync_payload->id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sync) {
|
|
|
|
bridge_sync_signal(sync);
|
|
|
|
}
|
|
|
|
AST_RWLIST_UNLOCK(&sync_structs);
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_frfree(frame);
|
|
|
|
}
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr)
|
|
|
|
{
|
|
|
|
struct ast_frame *dup;
|
|
|
|
|
|
|
|
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);
|
2013-07-25 02:20:23 +00:00
|
|
|
if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
|
2013-07-24 15:38:18 +00:00
|
|
|
/* Drop frames on channels leaving the bridge. */
|
|
|
|
ast_bridge_channel_unlock(bridge_channel);
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_frame_free(dup);
|
2013-07-24 15:38:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-25 16:49:16 +00:00
|
|
|
if (ast_channel_is_multistream(bridge_channel->chan) &&
|
|
|
|
(fr->frametype == AST_FRAME_IMAGE || fr->frametype == AST_FRAME_TEXT ||
|
|
|
|
fr->frametype == AST_FRAME_VIDEO || fr->frametype == AST_FRAME_VOICE)) {
|
|
|
|
/* Media frames need to be mapped to an appropriate write stream */
|
|
|
|
dup->stream_num = AST_VECTOR_GET(
|
|
|
|
&bridge_channel->stream_map.to_bridge, fr->stream_num);
|
Add primitive SFU support to bridge_softmix.
This sets up the "plumbing" in bridge_softmix to
be able to accommodate Asterisk asking as an SFU
(selective forwarding unit) for conferences.
The way this works is that whenever a channel enters or leaves a
conference, all participants in the bridge get sent a stream topology
change request. The topologies consist of the channels' original
topology, along with video destination streams corresponding to each
participants' source video streams. So for instance, if Alice, Bob, and
Carol are in the conference, and each supplies one video stream, then
the topologies for each would look like so:
Alice:
Audio,
Source video(Alice),
Destination Video(Bob),
Destination video (Carol)
Bob:
Audio,
Source video(Bob)
Destination Video(Alice),
Destination video (Carol)
Carol:
Audio,
Source video(Carol)
Destination Video(Alice),
Destination video (Bob)
This way, video that arrives from a source video stream can then be
copied out to the destination video streams on the other participants'
channels.
Once the bridge gets told that a topology on a channel has changed, the
bridge constructs a map in order to get the video frames routed to the
proper destination streams. This is done using the bridge channel's
stream_map.
This change is bare-bones with regards to SFU support. Some key features
are missing at this point:
* Stream limits. This commit makes no effort to limit the number of
streams on a specific channel. This means that if there were 50 video
callers in a conference, bridge_softmix will happily send out topology
change requests to every channel in the bridge, requesting 50+
streams.
* Configuration. The plumbing has been added to bridge_softmix, but
there has been nothing added as of yet to app_confbridge to enable SFU
video mode.
* Testing. Some functions included here have unit tests.
However, the functionality as a whole has only been verified by
hand-tracing the code.
* Selectivenss. For a "selective" forwarding unit, this does not
currently have any means of being selective.
* Features. Presumably, someone might wish to only receive video from
specific sources. There are no external-facing functions at the moment
that allow for users to select who they receive video from.
* Efficiency. The current scheme treats all video streams as being
unidirectional. We could be re-using a source video stream as a
desetnation, too. But to simplify things on this first round, I did it
this way.
Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
|
|
|
if (dup->stream_num == -1) {
|
|
|
|
ast_bridge_channel_unlock(bridge_channel);
|
|
|
|
bridge_frame_free(dup);
|
|
|
|
return 0;
|
|
|
|
}
|
2017-04-25 16:49:16 +00:00
|
|
|
} else {
|
|
|
|
dup->stream_num = -1;
|
|
|
|
}
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
AST_LIST_INSERT_TAIL(&bridge_channel->wr_queue, dup, frame_list);
|
2017-04-18 00:06:10 +00:00
|
|
|
if (ast_alertpipe_write(bridge_channel->alert_pipe)) {
|
2013-07-24 15:38:18 +00:00
|
|
|
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));
|
|
|
|
}
|
2013-07-25 02:20:23 +00:00
|
|
|
ast_bridge_channel_unlock(bridge_channel);
|
|
|
|
return 0;
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
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)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
|
|
|
struct ast_frame frame = {
|
2013-07-25 02:20:23 +00:00
|
|
|
.frametype = AST_FRAME_CONTROL,
|
|
|
|
.subclass.integer = control,
|
2013-07-24 15:38:18 +00:00
|
|
|
.datalen = datalen,
|
|
|
|
.data.ptr = (void *) data,
|
|
|
|
};
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
return ast_bridge_channel_queue_frame(bridge_channel, &frame);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2015-04-07 15:22:42 +00:00
|
|
|
struct ast_json *blob;
|
|
|
|
int res;
|
2013-07-24 15:38:18 +00:00
|
|
|
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;
|
2015-04-07 15:22:42 +00:00
|
|
|
blob = NULL;
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
stasis: Reduce creation of channel snapshots to improve performance
During some performance testing of Asterisk with AGI, ARI, and lots of Local
channels, we noticed that there's quite a hit in performance during channel
creation and releasing to the dialplan (ARI continue). After investigating
the performance spike that occurs during channel creation, we discovered
that we create a lot of channel snapshots that are technically unnecessary.
This includes creating snapshots during:
* AGI execution
* Returning objects for ARI commands
* During some Local channel operations
* During some dialling operations
* During variable setting
* During some bridging operations
And more.
This patch does the following:
- It removes a number of fields from channel snapshots. These fields were
rarely used, were expensive to have on the snapshot, and hurt performance.
This included formats, translation paths, Log Call ID, callgroup, pickup
group, and all channel variables. As a result, AMI Status,
"core show channel", "core show channelvar", and "pjsip show channel" were
modified to either hit the live channel or not show certain pieces of data.
While this is unfortunate, the performance gain from this patch is worth
the loss in behaviour.
- It adds a mechanism to publish a cached snapshot + blob. A large number of
publications were changed to use this, including:
- During Dial begin
- During Variable assignment (if no AMI variables are emitted - if AMI
variables are set, we have to make snapshots when a variable is changed)
- During channel pickup
- When a channel is put on hold/unhold
- When a DTMF digit is begun/ended
- When creating a bridge snapshot
- When an AOC event is raised
- During Local channel optimization/Local bridging
- When endpoint snapshots are generated
- All AGI events
- All ARI responses that return a channel
- Events in the AgentPool, MeetMe, and some in Queue
- Additionally, some extraneous channel snapshots were being made that were
unnecessary. These were removed.
- The result of ast_hashtab_hash_string is now cached in stasis_cache. This
reduces a large number of calls to ast_hashtab_hash_string, which reduced
the amount of time spent in this function in gprof by around 50%.
#ASTERISK-23811 #close
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/3568/
........
Merged revisions 416211 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@416216 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-06-13 18:24:49 +00:00
|
|
|
ast_channel_publish_cached_blob(bridge_channel->chan, ast_channel_hold_type(), blob);
|
|
|
|
|
2015-04-07 15:22:42 +00:00
|
|
|
res = ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
|
2013-07-24 15:38:18 +00:00
|
|
|
moh_class, datalen);
|
2015-04-07 15:22:42 +00:00
|
|
|
|
|
|
|
ast_json_unref(blob);
|
|
|
|
return res;
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
stasis: Reduce creation of channel snapshots to improve performance
During some performance testing of Asterisk with AGI, ARI, and lots of Local
channels, we noticed that there's quite a hit in performance during channel
creation and releasing to the dialplan (ARI continue). After investigating
the performance spike that occurs during channel creation, we discovered
that we create a lot of channel snapshots that are technically unnecessary.
This includes creating snapshots during:
* AGI execution
* Returning objects for ARI commands
* During some Local channel operations
* During some dialling operations
* During variable setting
* During some bridging operations
And more.
This patch does the following:
- It removes a number of fields from channel snapshots. These fields were
rarely used, were expensive to have on the snapshot, and hurt performance.
This included formats, translation paths, Log Call ID, callgroup, pickup
group, and all channel variables. As a result, AMI Status,
"core show channel", "core show channelvar", and "pjsip show channel" were
modified to either hit the live channel or not show certain pieces of data.
While this is unfortunate, the performance gain from this patch is worth
the loss in behaviour.
- It adds a mechanism to publish a cached snapshot + blob. A large number of
publications were changed to use this, including:
- During Dial begin
- During Variable assignment (if no AMI variables are emitted - if AMI
variables are set, we have to make snapshots when a variable is changed)
- During channel pickup
- When a channel is put on hold/unhold
- When a DTMF digit is begun/ended
- When creating a bridge snapshot
- When an AOC event is raised
- During Local channel optimization/Local bridging
- When endpoint snapshots are generated
- All AGI events
- All ARI responses that return a channel
- Events in the AgentPool, MeetMe, and some in Queue
- Additionally, some extraneous channel snapshots were being made that were
unnecessary. These were removed.
- The result of ast_hashtab_hash_string is now cached in stasis_cache. This
reduces a large number of calls to ast_hashtab_hash_string, which reduced
the amount of time spent in this function in gprof by around 50%.
#ASTERISK-23811 #close
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/3568/
........
Merged revisions 416211 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@416216 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-06-13 18:24:49 +00:00
|
|
|
ast_channel_publish_cached_blob(bridge_channel->chan, ast_channel_unhold_type(), NULL);
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2013-08-16 17:33:21 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Helper function to kick off a PBX app on a bridge_channel
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
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 {
|
2014-07-22 20:22:36 +00:00
|
|
|
struct ast_str *substituted_args = ast_str_create(16);
|
|
|
|
|
|
|
|
if (substituted_args) {
|
|
|
|
ast_str_substitute_variables(&substituted_args, 0, chan, app_args);
|
|
|
|
res = pbx_exec(chan, app, ast_str_buffer(substituted_args));
|
|
|
|
ast_free(substituted_args);
|
|
|
|
} else {
|
|
|
|
ast_log(LOG_WARNING, "Could not substitute application argument variables for %s\n", app_name);
|
|
|
|
res = pbx_exec(chan, app, app_args);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
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. */
|
2013-08-22 21:09:52 +00:00
|
|
|
ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Marshal an application to be executed on a bridge_channel
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
2013-07-24 19:24:09 +00:00
|
|
|
return post_it(bridge_channel, BRIDGE_CHANNEL_ACTION_RUN_APP, app_data, len_data);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
|
|
|
|
{
|
2013-07-25 02:20:23 +00:00
|
|
|
return payload_helper_app(bridge_channel_write_action_data,
|
2013-07-24 15:38:18 +00:00
|
|
|
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)
|
|
|
|
{
|
2013-07-25 02:20:23 +00:00
|
|
|
return payload_helper_app(bridge_channel_queue_action_data,
|
2013-07-24 15:38:18 +00:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_MOH)) {
|
2013-08-23 18:33:36 +00:00
|
|
|
const char *latest_musicclass;
|
|
|
|
|
|
|
|
ast_channel_lock(bridge_channel->chan);
|
|
|
|
latest_musicclass = ast_strdupa(ast_channel_latest_musicclass(bridge_channel->chan));
|
|
|
|
ast_channel_unlock(bridge_channel->chan);
|
|
|
|
ast_moh_start(bridge_channel->chan, latest_musicclass, NULL);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Marshal a file to be played on a bridge_channel
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
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. */
|
2014-03-17 17:22:12 +00:00
|
|
|
payload = ast_alloca(len_payload);
|
2013-07-24 15:38:18 +00:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
2013-07-24 19:24:09 +00:00
|
|
|
return post_it(bridge_channel, BRIDGE_CHANNEL_ACTION_PLAY_FILE, payload, len_payload);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2013-07-25 02:20:23 +00:00
|
|
|
return payload_helper_playfile(bridge_channel_write_action_data,
|
2013-07-24 15:38:18 +00:00
|
|
|
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)
|
|
|
|
{
|
2013-07-25 02:20:23 +00:00
|
|
|
return payload_helper_playfile(bridge_channel_queue_action_data,
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge_channel, custom_play, playfile, moh_class);
|
|
|
|
}
|
|
|
|
|
2014-03-17 17:22:12 +00:00
|
|
|
int ast_bridge_channel_queue_playfile_sync(struct ast_bridge_channel *bridge_channel,
|
|
|
|
ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
|
|
|
|
{
|
|
|
|
return payload_helper_playfile(bridge_channel_queue_action_data_sync,
|
|
|
|
bridge_channel, custom_play, playfile, moh_class);
|
|
|
|
}
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
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;
|
2013-08-21 15:51:19 +00:00
|
|
|
/*! Option flags determining how callback is called. */
|
|
|
|
unsigned int flags;
|
2013-07-24 15:38:18 +00:00
|
|
|
/*! 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
|
|
|
|
*
|
2013-08-21 15:51:19 +00:00
|
|
|
* \param bridge_channel Which channel to call the callback on.
|
|
|
|
* \param data Action frame data to call the callback.
|
2013-07-24 15:38:18 +00:00
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel, struct bridge_custom_callback *data)
|
|
|
|
{
|
2013-08-21 15:51:19 +00:00
|
|
|
if (ast_test_flag(data, AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA)) {
|
|
|
|
bridge_channel_suspend(bridge_channel);
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size);
|
2013-08-21 15:51:19 +00:00
|
|
|
if (ast_test_flag(data, AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA)) {
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
bridge_channel_unsuspend(bridge_channel);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Marshal a custom callback function to be called on a bridge_channel
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
static int payload_helper_cb(ast_bridge_channel_post_action_data post_it,
|
2013-08-21 15:51:19 +00:00
|
|
|
struct ast_bridge_channel *bridge_channel,
|
|
|
|
enum ast_bridge_channel_custom_callback_option flags,
|
|
|
|
ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
|
|
|
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;
|
2013-08-21 15:51:19 +00:00
|
|
|
cb_data->flags = flags;
|
2013-07-24 15:38:18 +00:00
|
|
|
cb_data->payload_exists = payload && payload_size;
|
|
|
|
if (cb_data->payload_exists) {
|
|
|
|
memcpy(cb_data->payload, payload, payload_size);/* Safe */
|
|
|
|
}
|
|
|
|
|
2013-07-24 19:24:09 +00:00
|
|
|
return post_it(bridge_channel, BRIDGE_CHANNEL_ACTION_CALLBACK, cb_data, len_data);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2013-08-21 15:51:19 +00:00
|
|
|
int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel,
|
|
|
|
enum ast_bridge_channel_custom_callback_option flags,
|
|
|
|
ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
2013-07-25 02:20:23 +00:00
|
|
|
return payload_helper_cb(bridge_channel_write_action_data,
|
2013-08-21 15:51:19 +00:00
|
|
|
bridge_channel, flags, callback, payload, payload_size);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2013-08-21 15:51:19 +00:00
|
|
|
int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel,
|
|
|
|
enum ast_bridge_channel_custom_callback_option flags,
|
|
|
|
ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
2013-07-25 02:20:23 +00:00
|
|
|
return payload_helper_cb(bridge_channel_queue_action_data,
|
2013-08-21 15:51:19 +00:00
|
|
|
bridge_channel, flags, callback, payload, payload_size);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct bridge_park {
|
|
|
|
int parker_uuid_offset;
|
|
|
|
int app_data_offset;
|
|
|
|
/* buffer used for holding those strings */
|
|
|
|
char parkee_uuid[0];
|
|
|
|
};
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Park a bridge_cahnnel
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload)
|
|
|
|
{
|
2013-08-17 15:01:54 +00:00
|
|
|
if (!ast_parking_provider_registered()) {
|
2013-08-01 20:55:17 +00:00
|
|
|
ast_log(AST_LOG_WARNING, "Unable to park %s: No parking provider loaded!\n",
|
|
|
|
ast_channel_name(bridge_channel->chan));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-17 15:01:54 +00:00
|
|
|
if (ast_parking_park_bridge_channel(bridge_channel, payload->parkee_uuid,
|
2013-07-24 15:38:18 +00:00
|
|
|
&payload->parkee_uuid[payload->parker_uuid_offset],
|
2013-08-01 20:55:17 +00:00
|
|
|
payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL)) {
|
|
|
|
ast_log(AST_LOG_WARNING, "Error occurred while parking %s\n",
|
|
|
|
ast_channel_name(bridge_channel->chan));
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Marshal a park action onto a bridge_channel
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-07-24 19:24:09 +00:00
|
|
|
return post_it(bridge_channel, BRIDGE_CHANNEL_ACTION_PARK, payload, len_payload);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data)
|
|
|
|
{
|
2013-07-25 02:20:23 +00:00
|
|
|
return payload_helper_park(bridge_channel_write_action_data,
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge_channel, parkee_uuid, parker_uuid, app_data);
|
|
|
|
}
|
|
|
|
|
2013-07-24 23:40:12 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Handle bridge channel interval expiration.
|
|
|
|
* \since 12.0.0
|
|
|
|
*
|
|
|
|
* \param bridge_channel Channel to run expired intervals on.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_channel)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
2013-07-24 23:40:12 +00:00
|
|
|
struct ast_heap *interval_hooks;
|
2013-07-24 21:13:00 +00:00
|
|
|
struct ast_bridge_hook_timer *hook;
|
2013-07-24 15:38:18 +00:00
|
|
|
struct timeval start;
|
2013-08-21 15:51:19 +00:00
|
|
|
int chan_suspended = 0;
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-07-24 23:40:12 +00:00
|
|
|
interval_hooks = bridge_channel->features->interval_hooks;
|
|
|
|
ast_heap_wrlock(interval_hooks);
|
2013-07-24 15:38:18 +00:00
|
|
|
start = ast_tvnow();
|
2013-07-24 23:40:12 +00:00
|
|
|
while ((hook = ast_heap_peek(interval_hooks, 1))) {
|
2013-07-24 15:38:18 +00:00
|
|
|
int interval;
|
|
|
|
unsigned int execution_time;
|
|
|
|
|
2013-07-24 21:13:00 +00:00
|
|
|
if (ast_tvdiff_ms(hook->timer.trip_time, start) > 0) {
|
2013-07-24 15:38:18 +00:00
|
|
|
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);
|
2013-07-24 23:40:12 +00:00
|
|
|
ast_heap_unlock(interval_hooks);
|
|
|
|
|
2013-08-21 15:51:19 +00:00
|
|
|
if (!chan_suspended
|
|
|
|
&& ast_test_flag(&hook->timer, AST_BRIDGE_HOOK_TIMER_OPTION_MEDIA)) {
|
|
|
|
chan_suspended = 1;
|
2013-07-24 23:40:12 +00:00
|
|
|
bridge_channel_suspend(bridge_channel);
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
|
|
|
|
ast_debug(1, "Executing hook %p on %p(%s)\n",
|
|
|
|
hook, bridge_channel, ast_channel_name(bridge_channel->chan));
|
2013-07-26 21:34:23 +00:00
|
|
|
interval = hook->generic.callback(bridge_channel, hook->generic.hook_pvt);
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-07-24 23:40:12 +00:00
|
|
|
ast_heap_wrlock(interval_hooks);
|
|
|
|
if (ast_heap_peek(interval_hooks, hook->timer.heap_index) != hook
|
|
|
|
|| !ast_heap_remove(interval_hooks, hook)) {
|
2013-07-24 15:38:18 +00:00
|
|
|
/* 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. */
|
2013-07-24 21:13:00 +00:00
|
|
|
hook->timer.interval = interval;
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ast_debug(1, "Updating interval hook %p with interval %u on %p(%s)\n",
|
2013-07-24 21:13:00 +00:00
|
|
|
hook, hook->timer.interval, bridge_channel,
|
2013-07-24 15:38:18 +00:00
|
|
|
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.
|
|
|
|
*/
|
2013-07-24 21:13:00 +00:00
|
|
|
execution_time = ast_tvdiff_ms(start, hook->timer.trip_time);
|
|
|
|
while (hook->timer.interval < execution_time) {
|
|
|
|
execution_time -= hook->timer.interval;
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
2013-07-24 21:13:00 +00:00
|
|
|
hook->timer.trip_time = ast_tvadd(start, ast_samp2tv(hook->timer.interval - execution_time, 1000));
|
|
|
|
hook->timer.seqno = ast_atomic_fetchadd_int((int *) &bridge_channel->features->interval_sequence, +1);
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-07-24 23:40:12 +00:00
|
|
|
if (ast_heap_push(interval_hooks, hook)) {
|
2013-07-24 15:38:18 +00:00
|
|
|
/* Could not push the hook back onto the heap. */
|
|
|
|
ao2_ref(hook, -1);
|
|
|
|
}
|
|
|
|
}
|
2013-07-24 23:40:12 +00:00
|
|
|
ast_heap_unlock(interval_hooks);
|
|
|
|
|
2013-08-21 15:51:19 +00:00
|
|
|
if (chan_suspended) {
|
2013-07-24 23:40:12 +00:00
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
bridge_channel_unsuspend(bridge_channel);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2013-08-16 17:33:21 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Write a DTMF stream out to a channel
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
static int bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf)
|
|
|
|
{
|
2013-07-25 02:20:23 +00:00
|
|
|
return bridge_channel_write_action_data(bridge_channel,
|
2013-07-24 19:24:09 +00:00
|
|
|
BRIDGE_CHANNEL_ACTION_DTMF_STREAM, dtmf, strlen(dtmf) + 1);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2013-08-23 21:02:26 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Indicate to the testsuite a feature was successfully detected.
|
|
|
|
*
|
|
|
|
* Currently, this function only will relay built-in features to the testsuite,
|
|
|
|
* but it could be modified to detect applicationmap items should the need arise.
|
|
|
|
*
|
|
|
|
* \param chan The channel that activated the feature
|
|
|
|
* \param dtmf The DTMF sequence entered to activate the feature
|
|
|
|
*/
|
|
|
|
static void testsuite_notify_feature_success(struct ast_channel *chan, const char *dtmf)
|
|
|
|
{
|
|
|
|
#ifdef TEST_FRAMEWORK
|
|
|
|
char *feature = "unknown";
|
2016-06-30 20:17:02 +00:00
|
|
|
struct ast_featuremap_config *featuremap;
|
|
|
|
struct ast_features_xfer_config *xfer;
|
|
|
|
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
featuremap = ast_get_chan_featuremap_config(chan);
|
|
|
|
xfer = ast_get_chan_features_xfer_config(chan);
|
|
|
|
ast_channel_unlock(chan);
|
2013-08-23 21:02:26 +00:00
|
|
|
|
|
|
|
if (featuremap) {
|
|
|
|
if (!strcmp(dtmf, featuremap->blindxfer)) {
|
|
|
|
feature = "blindxfer";
|
|
|
|
} else if (!strcmp(dtmf, featuremap->atxfer)) {
|
|
|
|
feature = "atxfer";
|
|
|
|
} else if (!strcmp(dtmf, featuremap->disconnect)) {
|
|
|
|
feature = "disconnect";
|
|
|
|
} else if (!strcmp(dtmf, featuremap->automon)) {
|
|
|
|
feature = "automon";
|
|
|
|
} else if (!strcmp(dtmf, featuremap->automixmon)) {
|
|
|
|
feature = "automixmon";
|
|
|
|
} else if (!strcmp(dtmf, featuremap->parkcall)) {
|
|
|
|
feature = "parkcall";
|
2014-07-15 23:03:40 +00:00
|
|
|
}
|
2014-07-17 14:28:16 +00:00
|
|
|
}
|
|
|
|
if (xfer) {
|
2014-07-15 23:03:40 +00:00
|
|
|
if (!strcmp(dtmf, xfer->atxferthreeway)) {
|
2013-10-23 16:48:39 +00:00
|
|
|
feature = "atxferthreeway";
|
2013-08-23 21:02:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-15 23:03:40 +00:00
|
|
|
ao2_cleanup(featuremap);
|
|
|
|
ao2_cleanup(xfer);
|
|
|
|
|
2013-08-23 21:02:26 +00:00
|
|
|
ast_test_suite_event_notify("FEATURE_DETECTION",
|
|
|
|
"Result: success\r\n"
|
|
|
|
"Feature: %s", feature);
|
|
|
|
#endif /* TEST_FRAMEWORK */
|
|
|
|
}
|
|
|
|
|
2015-03-26 17:13:26 +00:00
|
|
|
static int bridge_channel_feature_digit_add(
|
|
|
|
struct ast_bridge_channel *bridge_channel, int digit, size_t dtmf_len)
|
|
|
|
{
|
|
|
|
if (dtmf_len < ARRAY_LEN(bridge_channel->dtmf_hook_state.collected) - 1) {
|
|
|
|
/* Add the new digit to the DTMF string so we can do our matching */
|
|
|
|
bridge_channel->dtmf_hook_state.collected[dtmf_len++] = digit;
|
|
|
|
bridge_channel->dtmf_hook_state.collected[dtmf_len] = '\0';
|
|
|
|
|
|
|
|
ast_debug(1, "DTMF feature string on %p(%s) is now '%s'\n",
|
|
|
|
bridge_channel, ast_channel_name(bridge_channel->chan),
|
|
|
|
bridge_channel->dtmf_hook_state.collected);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dtmf_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int bridge_channel_feature_digit_timeout(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
unsigned int digit_timeout;
|
|
|
|
struct ast_features_general_config *gen_cfg;
|
|
|
|
|
|
|
|
/* Determine interdigit timeout */
|
|
|
|
ast_channel_lock(bridge_channel->chan);
|
|
|
|
gen_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
|
|
|
|
ast_channel_unlock(bridge_channel->chan);
|
|
|
|
|
|
|
|
if (!gen_cfg) {
|
|
|
|
ast_log(LOG_ERROR, "Unable to retrieve features configuration.\n");
|
|
|
|
return 3000; /* Pick a reasonable failsafe timeout in ms */
|
|
|
|
}
|
|
|
|
|
|
|
|
digit_timeout = gen_cfg->featuredigittimeout;
|
|
|
|
ao2_ref(gen_cfg, -1);
|
|
|
|
|
|
|
|
return digit_timeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_bridge_channel_feature_digit_add(struct ast_bridge_channel *bridge_channel, int digit)
|
|
|
|
{
|
|
|
|
if (digit) {
|
|
|
|
bridge_channel_feature_digit_add(
|
|
|
|
bridge_channel, digit, strlen(bridge_channel->dtmf_hook_state.collected));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
void ast_bridge_channel_feature_digit(struct ast_bridge_channel *bridge_channel, int digit)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
|
|
|
struct ast_bridge_features *features = bridge_channel->features;
|
2013-07-24 21:13:00 +00:00
|
|
|
struct ast_bridge_hook_dtmf *hook = NULL;
|
2013-08-15 14:20:59 +00:00
|
|
|
size_t dtmf_len;
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
struct sanity_check_of_dtmf_size {
|
|
|
|
char check[1 / (ARRAY_LEN(bridge_channel->dtmf_hook_state.collected) == ARRAY_LEN(hook->dtmf.code))];
|
|
|
|
};
|
|
|
|
|
|
|
|
dtmf_len = strlen(bridge_channel->dtmf_hook_state.collected);
|
|
|
|
if (!dtmf_len && !digit) {
|
|
|
|
/* Nothing to do */
|
2013-07-24 15:38:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
if (digit) {
|
2015-03-26 17:13:26 +00:00
|
|
|
dtmf_len = bridge_channel_feature_digit_add(bridge_channel, digit, dtmf_len);
|
|
|
|
}
|
2013-08-15 14:20:59 +00:00
|
|
|
|
2015-03-26 17:13:26 +00:00
|
|
|
while (digit) {
|
2014-11-06 19:12:18 +00:00
|
|
|
/* See if a DTMF feature hook matches or can match */
|
|
|
|
hook = ao2_find(features->dtmf_hooks, bridge_channel->dtmf_hook_state.collected,
|
|
|
|
OBJ_SEARCH_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),
|
|
|
|
bridge_channel->dtmf_hook_state.collected);
|
2015-03-26 17:13:26 +00:00
|
|
|
break;
|
2014-11-06 19:12:18 +00:00
|
|
|
} else if (dtmf_len != strlen(hook->dtmf.code)) {
|
|
|
|
unsigned int digit_timeout;
|
|
|
|
/* Need more digits to match */
|
|
|
|
ao2_ref(hook, -1);
|
2015-03-26 17:13:26 +00:00
|
|
|
digit_timeout = bridge_channel_feature_digit_timeout(bridge_channel);
|
2014-11-06 19:12:18 +00:00
|
|
|
bridge_channel->dtmf_hook_state.interdigit_timeout =
|
|
|
|
ast_tvadd(ast_tvnow(), ast_samp2tv(digit_timeout, 1000));
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
int remove_me;
|
|
|
|
int already_suspended;
|
|
|
|
|
|
|
|
ast_debug(1, "DTMF feature hook %p matched DTMF string '%s' on %p(%s)\n",
|
|
|
|
hook, bridge_channel->dtmf_hook_state.collected, bridge_channel,
|
|
|
|
ast_channel_name(bridge_channel->chan));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the collected digits before executing the hook
|
|
|
|
* in case the hook starts another sequence.
|
|
|
|
*/
|
|
|
|
bridge_channel->dtmf_hook_state.collected[0] = '\0';
|
|
|
|
|
|
|
|
ast_bridge_channel_lock_bridge(bridge_channel);
|
|
|
|
already_suspended = bridge_channel->suspended;
|
|
|
|
if (!already_suspended) {
|
|
|
|
bridge_channel_internal_suspend_nolock(bridge_channel);
|
2013-08-15 14:20:59 +00:00
|
|
|
}
|
2014-11-06 19:12:18 +00:00
|
|
|
ast_bridge_unlock(bridge_channel->bridge);
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
|
|
|
|
/* Execute the matched hook on this channel. */
|
|
|
|
remove_me = hook->generic.callback(bridge_channel, hook->generic.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);
|
|
|
|
}
|
|
|
|
testsuite_notify_feature_success(bridge_channel->chan, hook->dtmf.code);
|
2013-08-15 14:20:59 +00:00
|
|
|
ao2_ref(hook, -1);
|
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
if (!already_suspended) {
|
|
|
|
bridge_channel_unsuspend(bridge_channel);
|
2013-08-15 14:20:59 +00:00
|
|
|
}
|
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
/*
|
|
|
|
* 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)) {
|
|
|
|
ast_bridge_channel_kick(bridge_channel, 0);
|
2015-03-26 17:13:26 +00:00
|
|
|
bridge_channel->dtmf_hook_state.collected[0] = '\0';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if there is dtmf that has been collected then loop back through,
|
|
|
|
but set digit to -1 so it doesn't try to do an add since the dtmf
|
|
|
|
is already in the buffer */
|
|
|
|
dtmf_len = strlen(bridge_channel->dtmf_hook_state.collected);
|
|
|
|
if (!dtmf_len) {
|
|
|
|
return;
|
2014-11-06 19:12:18 +00:00
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
2015-03-26 17:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!digit) {
|
2014-11-06 19:12:18 +00:00
|
|
|
ast_debug(1, "DTMF feature string collection on %p(%s) timed out\n",
|
|
|
|
bridge_channel, ast_channel_name(bridge_channel->chan));
|
2013-08-15 14:20:59 +00:00
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
/* Timeout or DTMF digit didn't allow a match with any hooks. */
|
|
|
|
if (features->dtmf_passthrough) {
|
|
|
|
/* Stream the collected DTMF to the other channels. */
|
|
|
|
bridge_channel_write_dtmf_stream(bridge_channel,
|
|
|
|
bridge_channel->dtmf_hook_state.collected);
|
|
|
|
}
|
|
|
|
bridge_channel->dtmf_hook_state.collected[0] = '\0';
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
ast_test_suite_event_notify("FEATURE_DETECTION", "Result: fail");
|
|
|
|
}
|
2013-08-23 21:51:19 +00:00
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Handle bridge channel DTMF feature timeout expiration.
|
|
|
|
* \since 12.8.0
|
|
|
|
*
|
|
|
|
* \param bridge_channel Channel to check expired interdigit timer on.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void bridge_channel_handle_feature_timeout(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
if (!bridge_channel->dtmf_hook_state.collected[0]
|
|
|
|
|| 0 < ast_tvdiff_ms(bridge_channel->dtmf_hook_state.interdigit_timeout,
|
|
|
|
ast_tvnow())) {
|
|
|
|
/* Not within a sequence or not timed out. */
|
|
|
|
return;
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
2014-11-06 19:12:18 +00:00
|
|
|
|
|
|
|
ast_bridge_channel_feature_digit(bridge_channel, 0);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2013-08-16 17:33:21 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Indicate that a bridge_channel is talking
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
static void bridge_channel_talking(struct ast_bridge_channel *bridge_channel, int talking)
|
|
|
|
{
|
|
|
|
struct ast_bridge_features *features = bridge_channel->features;
|
2013-07-24 21:13:00 +00:00
|
|
|
struct ast_bridge_hook *hook;
|
|
|
|
struct ao2_iterator iter;
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-07-24 21:13:00 +00:00
|
|
|
/* Run any talk detection hooks. */
|
|
|
|
iter = ao2_iterator_init(features->other_hooks, 0);
|
|
|
|
for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
|
|
|
|
int remove_me;
|
|
|
|
ast_bridge_talking_indicate_callback talk_cb;
|
|
|
|
|
|
|
|
if (hook->type != AST_BRIDGE_HOOK_TYPE_TALK) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
talk_cb = (ast_bridge_talking_indicate_callback) hook->callback;
|
|
|
|
remove_me = talk_cb(bridge_channel, hook->hook_pvt, talking);
|
|
|
|
if (remove_me) {
|
|
|
|
ast_debug(1, "Talk detection hook %p is being removed from %p(%s)\n",
|
|
|
|
hook, bridge_channel, ast_channel_name(bridge_channel->chan));
|
|
|
|
ao2_unlink(features->other_hooks, hook);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
2013-07-24 21:13:00 +00:00
|
|
|
ao2_iterator_destroy(&iter);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \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);
|
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/*! \brief Data specifying where a blind transfer is going to */
|
|
|
|
struct blind_transfer_data {
|
|
|
|
char exten[AST_MAX_EXTENSION];
|
|
|
|
char context[AST_MAX_CONTEXT];
|
|
|
|
};
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-08-16 17:33:21 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Execute after bridge actions on a channel when it leaves a bridge
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-06-10 23:28:26 +00:00
|
|
|
/* The ast_channel_move function will end up updating the connected line information
|
|
|
|
* on chan_target to the value we have here, but will not inform it. To ensure that
|
|
|
|
* AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO is executed we wipe it away here. If
|
|
|
|
* we don't do this then the change will be considered redundant, since the connected
|
|
|
|
* line information is already there (despite the channel not being told).
|
|
|
|
*/
|
|
|
|
ast_channel_lock(chan_target);
|
|
|
|
ast_party_connected_line_free(ast_channel_connected_indicated(chan_target));
|
|
|
|
ast_party_connected_line_init(ast_channel_connected_indicated(chan_target));
|
|
|
|
ast_channel_unlock(chan_target);
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-06-10 23:28:26 +00:00
|
|
|
/* A connected line update is queued so that if chan_target is remotely involved with
|
|
|
|
* anything (such as dialing a channel) the other channel(s) will be informed of the
|
|
|
|
* new channel they are involved with.
|
|
|
|
*/
|
|
|
|
ast_channel_lock(chan_target);
|
|
|
|
ast_connected_line_copy_from_caller(&connected_target, ast_channel_caller(chan_target));
|
|
|
|
ast_channel_queue_connected_line_update(chan_target, &connected_target, NULL);
|
|
|
|
ast_channel_unlock(chan_target);
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_party_connected_line_free(&connected_target);
|
|
|
|
}
|
|
|
|
|
2013-08-16 17:33:21 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Execute logic to cleanup when after bridge fails
|
|
|
|
*/
|
2013-07-25 02:20:23 +00:00
|
|
|
static void after_bridge_move_channel_fail(enum ast_bridge_after_cb_reason reason, void *data)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
|
|
|
RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup);
|
|
|
|
|
|
|
|
ast_log(LOG_WARNING, "Unable to complete transfer: %s\n",
|
2013-07-25 02:20:23 +00:00
|
|
|
ast_bridge_after_cb_reason_string(reason));
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
|
|
|
|
}
|
|
|
|
|
2013-08-16 17:33:21 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Perform a blind transfer on a channel in a bridge
|
|
|
|
*/
|
2013-07-25 02:20:23 +00:00
|
|
|
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);
|
2013-08-22 21:09:52 +00:00
|
|
|
ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING);
|
2013-07-25 02:20:23 +00:00
|
|
|
}
|
|
|
|
|
2013-08-16 17:33:21 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Perform an attended transfer on a channel in a bridge
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
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 */
|
2013-08-22 21:09:52 +00:00
|
|
|
ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING);
|
2013-07-24 15:38:18 +00:00
|
|
|
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);
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
if (ast_bridge_set_after_callback(chan_bridged, after_bridge_move_channel,
|
2013-07-24 15:38:18 +00:00
|
|
|
after_bridge_move_channel_fail, ast_channel_ref(chan_target))) {
|
|
|
|
ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
/* Release the ref we tried to pass to ast_bridge_set_after_callback(). */
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_channel_unref(chan_target);
|
|
|
|
}
|
2013-08-22 21:09:52 +00:00
|
|
|
ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \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.
|
2014-03-17 17:22:12 +00:00
|
|
|
* \param data data from the action.
|
2013-07-24 15:38:18 +00:00
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
2014-03-17 17:22:12 +00:00
|
|
|
static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_channel,
|
|
|
|
enum bridge_channel_action_type action, void *data)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
2014-03-17 17:22:12 +00:00
|
|
|
switch (action) {
|
2013-07-24 19:24:09 +00:00
|
|
|
case BRIDGE_CHANNEL_ACTION_DTMF_STREAM:
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge_channel_suspend(bridge_channel);
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_channel_dtmf_stream(bridge_channel, data);
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
bridge_channel_unsuspend(bridge_channel);
|
|
|
|
break;
|
2013-07-24 19:24:09 +00:00
|
|
|
case BRIDGE_CHANNEL_ACTION_TALKING_START:
|
|
|
|
case BRIDGE_CHANNEL_ACTION_TALKING_STOP:
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge_channel_talking(bridge_channel,
|
2014-03-17 17:22:12 +00:00
|
|
|
action == BRIDGE_CHANNEL_ACTION_TALKING_START);
|
2013-07-24 15:38:18 +00:00
|
|
|
break;
|
2013-07-24 19:24:09 +00:00
|
|
|
case BRIDGE_CHANNEL_ACTION_PLAY_FILE:
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge_channel_suspend(bridge_channel);
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_channel_playfile(bridge_channel, data);
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
bridge_channel_unsuspend(bridge_channel);
|
|
|
|
break;
|
2013-07-24 19:24:09 +00:00
|
|
|
case BRIDGE_CHANNEL_ACTION_RUN_APP:
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge_channel_suspend(bridge_channel);
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_channel_run_app(bridge_channel, data);
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
bridge_channel_unsuspend(bridge_channel);
|
|
|
|
break;
|
2013-07-24 19:24:09 +00:00
|
|
|
case BRIDGE_CHANNEL_ACTION_CALLBACK:
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_channel_do_callback(bridge_channel, data);
|
2013-07-24 15:38:18 +00:00
|
|
|
break;
|
2013-07-24 19:24:09 +00:00
|
|
|
case BRIDGE_CHANNEL_ACTION_PARK:
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge_channel_suspend(bridge_channel);
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_channel_park(bridge_channel, data);
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
bridge_channel_unsuspend(bridge_channel);
|
|
|
|
break;
|
2013-07-24 19:24:09 +00:00
|
|
|
case BRIDGE_CHANNEL_ACTION_BLIND_TRANSFER:
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_channel_blind_transfer(bridge_channel, data);
|
2013-07-24 15:38:18 +00:00
|
|
|
break;
|
2013-07-24 19:24:09 +00:00
|
|
|
case BRIDGE_CHANNEL_ACTION_ATTENDED_TRANSFER:
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_channel_attended_transfer(bridge_channel, data);
|
2013-07-24 15:38:18 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2015-08-24 11:21:37 +00:00
|
|
|
|
|
|
|
/* While invoking an action it is possible for the channel to be hung up. So
|
|
|
|
* that the bridge respects this we check here and if hung up kick it out.
|
|
|
|
*/
|
|
|
|
if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) {
|
|
|
|
ast_bridge_channel_kick(bridge_channel, 0);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \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
|
|
|
|
*/
|
2013-07-25 02:20:23 +00:00
|
|
|
static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_channel)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
|
|
|
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. */
|
2013-08-22 21:09:52 +00:00
|
|
|
bridge_dissolve(bridge, ast_channel_hangupcause(bridge_channel->chan));
|
2013-07-24 15:38:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (bridge_channel->state) {
|
2013-07-25 02:20:23 +00:00
|
|
|
case BRIDGE_CHANNEL_STATE_END:
|
2013-07-24 15:38:18 +00:00
|
|
|
/* 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))) {
|
2013-08-22 21:09:52 +00:00
|
|
|
bridge_dissolve(bridge, ast_channel_hangupcause(bridge_channel->chan));
|
2013-07-24 15:38:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-08-09 17:22:28 +00:00
|
|
|
|
|
|
|
if (bridge->num_lonely && bridge->num_lonely == bridge->num_channels) {
|
2013-08-22 21:09:52 +00:00
|
|
|
/*
|
|
|
|
* This will start a chain reaction where each channel leaving
|
|
|
|
* enters this function and causes the next to leave as long as
|
|
|
|
* there aren't non-lonely channels in the bridge.
|
|
|
|
*/
|
|
|
|
ast_bridge_channel_leave_bridge(AST_LIST_FIRST(&bridge->channels),
|
|
|
|
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE,
|
|
|
|
ast_channel_hangupcause(bridge_channel->chan));
|
2013-08-09 17:22:28 +00:00
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2013-08-09 17:22:28 +00:00
|
|
|
if (ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_LONELY)) {
|
|
|
|
--bridge->num_lonely;
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
--bridge->num_channels;
|
|
|
|
AST_LIST_REMOVE(&bridge->channels, bridge_channel, entry);
|
2014-07-09 16:34:51 +00:00
|
|
|
|
|
|
|
bridge_channel_dissolve_check(bridge_channel);
|
2013-07-24 15:38:18 +00:00
|
|
|
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.
|
|
|
|
*/
|
2014-05-19 01:10:23 +00:00
|
|
|
if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING)
|
2014-06-08 18:12:53 +00:00
|
|
|
&& (ast_channel_is_leaving_bridge(bridge_channel->chan)
|
|
|
|
|| bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT)) {
|
Better handle clearing the OUTGOING flag when a channel leaves a bridge
When a channel with the OUTGOING flag leaves a bridge, and it will survive
being pulled from the bridge (either because it will execute dialplan,
go into another bridge, or live in a friendly autoloop), we have to clear
the OUTGOING flag. This is the signal to the CDR engine that this channel
is no longer a second class citizen, i.e., it is not "dialed".
The soft hangup flags are only half the picture. If a channel is being
moved from one bridge to another, the soft hangup flags aren't set; however,
the state of the bridge_channel will not be hung up. Since the channel does
not have one of the two hang up states, that implies that the channel is
still technically alive.
This patch modifies the check so that it checks both the soft hangup flags
as well as the bridge_channel state. If either suggests that the channel
is going to persist, we clear the OUTGOING flag.
........
Merged revisions 397690 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@397691 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-26 23:48:56 +00:00
|
|
|
ast_debug(2, "Channel %s will survive this bridge; clearing outgoing (dialed) flag\n", ast_channel_name(bridge_channel->chan));
|
2017-05-13 16:40:00 +00:00
|
|
|
ast_channel_clear_flag(bridge_channel->chan, AST_FLAG_OUTGOING);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bridge->reconfigured = 1;
|
|
|
|
ast_bridge_publish_leave(bridge, bridge_channel->chan);
|
|
|
|
}
|
|
|
|
|
2016-02-20 01:06:14 +00:00
|
|
|
int bridge_channel_internal_push_full(struct ast_bridge_channel *bridge_channel, int optimized)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
|
|
|
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
|
2013-07-25 02:20:23 +00:00
|
|
|
|| bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT
|
|
|
|
|| (swap && swap->state != BRIDGE_CHANNEL_STATE_WAIT)
|
2016-04-15 16:41:49 +00:00
|
|
|
|| bridge->v_table->push(bridge, bridge_channel, swap)) {
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n",
|
|
|
|
bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
|
|
|
|
return -1;
|
|
|
|
}
|
2015-01-27 17:34:37 +00:00
|
|
|
|
2016-04-15 16:41:49 +00:00
|
|
|
ast_bridge_channel_establish_roles(bridge_channel);
|
|
|
|
|
2015-01-27 17:34:37 +00:00
|
|
|
if (swap) {
|
|
|
|
int dissolve = ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY);
|
|
|
|
|
|
|
|
/* This flag is cleared so the act of this channel leaving does not cause it to dissolve if need be */
|
|
|
|
ast_clear_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY);
|
|
|
|
|
2016-02-20 01:06:14 +00:00
|
|
|
if (optimized) {
|
|
|
|
bridge_channel_cancel_owed_events(swap);
|
|
|
|
}
|
2015-01-27 17:34:37 +00:00
|
|
|
ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0);
|
|
|
|
bridge_channel_internal_pull(swap);
|
|
|
|
|
|
|
|
ast_set2_flag(&bridge->feature_flags, dissolve, AST_BRIDGE_FLAG_DISSOLVE_EMPTY);
|
|
|
|
}
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge_channel->in_bridge = 1;
|
|
|
|
bridge_channel->just_joined = 1;
|
|
|
|
AST_LIST_INSERT_TAIL(&bridge->channels, bridge_channel, entry);
|
|
|
|
++bridge->num_channels;
|
2013-08-09 17:22:28 +00:00
|
|
|
if (ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_LONELY)) {
|
|
|
|
++bridge->num_lonely;
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
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);
|
|
|
|
|
2013-08-02 14:13:04 +00:00
|
|
|
ast_bridge_publish_enter(bridge, bridge_channel->chan, swap ? swap->chan : NULL);
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2016-05-25 15:34:42 +00:00
|
|
|
/* Clear any BLINDTRANSFER,ATTENDEDTRANSFER and FORWARDERNAME since the transfer has completed. */
|
2013-07-24 15:38:18 +00:00
|
|
|
pbx_builtin_setvar_helper(bridge_channel->chan, "BLINDTRANSFER", NULL);
|
|
|
|
pbx_builtin_setvar_helper(bridge_channel->chan, "ATTENDEDTRANSFER", NULL);
|
2016-05-25 15:34:42 +00:00
|
|
|
pbx_builtin_setvar_helper(bridge_channel->chan, "FORWARDERNAME", NULL);
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-07-24 23:40:12 +00:00
|
|
|
/* Wake up the bridge channel thread to reevaluate any interval timers. */
|
|
|
|
ast_queue_frame(bridge_channel->chan, &ast_null_frame);
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge->reconfigured = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-20 01:06:14 +00:00
|
|
|
int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
return bridge_channel_internal_push_full(bridge_channel, 0);
|
|
|
|
}
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
/*!
|
|
|
|
* \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;
|
|
|
|
|
|
|
|
chan = bridge_channel->chan;
|
|
|
|
switch (fr->subclass.integer) {
|
|
|
|
case AST_CONTROL_REDIRECTING:
|
|
|
|
is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);
|
2013-08-15 22:10:20 +00:00
|
|
|
if (ast_channel_redirecting_sub(NULL, chan, fr, 1) &&
|
|
|
|
ast_channel_redirecting_macro(NULL, chan, fr, is_caller, 1)) {
|
2013-07-24 15:38:18 +00:00
|
|
|
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);
|
2013-08-15 22:10:20 +00:00
|
|
|
if (ast_channel_connected_line_sub(NULL, chan, fr, 1) &&
|
|
|
|
ast_channel_connected_line_macro(NULL, chan, fr, is_caller, 1)) {
|
2013-07-24 15:38:18 +00:00
|
|
|
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;
|
2014-10-03 17:47:42 +00:00
|
|
|
case AST_CONTROL_MASQUERADE_NOTIFY:
|
|
|
|
/* Should never happen. */
|
|
|
|
ast_assert(0);
|
|
|
|
break;
|
2017-04-25 16:49:16 +00:00
|
|
|
case AST_CONTROL_STREAM_TOPOLOGY_CHANGED:
|
|
|
|
ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
|
|
|
|
break;
|
2013-07-24 15:38:18 +00:00
|
|
|
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;
|
2014-03-17 17:22:12 +00:00
|
|
|
struct sync_payload *sync_payload;
|
2017-04-25 16:49:16 +00:00
|
|
|
int num;
|
2013-07-24 15:38:18 +00:00
|
|
|
|
|
|
|
ast_bridge_channel_lock(bridge_channel);
|
2014-11-06 19:12:18 +00:00
|
|
|
|
|
|
|
/* It's not good to have unbalanced frames and alert_pipe alerts. */
|
|
|
|
ast_assert(!AST_LIST_EMPTY(&bridge_channel->wr_queue));
|
|
|
|
if (AST_LIST_EMPTY(&bridge_channel->wr_queue)) {
|
|
|
|
/* No frame, flush the alert pipe of excess alerts. */
|
|
|
|
ast_log(LOG_WARNING, "Weird. No frame from bridge for %s to process?\n",
|
|
|
|
ast_channel_name(bridge_channel->chan));
|
2017-04-18 00:06:10 +00:00
|
|
|
ast_alertpipe_read(bridge_channel->alert_pipe);
|
2014-11-06 19:12:18 +00:00
|
|
|
ast_bridge_channel_unlock(bridge_channel);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&bridge_channel->wr_queue, fr, frame_list) {
|
|
|
|
if (bridge_channel->dtmf_hook_state.collected[0]) {
|
|
|
|
switch (fr->frametype) {
|
|
|
|
case AST_FRAME_BRIDGE_ACTION:
|
|
|
|
case AST_FRAME_BRIDGE_ACTION_SYNC:
|
|
|
|
/* Defer processing these frames while DTMF is collected. */
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
2017-04-18 00:06:10 +00:00
|
|
|
ast_alertpipe_read(bridge_channel->alert_pipe);
|
2014-11-06 19:12:18 +00:00
|
|
|
AST_LIST_REMOVE_CURRENT(frame_list);
|
|
|
|
break;
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
2014-11-06 19:12:18 +00:00
|
|
|
AST_LIST_TRAVERSE_SAFE_END;
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_bridge_channel_unlock(bridge_channel);
|
|
|
|
if (!fr) {
|
2014-11-06 19:12:18 +00:00
|
|
|
/*
|
|
|
|
* Wait some to reduce CPU usage from a tight loop
|
|
|
|
* without any wait because we only have deferred
|
|
|
|
* frames in the wr_queue.
|
|
|
|
*/
|
|
|
|
usleep(1);
|
2013-07-24 15:38:18 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-11-06 19:12:18 +00:00
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
switch (fr->frametype) {
|
|
|
|
case AST_FRAME_BRIDGE_ACTION:
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_channel_handle_action(bridge_channel, fr->subclass.integer, fr->data.ptr);
|
|
|
|
break;
|
|
|
|
case AST_FRAME_BRIDGE_ACTION_SYNC:
|
|
|
|
sync_payload = fr->data.ptr;
|
|
|
|
bridge_channel_handle_action(bridge_channel, fr->subclass.integer, sync_payload->data);
|
2013-07-24 15:38:18 +00:00
|
|
|
break;
|
|
|
|
case AST_FRAME_CONTROL:
|
|
|
|
bridge_channel_handle_control(bridge_channel, fr);
|
|
|
|
break;
|
|
|
|
case AST_FRAME_NULL:
|
|
|
|
break;
|
|
|
|
default:
|
Add primitive SFU support to bridge_softmix.
This sets up the "plumbing" in bridge_softmix to
be able to accommodate Asterisk asking as an SFU
(selective forwarding unit) for conferences.
The way this works is that whenever a channel enters or leaves a
conference, all participants in the bridge get sent a stream topology
change request. The topologies consist of the channels' original
topology, along with video destination streams corresponding to each
participants' source video streams. So for instance, if Alice, Bob, and
Carol are in the conference, and each supplies one video stream, then
the topologies for each would look like so:
Alice:
Audio,
Source video(Alice),
Destination Video(Bob),
Destination video (Carol)
Bob:
Audio,
Source video(Bob)
Destination Video(Alice),
Destination video (Carol)
Carol:
Audio,
Source video(Carol)
Destination Video(Alice),
Destination video (Bob)
This way, video that arrives from a source video stream can then be
copied out to the destination video streams on the other participants'
channels.
Once the bridge gets told that a topology on a channel has changed, the
bridge constructs a map in order to get the video frames routed to the
proper destination streams. This is done using the bridge channel's
stream_map.
This change is bare-bones with regards to SFU support. Some key features
are missing at this point:
* Stream limits. This commit makes no effort to limit the number of
streams on a specific channel. This means that if there were 50 video
callers in a conference, bridge_softmix will happily send out topology
change requests to every channel in the bridge, requesting 50+
streams.
* Configuration. The plumbing has been added to bridge_softmix, but
there has been nothing added as of yet to app_confbridge to enable SFU
video mode.
* Testing. Some functions included here have unit tests.
However, the functionality as a whole has only been verified by
hand-tracing the code.
* Selectivenss. For a "selective" forwarding unit, this does not
currently have any means of being selective.
* Features. Presumably, someone might wish to only receive video from
specific sources. There are no external-facing functions at the moment
that allow for users to select who they receive video from.
* Efficiency. The current scheme treats all video streams as being
unidirectional. We could be re-using a source video stream as a
desetnation, too. But to simplify things on this first round, I did it
this way.
Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
|
|
|
if (fr->stream_num > 0 &&
|
|
|
|
(fr->stream_num >= (int)AST_VECTOR_SIZE(&bridge_channel->stream_map.to_channel) ||
|
|
|
|
AST_VECTOR_GET(&bridge_channel->stream_map.to_channel, fr->stream_num) == -1)) {
|
2017-04-25 16:49:16 +00:00
|
|
|
/* Nowhere to write to, so drop it */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find what stream number to write to for the channel */
|
|
|
|
num = fr->stream_num < 0 ? -1 :
|
|
|
|
AST_VECTOR_GET(&bridge_channel->stream_map.to_channel, fr->stream_num);
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
/* Write the frame to the channel. */
|
2013-07-25 02:20:23 +00:00
|
|
|
bridge_channel->activity = BRIDGE_CHANNEL_THREAD_SIMPLE;
|
2017-04-25 16:49:16 +00:00
|
|
|
ast_write_stream(bridge_channel->chan, num, fr);
|
2013-07-24 15:38:18 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_frame_free(fr);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \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;
|
2014-11-06 19:12:18 +00:00
|
|
|
struct ast_bridge_hook_dtmf *hook = NULL;
|
2013-07-24 15:38:18 +00:00
|
|
|
char dtmf[2];
|
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
/*
|
|
|
|
* See if we are already matching a DTMF feature hook sequence or
|
|
|
|
* if this DTMF matches the beginning of any DTMF feature hooks.
|
|
|
|
*/
|
2013-07-24 15:38:18 +00:00
|
|
|
dtmf[0] = frame->subclass.integer;
|
|
|
|
dtmf[1] = '\0';
|
2014-11-06 19:12:18 +00:00
|
|
|
if (bridge_channel->dtmf_hook_state.collected[0]
|
|
|
|
|| (hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_SEARCH_PARTIAL_KEY))) {
|
2013-08-15 14:20:59 +00:00
|
|
|
enum ast_frame_type frametype = frame->frametype;
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_frame_free(frame);
|
2013-07-24 15:38:18 +00:00
|
|
|
frame = NULL;
|
2013-08-15 14:20:59 +00:00
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
ao2_cleanup(hook);
|
2013-08-15 14:20:59 +00:00
|
|
|
|
|
|
|
switch (frametype) {
|
|
|
|
case AST_FRAME_DTMF_BEGIN:
|
2014-11-06 19:12:18 +00:00
|
|
|
/* Just eat the frame. */
|
2013-08-15 14:20:59 +00:00
|
|
|
break;
|
|
|
|
case AST_FRAME_DTMF_END:
|
2014-11-06 19:12:18 +00:00
|
|
|
ast_bridge_channel_feature_digit(bridge_channel, dtmf[0]);
|
2013-08-15 14:20:59 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Unexpected frame type. */
|
|
|
|
ast_assert(0);
|
|
|
|
break;
|
|
|
|
}
|
2013-08-23 21:02:26 +00:00
|
|
|
#ifdef TEST_FRAMEWORK
|
|
|
|
} else if (frame->frametype == AST_FRAME_DTMF_END) {
|
|
|
|
/* Only transmit this event on DTMF end or else every DTMF
|
|
|
|
* press will result in the event being broadcast twice
|
|
|
|
*/
|
|
|
|
ast_test_suite_event_notify("FEATURE_DETECTION", "Result: fail");
|
|
|
|
#endif
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return frame;
|
|
|
|
}
|
|
|
|
|
2016-05-09 20:00:56 +00:00
|
|
|
static const char *controls[] = {
|
|
|
|
[AST_CONTROL_RINGING] = "RINGING",
|
|
|
|
[AST_CONTROL_PROCEEDING] = "PROCEEDING",
|
|
|
|
[AST_CONTROL_PROGRESS] = "PROGRESS",
|
|
|
|
[AST_CONTROL_BUSY] = "BUSY",
|
|
|
|
[AST_CONTROL_CONGESTION] = "CONGESTION",
|
|
|
|
[AST_CONTROL_ANSWER] = "ANSWER",
|
|
|
|
};
|
2013-07-24 15:38:18 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
* \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;
|
|
|
|
|
2016-05-09 20:00:56 +00:00
|
|
|
if (!ast_strlen_zero(ast_channel_call_forward(bridge_channel->chan))) {
|
|
|
|
/* TODO If early bridging is ever used by anything other than ARI,
|
|
|
|
* it's important that we actually attempt to handle the call forward
|
|
|
|
* attempt, as well as expand features on a bridge channel to allow/disallow
|
|
|
|
* call forwarding. For now, all we do is raise an event, showing that
|
|
|
|
* a call forward is being attempted.
|
|
|
|
*/
|
|
|
|
ast_channel_publish_dial_forward(NULL, bridge_channel->chan, NULL, NULL, "CANCEL",
|
|
|
|
ast_channel_call_forward(bridge_channel->chan));
|
|
|
|
}
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
if (bridge_channel->features->mute) {
|
|
|
|
frame = ast_read_noaudio(bridge_channel->chan);
|
|
|
|
} else {
|
|
|
|
frame = ast_read(bridge_channel->chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!frame) {
|
2013-08-22 21:09:52 +00:00
|
|
|
ast_bridge_channel_kick(bridge_channel, 0);
|
2013-07-24 15:38:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (frame->frametype) {
|
|
|
|
case AST_FRAME_CONTROL:
|
|
|
|
switch (frame->subclass.integer) {
|
2016-05-09 20:00:56 +00:00
|
|
|
case AST_CONTROL_CONGESTION:
|
|
|
|
case AST_CONTROL_BUSY:
|
|
|
|
ast_channel_publish_dial(NULL, bridge_channel->chan, NULL, controls[frame->subclass.integer]);
|
|
|
|
break;
|
2013-07-24 15:38:18 +00:00
|
|
|
case AST_CONTROL_HANGUP:
|
2013-08-22 21:09:52 +00:00
|
|
|
ast_bridge_channel_kick(bridge_channel, 0);
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_frame_free(frame);
|
2013-07-24 15:38:18 +00:00
|
|
|
return;
|
2016-05-09 20:00:56 +00:00
|
|
|
case AST_CONTROL_RINGING:
|
|
|
|
case AST_CONTROL_PROGRESS:
|
|
|
|
case AST_CONTROL_PROCEEDING:
|
|
|
|
case AST_CONTROL_ANSWER:
|
|
|
|
ast_channel_publish_dial(NULL, bridge_channel->chan, NULL, controls[frame->subclass.integer]);
|
|
|
|
break;
|
2017-04-25 16:49:16 +00:00
|
|
|
case AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE:
|
|
|
|
if (bridge_channel->bridge->technology->stream_topology_request_change &&
|
|
|
|
bridge_channel->bridge->technology->stream_topology_request_change(
|
|
|
|
bridge_channel->bridge, bridge_channel)) {
|
|
|
|
/* Topology change was denied so drop frame */
|
|
|
|
bridge_frame_free(frame);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AST_CONTROL_STREAM_TOPOLOGY_CHANGED:
|
|
|
|
/*
|
|
|
|
* If a stream topology has changed then the bridge_channel's
|
|
|
|
* media mapping needs to be updated.
|
|
|
|
*/
|
|
|
|
if (bridge_channel->bridge->technology->stream_topology_changed) {
|
|
|
|
bridge_channel->bridge->technology->stream_topology_changed(
|
|
|
|
bridge_channel->bridge, bridge_channel);
|
Add primitive SFU support to bridge_softmix.
This sets up the "plumbing" in bridge_softmix to
be able to accommodate Asterisk asking as an SFU
(selective forwarding unit) for conferences.
The way this works is that whenever a channel enters or leaves a
conference, all participants in the bridge get sent a stream topology
change request. The topologies consist of the channels' original
topology, along with video destination streams corresponding to each
participants' source video streams. So for instance, if Alice, Bob, and
Carol are in the conference, and each supplies one video stream, then
the topologies for each would look like so:
Alice:
Audio,
Source video(Alice),
Destination Video(Bob),
Destination video (Carol)
Bob:
Audio,
Source video(Bob)
Destination Video(Alice),
Destination video (Carol)
Carol:
Audio,
Source video(Carol)
Destination Video(Alice),
Destination video (Bob)
This way, video that arrives from a source video stream can then be
copied out to the destination video streams on the other participants'
channels.
Once the bridge gets told that a topology on a channel has changed, the
bridge constructs a map in order to get the video frames routed to the
proper destination streams. This is done using the bridge channel's
stream_map.
This change is bare-bones with regards to SFU support. Some key features
are missing at this point:
* Stream limits. This commit makes no effort to limit the number of
streams on a specific channel. This means that if there were 50 video
callers in a conference, bridge_softmix will happily send out topology
change requests to every channel in the bridge, requesting 50+
streams.
* Configuration. The plumbing has been added to bridge_softmix, but
there has been nothing added as of yet to app_confbridge to enable SFU
video mode.
* Testing. Some functions included here have unit tests.
However, the functionality as a whole has only been verified by
hand-tracing the code.
* Selectivenss. For a "selective" forwarding unit, this does not
currently have any means of being selective.
* Features. Presumably, someone might wish to only receive video from
specific sources. There are no external-facing functions at the moment
that allow for users to select who they receive video from.
* Efficiency. The current scheme treats all video streams as being
unidirectional. We could be re-using a source video stream as a
desetnation, too. But to simplify things on this first round, I did it
this way.
Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
|
|
|
} else {
|
|
|
|
ast_bridge_channel_stream_map(bridge_channel);
|
2017-04-25 16:49:16 +00:00
|
|
|
}
|
|
|
|
break;
|
2013-07-24 15:38:18 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AST_FRAME_DTMF_BEGIN:
|
2013-08-15 14:20:59 +00:00
|
|
|
case AST_FRAME_DTMF_END:
|
2013-07-24 15:38:18 +00:00
|
|
|
frame = bridge_handle_dtmf(bridge_channel, frame);
|
|
|
|
if (!frame) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!bridge_channel->features->dtmf_passthrough) {
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_frame_free(frame);
|
2013-07-24 15:38:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Simply write the frame out to the bridge technology. */
|
|
|
|
bridge_channel_write_frame(bridge_channel, frame);
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_frame_free(frame);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2013-07-24 23:40:12 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Determine how long till the next timer interval.
|
|
|
|
* \since 12.0.0
|
|
|
|
*
|
|
|
|
* \param bridge_channel Channel to determine how long can wait.
|
|
|
|
*
|
|
|
|
* \retval ms Number of milliseconds to wait.
|
|
|
|
* \retval -1 to wait forever.
|
|
|
|
*/
|
|
|
|
static int bridge_channel_next_interval(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
struct ast_heap *interval_hooks = bridge_channel->features->interval_hooks;
|
|
|
|
struct ast_bridge_hook_timer *hook;
|
|
|
|
int ms;
|
|
|
|
|
|
|
|
ast_heap_wrlock(interval_hooks);
|
|
|
|
hook = ast_heap_peek(interval_hooks, 1);
|
|
|
|
if (hook) {
|
|
|
|
ms = ast_tvdiff_ms(hook->timer.trip_time, ast_tvnow());
|
|
|
|
if (ms < 0) {
|
|
|
|
/* Expire immediately. An interval hook is ready to run. */
|
|
|
|
ms = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* No hook so wait forever. */
|
|
|
|
ms = -1;
|
|
|
|
}
|
|
|
|
ast_heap_unlock(interval_hooks);
|
|
|
|
|
|
|
|
return ms;
|
|
|
|
}
|
|
|
|
|
2014-11-06 19:12:18 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Determine how long till the DTMF interdigit timeout.
|
|
|
|
* \since 12.8.0
|
|
|
|
*
|
|
|
|
* \param bridge_channel Channel to determine how long can wait.
|
|
|
|
*
|
|
|
|
* \retval ms Number of milliseconds to wait.
|
|
|
|
* \retval -1 to wait forever.
|
|
|
|
*/
|
|
|
|
static int bridge_channel_feature_timeout(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
int ms;
|
|
|
|
|
|
|
|
if (bridge_channel->dtmf_hook_state.collected[0]) {
|
|
|
|
ms = ast_tvdiff_ms(bridge_channel->dtmf_hook_state.interdigit_timeout,
|
|
|
|
ast_tvnow());
|
|
|
|
if (ms < 0) {
|
|
|
|
/* Expire immediately. */
|
|
|
|
ms = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Timer is not active so wait forever. */
|
|
|
|
ms = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ms;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Determine how long till a timeout.
|
|
|
|
* \since 12.8.0
|
|
|
|
*
|
|
|
|
* \param bridge_channel Channel to determine how long can wait.
|
|
|
|
*
|
|
|
|
* \retval ms Number of milliseconds to wait.
|
|
|
|
* \retval -1 to wait forever.
|
|
|
|
*/
|
|
|
|
static int bridge_channel_next_timeout(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
int ms_interval;
|
|
|
|
int ms;
|
|
|
|
|
|
|
|
ms_interval = bridge_channel_next_interval(bridge_channel);
|
|
|
|
ms = bridge_channel_feature_timeout(bridge_channel);
|
|
|
|
if (ms < 0 || (0 <= ms_interval && ms_interval < ms)) {
|
|
|
|
/* Interval hook timeout is next. */
|
|
|
|
ms = ms_interval;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ms;
|
|
|
|
}
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
/*!
|
|
|
|
* \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)
|
|
|
|
{
|
2013-07-24 23:40:12 +00:00
|
|
|
int ms;
|
2013-07-24 15:38:18 +00:00
|
|
|
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);
|
2013-07-25 02:20:23 +00:00
|
|
|
if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
|
2013-07-24 15:38:18 +00:00
|
|
|
} else if (bridge_channel->suspended) {
|
2013-08-15 18:20:52 +00:00
|
|
|
/* XXX ASTERISK-21271 the external party use of suspended will go away as will these references because this is the bridge channel thread */
|
2013-07-24 15:38:18 +00:00
|
|
|
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_bridge_channel_unlock(bridge_channel);
|
|
|
|
outfd = -1;
|
2014-11-06 19:12:18 +00:00
|
|
|
ms = bridge_channel_next_timeout(bridge_channel);
|
2013-07-24 15:38:18 +00:00
|
|
|
chan = ast_waitfor_nandfds(&bridge_channel->chan, 1,
|
|
|
|
&bridge_channel->alert_pipe[0], 1, NULL, &outfd, &ms);
|
2014-08-13 16:24:37 +00:00
|
|
|
if (ast_channel_unbridged(bridge_channel->chan)) {
|
|
|
|
ast_channel_set_unbridged(bridge_channel->chan, 0);
|
2013-07-24 15:38:18 +00:00
|
|
|
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);
|
2013-07-25 02:20:23 +00:00
|
|
|
bridge_channel->activity = BRIDGE_CHANNEL_THREAD_FRAME;
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_bridge_channel_unlock(bridge_channel);
|
|
|
|
if (!bridge_channel->suspended
|
2013-07-25 02:20:23 +00:00
|
|
|
&& bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
|
2013-07-24 15:38:18 +00:00
|
|
|
if (chan) {
|
|
|
|
bridge_handle_trip(bridge_channel);
|
2013-07-24 23:40:12 +00:00
|
|
|
} else if (ms == 0) {
|
2014-11-06 19:12:18 +00:00
|
|
|
/* An interdigit timeout or interval expired. */
|
|
|
|
bridge_channel_handle_feature_timeout(bridge_channel);
|
2013-07-24 23:40:12 +00:00
|
|
|
bridge_channel_handle_interval(bridge_channel);
|
2014-11-06 19:12:18 +00:00
|
|
|
} else if (-1 < outfd) {
|
|
|
|
/*
|
|
|
|
* Must do this after checking timeouts or may have
|
|
|
|
* an infinite loop due to deferring write queue
|
|
|
|
* actions while trying to match DTMF feature hooks.
|
|
|
|
*/
|
|
|
|
bridge_channel_handle_write(bridge_channel);
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
}
|
2013-07-25 02:20:23 +00:00
|
|
|
bridge_channel->activity = BRIDGE_CHANNEL_THREAD_IDLE;
|
2013-07-24 15:38:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
ast_bridge_channel_unlock(bridge_channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
2013-07-24 21:13:00 +00:00
|
|
|
* \brief Handle bridge channel join/leave event.
|
2013-07-24 15:38:18 +00:00
|
|
|
* \since 12.0.0
|
|
|
|
*
|
2013-07-24 21:13:00 +00:00
|
|
|
* \param bridge_channel Which channel is involved.
|
|
|
|
* \param type Specified join/leave event.
|
2013-07-24 15:38:18 +00:00
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
2013-07-24 21:13:00 +00:00
|
|
|
static void bridge_channel_event_join_leave(struct ast_bridge_channel *bridge_channel, enum ast_bridge_hook_type type)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
|
|
|
struct ast_bridge_features *features = bridge_channel->features;
|
|
|
|
struct ast_bridge_hook *hook;
|
|
|
|
struct ao2_iterator iter;
|
|
|
|
|
2013-07-24 21:13:00 +00:00
|
|
|
/* Run the specified hooks. */
|
|
|
|
iter = ao2_iterator_init(features->other_hooks, 0);
|
|
|
|
for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
|
|
|
|
if (hook->type == type) {
|
|
|
|
break;
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
if (hook) {
|
2013-07-24 21:13:00 +00:00
|
|
|
/* Found the first specified hook to run. */
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge_channel_suspend(bridge_channel);
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
|
|
|
do {
|
2013-07-24 21:13:00 +00:00
|
|
|
if (hook->type == type) {
|
2013-07-26 21:34:23 +00:00
|
|
|
hook->callback(bridge_channel, hook->hook_pvt);
|
2013-07-24 21:13:00 +00:00
|
|
|
ao2_unlink(features->other_hooks, hook);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-04-19 21:58:32 +00:00
|
|
|
int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
|
2013-07-24 15:38:18 +00:00
|
|
|
{
|
2013-07-26 16:34:56 +00:00
|
|
|
int res = 0;
|
2014-06-26 12:43:47 +00:00
|
|
|
struct ast_bridge_features *channel_features;
|
2015-01-22 19:30:12 +00:00
|
|
|
struct ast_channel *swap;
|
2013-08-23 18:33:36 +00:00
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_debug(1, "Bridge %s: %p(%s) is joining\n",
|
|
|
|
bridge_channel->bridge->uniqueid,
|
|
|
|
bridge_channel, ast_channel_name(bridge_channel->chan));
|
|
|
|
|
|
|
|
/*
|
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
|
|
|
* Directly locking the bridge is safe here because nobody else
|
|
|
|
* knows about this bridge_channel yet.
|
|
|
|
*/
|
|
|
|
ast_bridge_lock(bridge_channel->bridge);
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_channel_lock(bridge_channel->chan);
|
2014-08-20 22:52:44 +00:00
|
|
|
|
|
|
|
bridge_channel->read_format = ao2_bump(ast_channel_readformat(bridge_channel->chan));
|
|
|
|
bridge_channel->write_format = ao2_bump(ast_channel_writeformat(bridge_channel->chan));
|
|
|
|
|
2014-06-26 12:43:47 +00:00
|
|
|
/* Make sure we're still good to be put into a bridge */
|
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
|
|
|
if (ast_channel_internal_bridge(bridge_channel->chan)
|
|
|
|
|| ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_ZOMBIE)) {
|
|
|
|
ast_channel_unlock(bridge_channel->chan);
|
|
|
|
ast_bridge_unlock(bridge_channel->bridge);
|
|
|
|
ast_debug(1, "Bridge %s: %p(%s) failed to join Bridge\n",
|
|
|
|
bridge_channel->bridge->uniqueid,
|
|
|
|
bridge_channel,
|
|
|
|
ast_channel_name(bridge_channel->chan));
|
|
|
|
return -1;
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge);
|
2014-06-26 12:43:47 +00:00
|
|
|
|
|
|
|
/* Attach features requested by the channel */
|
|
|
|
channel_features = ast_channel_feature_hooks_get(bridge_channel->chan);
|
|
|
|
if (channel_features) {
|
|
|
|
ast_bridge_features_merge(bridge_channel->features, channel_features);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_channel_unlock(bridge_channel->chan);
|
|
|
|
|
|
|
|
/* Add the jitterbuffer if the channel requires it */
|
|
|
|
ast_jb_enable_for_channel(bridge_channel->chan);
|
|
|
|
|
|
|
|
if (!bridge_channel->bridge->callid) {
|
|
|
|
bridge_channel->bridge->callid = ast_read_threadstorage_callid();
|
|
|
|
}
|
|
|
|
|
2015-01-22 19:30:12 +00:00
|
|
|
/* Take the swap channel ref from the bridge_channel struct. */
|
|
|
|
swap = bridge_channel->swap;
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
if (bridge_channel_internal_push(bridge_channel)) {
|
2013-10-22 17:06:21 +00:00
|
|
|
int cause = bridge_channel->bridge->cause;
|
|
|
|
|
|
|
|
ast_bridge_unlock(bridge_channel->bridge);
|
|
|
|
ast_bridge_channel_kick(bridge_channel, cause);
|
|
|
|
ast_bridge_channel_lock_bridge(bridge_channel);
|
|
|
|
ast_bridge_features_remove(bridge_channel->features,
|
|
|
|
AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
|
|
|
bridge_channel_dissolve_check(bridge_channel);
|
2013-07-26 16:34:56 +00:00
|
|
|
res = -1;
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
2013-09-13 22:19:23 +00:00
|
|
|
bridge_reconfigured(bridge_channel->bridge, !bridge_channel->inhibit_colp);
|
2013-07-24 15:38:18 +00:00
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
|
2013-07-24 15:38:18 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2016-04-19 21:58:32 +00:00
|
|
|
bridge_channel_impart_signal(bridge_channel->chan);
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_bridge_unlock(bridge_channel->bridge);
|
2015-01-22 19:30:12 +00:00
|
|
|
|
|
|
|
/* Must release any swap ref after unlocking the bridge. */
|
|
|
|
ao2_t_cleanup(swap, "Bridge push with swap successful");
|
|
|
|
swap = NULL;
|
|
|
|
|
2013-07-24 21:13:00 +00:00
|
|
|
bridge_channel_event_join_leave(bridge_channel, AST_BRIDGE_HOOK_TYPE_JOIN);
|
2014-11-24 20:32:29 +00:00
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
while (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
|
2013-07-24 15:38:18 +00:00
|
|
|
/* Wait for something to do. */
|
|
|
|
bridge_channel_wait(bridge_channel);
|
|
|
|
}
|
2014-11-24 20:32:29 +00:00
|
|
|
|
|
|
|
/* Force a timeout on any accumulated DTMF hook digits. */
|
|
|
|
ast_bridge_channel_feature_digit(bridge_channel, 0);
|
|
|
|
|
2013-07-24 21:13:00 +00:00
|
|
|
bridge_channel_event_join_leave(bridge_channel, AST_BRIDGE_HOOK_TYPE_LEAVE);
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_bridge_channel_lock_bridge(bridge_channel);
|
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
bridge_channel_internal_pull(bridge_channel);
|
2013-08-23 18:33:36 +00:00
|
|
|
bridge_channel_settle_owed_events(bridge_channel->bridge, bridge_channel);
|
2013-07-24 15:38:18 +00:00
|
|
|
bridge_reconfigured(bridge_channel->bridge, 1);
|
|
|
|
|
2016-11-04 20:37:57 +00:00
|
|
|
/* Remove ourselves if we are the video source */
|
|
|
|
ast_bridge_remove_video_src(bridge_channel->bridge, bridge_channel->chan);
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
ast_bridge_unlock(bridge_channel->bridge);
|
|
|
|
|
2015-01-22 19:30:12 +00:00
|
|
|
/* Must release any swap ref after unlocking the bridge. */
|
|
|
|
ao2_t_cleanup(swap, "Bridge push with swap failed or exited immediately");
|
|
|
|
|
2013-08-23 18:33:36 +00:00
|
|
|
/* Complete any active hold before exiting the bridge. */
|
|
|
|
if (ast_channel_hold_state(bridge_channel->chan) == AST_CONTROL_HOLD) {
|
|
|
|
ast_debug(1, "Channel %s simulating UNHOLD for bridge end.\n",
|
|
|
|
ast_channel_name(bridge_channel->chan));
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_UNHOLD);
|
|
|
|
}
|
2013-07-24 15:38:18 +00:00
|
|
|
|
|
|
|
/* 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");
|
|
|
|
}
|
|
|
|
|
2016-02-22 19:54:47 +00:00
|
|
|
/* Complete any T.38 session before exiting the bridge. */
|
|
|
|
if (ast_channel_is_t38_active(bridge_channel->chan)) {
|
|
|
|
struct ast_control_t38_parameters t38_parameters = {
|
|
|
|
.request_response = AST_T38_TERMINATED,
|
|
|
|
};
|
|
|
|
|
|
|
|
ast_debug(1, "Channel %s simulating T.38 terminate for bridge end.\n",
|
|
|
|
ast_channel_name(bridge_channel->chan));
|
|
|
|
ast_indicate_data(bridge_channel->chan, AST_CONTROL_T38_PARAMETERS,
|
|
|
|
&t38_parameters, sizeof(t38_parameters));
|
|
|
|
}
|
|
|
|
|
2013-08-23 18:33:36 +00:00
|
|
|
/* Indicate a source change since this channel is leaving the bridge system. */
|
|
|
|
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE);
|
|
|
|
|
2013-07-24 15:38:18 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
2013-07-26 16:34:56 +00:00
|
|
|
|
|
|
|
return res;
|
2013-07-24 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
int bridge_channel_internal_queue_blind_transfer(struct ast_channel *transferee,
|
2013-07-24 19:24:09 +00:00
|
|
|
const char *exten, const char *context,
|
|
|
|
transfer_channel_cb new_channel_cb, void *user_data)
|
|
|
|
{
|
|
|
|
RAII_VAR(struct ast_bridge_channel *, transferee_bridge_channel, NULL, ao2_cleanup);
|
|
|
|
struct blind_transfer_data blind_data;
|
|
|
|
|
|
|
|
ast_channel_lock(transferee);
|
|
|
|
transferee_bridge_channel = ast_channel_get_bridge_channel(transferee);
|
|
|
|
ast_channel_unlock(transferee);
|
|
|
|
|
|
|
|
if (!transferee_bridge_channel) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_channel_cb) {
|
|
|
|
new_channel_cb(transferee, user_data, AST_BRIDGE_TRANSFER_SINGLE_PARTY);
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_copy_string(blind_data.exten, exten, sizeof(blind_data.exten));
|
|
|
|
ast_copy_string(blind_data.context, context, sizeof(blind_data.context));
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
return bridge_channel_queue_action_data(transferee_bridge_channel,
|
2013-07-24 19:24:09 +00:00
|
|
|
BRIDGE_CHANNEL_ACTION_BLIND_TRANSFER, &blind_data, sizeof(blind_data));
|
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
int bridge_channel_internal_queue_attended_transfer(struct ast_channel *transferee,
|
2013-07-24 19:24:09 +00:00
|
|
|
struct ast_channel *unbridged_chan)
|
|
|
|
{
|
|
|
|
RAII_VAR(struct ast_bridge_channel *, transferee_bridge_channel, NULL, ao2_cleanup);
|
|
|
|
char unbridged_chan_name[AST_CHANNEL_NAME];
|
|
|
|
|
|
|
|
ast_channel_lock(transferee);
|
|
|
|
transferee_bridge_channel = ast_channel_get_bridge_channel(transferee);
|
|
|
|
ast_channel_unlock(transferee);
|
|
|
|
|
|
|
|
if (!transferee_bridge_channel) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_copy_string(unbridged_chan_name, ast_channel_name(unbridged_chan),
|
|
|
|
sizeof(unbridged_chan_name));
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
return bridge_channel_queue_action_data(transferee_bridge_channel,
|
2013-07-24 19:24:09 +00:00
|
|
|
BRIDGE_CHANNEL_ACTION_ATTENDED_TRANSFER, unbridged_chan_name,
|
|
|
|
sizeof(unbridged_chan_name));
|
2013-07-24 16:01:20 +00:00
|
|
|
}
|
2013-07-25 02:20:23 +00:00
|
|
|
|
|
|
|
int bridge_channel_internal_allows_optimization(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
return bridge_channel->in_bridge
|
|
|
|
&& AST_LIST_EMPTY(&bridge_channel->wr_queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Destroy elements of the bridge channel structure and the bridge channel structure itself */
|
|
|
|
static void bridge_channel_destroy(void *obj)
|
|
|
|
{
|
|
|
|
struct ast_bridge_channel *bridge_channel = obj;
|
|
|
|
struct ast_frame *fr;
|
|
|
|
|
|
|
|
if (bridge_channel->callid) {
|
2015-03-13 01:12:35 +00:00
|
|
|
bridge_channel->callid = 0;
|
2013-07-25 02:20:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bridge_channel->bridge) {
|
|
|
|
ao2_ref(bridge_channel->bridge, -1);
|
|
|
|
bridge_channel->bridge = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Flush any unhandled wr_queue frames. */
|
|
|
|
while ((fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list))) {
|
2014-03-17 17:22:12 +00:00
|
|
|
bridge_frame_free(fr);
|
2013-07-25 02:20:23 +00:00
|
|
|
}
|
2017-04-18 00:06:10 +00:00
|
|
|
ast_alertpipe_close(bridge_channel->alert_pipe);
|
2013-07-25 02:20:23 +00:00
|
|
|
|
2017-06-08 19:38:51 +00:00
|
|
|
/* Flush any unhandled deferred_queue frames. */
|
|
|
|
while ((fr = AST_LIST_REMOVE_HEAD(&bridge_channel->deferred_queue, frame_list))) {
|
|
|
|
ast_frfree(fr);
|
|
|
|
}
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
ast_cond_destroy(&bridge_channel->cond);
|
media formats: re-architect handling of media for performance improvements
In the old times media formats were represented using a bit field. This was
fast but had a few limitations.
1. Asterisk was limited in how many formats it could handle.
2. Formats, being a bit field, could not include any attribute information.
A format was strictly its type, e.g., "this is ulaw".
This was changed in Asterisk 10 (see
https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal for
notes on that work) which led to the creation of the ast_format structure.
This structure allowed Asterisk to handle attributes and bundle information
with a format.
Additionally, ast_format_cap was created to act as a container for multiple
formats that, together, formed the capability of some entity. Another
mechanism was added to allow logic to be registered which performed format
attribute negotiation. Everywhere throughout the codebase Asterisk was
changed to use this strategy.
Unfortunately, in software, there is no free lunch. These new capabilities
came at a cost.
Performance analysis and profiling showed that we spend an inordinate
amount of time comparing, copying, and generally manipulating formats and
their related structures. Basic prototyping has shown that a reasonably
large performance improvement could be made in this area. This patch is the
result of that project, which overhauled the media format architecture
and its usage in Asterisk to improve performance.
Generally, the new philosophy for handling formats is as follows:
* The ast_format structure is reference counted. This removed a large amount
of the memory allocations and copying that was done in prior versions.
* In order to prevent race conditions while keeping things performant, the
ast_format structure is immutable by convention and lock-free. Violate this
tenet at your peril!
* Because formats are reference counted, codecs are also reference counted.
The Asterisk core generally provides built-in codecs and caches the
ast_format structures created to represent them. Generally, to prevent
inordinate amounts of module reference bumping, codecs and formats can be
added at run-time but cannot be removed.
* All compatibility with the bit field representation of codecs/formats has
been moved to a compatibility API. The primary user of this representation
is chan_iax2, which must continue to maintain its bit-field usage of formats
for interoperability concerns.
* When a format is negotiated with attributes, or when a format cannot be
represented by one of the cached formats, a new format object is created or
cloned from an existing format. That format may have the same codec
underlying it, but is a different format than a version of the format with
different attributes or without attributes.
* While formats are reference counted objects, the reference count maintained
on the format should be manipulated with care. Formats are generally cached
and will persist for the lifetime of Asterisk and do not explicitly need
to have their lifetime modified. An exception to this is when the user of a
format does not know where the format came from *and* the user may outlive
the provider of the format. This occurs, for example, when a format is read
from a channel: the channel may have a format with attributes (hence,
non-cached) and the user of the format may last longer than the channel (if
the reference to the channel is released prior to the format's reference).
For more information on this work, see the API design notes:
https://wiki.asterisk.org/wiki/display/AST/Media+Format+Rewrite
Finally, this work was the culmination of a large number of developer's
efforts. Extra thanks goes to Corey Farrell, who took on a large amount of the
work in the Asterisk core, chan_sip, and was an invaluable resource in peer
reviews throughout this project.
There were a substantial number of patches contributed during this work; the
following issues/patch names simply reflect some of the work (and will cause
the release scripts to give attribution to the individuals who work on them).
Reviews:
https://reviewboard.asterisk.org/r/3814
https://reviewboard.asterisk.org/r/3808
https://reviewboard.asterisk.org/r/3805
https://reviewboard.asterisk.org/r/3803
https://reviewboard.asterisk.org/r/3801
https://reviewboard.asterisk.org/r/3798
https://reviewboard.asterisk.org/r/3800
https://reviewboard.asterisk.org/r/3794
https://reviewboard.asterisk.org/r/3793
https://reviewboard.asterisk.org/r/3792
https://reviewboard.asterisk.org/r/3791
https://reviewboard.asterisk.org/r/3790
https://reviewboard.asterisk.org/r/3789
https://reviewboard.asterisk.org/r/3788
https://reviewboard.asterisk.org/r/3787
https://reviewboard.asterisk.org/r/3786
https://reviewboard.asterisk.org/r/3784
https://reviewboard.asterisk.org/r/3783
https://reviewboard.asterisk.org/r/3778
https://reviewboard.asterisk.org/r/3774
https://reviewboard.asterisk.org/r/3775
https://reviewboard.asterisk.org/r/3772
https://reviewboard.asterisk.org/r/3761
https://reviewboard.asterisk.org/r/3754
https://reviewboard.asterisk.org/r/3753
https://reviewboard.asterisk.org/r/3751
https://reviewboard.asterisk.org/r/3750
https://reviewboard.asterisk.org/r/3748
https://reviewboard.asterisk.org/r/3747
https://reviewboard.asterisk.org/r/3746
https://reviewboard.asterisk.org/r/3742
https://reviewboard.asterisk.org/r/3740
https://reviewboard.asterisk.org/r/3739
https://reviewboard.asterisk.org/r/3738
https://reviewboard.asterisk.org/r/3737
https://reviewboard.asterisk.org/r/3736
https://reviewboard.asterisk.org/r/3734
https://reviewboard.asterisk.org/r/3722
https://reviewboard.asterisk.org/r/3713
https://reviewboard.asterisk.org/r/3703
https://reviewboard.asterisk.org/r/3689
https://reviewboard.asterisk.org/r/3687
https://reviewboard.asterisk.org/r/3674
https://reviewboard.asterisk.org/r/3671
https://reviewboard.asterisk.org/r/3667
https://reviewboard.asterisk.org/r/3665
https://reviewboard.asterisk.org/r/3625
https://reviewboard.asterisk.org/r/3602
https://reviewboard.asterisk.org/r/3519
https://reviewboard.asterisk.org/r/3518
https://reviewboard.asterisk.org/r/3516
https://reviewboard.asterisk.org/r/3515
https://reviewboard.asterisk.org/r/3512
https://reviewboard.asterisk.org/r/3506
https://reviewboard.asterisk.org/r/3413
https://reviewboard.asterisk.org/r/3410
https://reviewboard.asterisk.org/r/3387
https://reviewboard.asterisk.org/r/3388
https://reviewboard.asterisk.org/r/3389
https://reviewboard.asterisk.org/r/3390
https://reviewboard.asterisk.org/r/3321
https://reviewboard.asterisk.org/r/3320
https://reviewboard.asterisk.org/r/3319
https://reviewboard.asterisk.org/r/3318
https://reviewboard.asterisk.org/r/3266
https://reviewboard.asterisk.org/r/3265
https://reviewboard.asterisk.org/r/3234
https://reviewboard.asterisk.org/r/3178
ASTERISK-23114 #close
Reported by: mjordan
media_formats_translation_core.diff uploaded by kharwell (License 6464)
rb3506.diff uploaded by mjordan (License 6283)
media_format_app_file.diff uploaded by kharwell (License 6464)
misc-2.diff uploaded by file (License 5000)
chan_mild-3.diff uploaded by file (License 5000)
chan_obscure.diff uploaded by file (License 5000)
jingle.diff uploaded by file (License 5000)
funcs.diff uploaded by file (License 5000)
formats.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
bridges.diff uploaded by file (License 5000)
mf-codecs-2.diff uploaded by file (License 5000)
mf-app_fax.diff uploaded by file (License 5000)
mf-apps-3.diff uploaded by file (License 5000)
media-formats-3.diff uploaded by file (License 5000)
ASTERISK-23715
rb3713.patch uploaded by coreyfarrell (License 5909)
rb3689.patch uploaded by mjordan (License 6283)
ASTERISK-23957
rb3722.patch uploaded by mjordan (License 6283)
mf-attributes-3.diff uploaded by file (License 5000)
ASTERISK-23958
Tested by: jrose
rb3822.patch uploaded by coreyfarrell (License 5909)
rb3800.patch uploaded by jrose (License 6182)
chan_sip.diff uploaded by mjordan (License 6283)
rb3747.patch uploaded by jrose (License 6182)
ASTERISK-23959 #close
Tested by: sgriepentrog, mjordan, coreyfarrell
sip_cleanup.diff uploaded by opticron (License 6273)
chan_sip_caps.diff uploaded by mjordan (License 6283)
rb3751.patch uploaded by coreyfarrell (License 5909)
chan_sip-3.diff uploaded by file (License 5000)
ASTERISK-23960 #close
Tested by: opticron
direct_media.diff uploaded by opticron (License 6273)
pjsip-direct-media.diff uploaded by file (License 5000)
format_cap_remove.diff uploaded by opticron (License 6273)
media_format_fixes.diff uploaded by opticron (License 6273)
chan_pjsip-2.diff uploaded by file (License 5000)
ASTERISK-23966 #close
Tested by: rmudgett
rb3803.patch uploaded by rmudgetti (License 5621)
chan_dahdi.diff uploaded by file (License 5000)
ASTERISK-24064 #close
Tested by: coreyfarrell, mjordan, opticron, file, rmudgett, sgriepentrog, jrose
rb3814.patch uploaded by rmudgett (License 5621)
moh_cleanup.diff uploaded by opticron (License 6273)
bridge_leak.diff uploaded by opticron (License 6273)
translate.diff uploaded by file (License 5000)
rb3795.patch uploaded by rmudgett (License 5621)
tls_fix.diff uploaded by mjordan (License 6283)
fax-mf-fix-2.diff uploaded by file (License 5000)
rtp_transfer_stuff uploaded by mjordan (License 6283)
rb3787.patch uploaded by rmudgett (License 5621)
media-formats-explicit-translate-format-3.diff uploaded by file (License 5000)
format_cache_case_fix.diff uploaded by opticron (License 6273)
rb3774.patch uploaded by rmudgett (License 5621)
rb3775.patch uploaded by rmudgett (License 5621)
rtp_engine_fix.diff uploaded by opticron (License 6273)
rtp_crash_fix.diff uploaded by opticron (License 6273)
rb3753.patch uploaded by mjordan (License 6283)
rb3750.patch uploaded by mjordan (License 6283)
rb3748.patch uploaded by rmudgett (License 5621)
media_format_fixes.diff uploaded by opticron (License 6273)
rb3740.patch uploaded by mjordan (License 6283)
rb3739.patch uploaded by mjordan (License 6283)
rb3734.patch uploaded by mjordan (License 6283)
rb3689.patch uploaded by mjordan (License 6283)
rb3674.patch uploaded by coreyfarrell (License 5909)
rb3671.patch uploaded by coreyfarrell (License 5909)
rb3667.patch uploaded by coreyfarrell (License 5909)
rb3665.patch uploaded by mjordan (License 6283)
rb3625.patch uploaded by coreyfarrell (License 5909)
rb3602.patch uploaded by coreyfarrell (License 5909)
format_compatibility-2.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419044 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-20 22:06:33 +00:00
|
|
|
|
|
|
|
ao2_cleanup(bridge_channel->write_format);
|
|
|
|
ao2_cleanup(bridge_channel->read_format);
|
2017-04-25 16:49:16 +00:00
|
|
|
|
|
|
|
AST_VECTOR_FREE(&bridge_channel->stream_map.to_bridge);
|
|
|
|
AST_VECTOR_FREE(&bridge_channel->stream_map.to_channel);
|
2013-07-25 02:20:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *bridge)
|
|
|
|
{
|
|
|
|
struct ast_bridge_channel *bridge_channel;
|
|
|
|
|
|
|
|
bridge_channel = ao2_alloc(sizeof(struct ast_bridge_channel), bridge_channel_destroy);
|
|
|
|
if (!bridge_channel) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ast_cond_init(&bridge_channel->cond, NULL);
|
2017-04-18 00:06:10 +00:00
|
|
|
if (ast_alertpipe_init(bridge_channel->alert_pipe)) {
|
2013-07-25 02:20:23 +00:00
|
|
|
ao2_ref(bridge_channel, -1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (bridge) {
|
|
|
|
bridge_channel->bridge = bridge;
|
|
|
|
ao2_ref(bridge_channel->bridge, +1);
|
|
|
|
}
|
|
|
|
|
2017-04-25 16:49:16 +00:00
|
|
|
/* The stream_map is initialized later - see ast_bridge_channel_stream_map */
|
|
|
|
|
2013-07-25 02:20:23 +00:00
|
|
|
return bridge_channel;
|
|
|
|
}
|
2017-04-25 16:49:16 +00:00
|
|
|
|
|
|
|
void ast_bridge_channel_stream_map(struct ast_bridge_channel *bridge_channel)
|
|
|
|
{
|
|
|
|
ast_stream_topology_map(ast_channel_get_stream_topology(bridge_channel->chan),
|
|
|
|
&bridge_channel->bridge->media_types, &bridge_channel->stream_map.to_bridge,
|
|
|
|
&bridge_channel->stream_map.to_channel);
|
|
|
|
}
|