|
|
|
|
@ -6655,6 +6655,7 @@ void ast_channel_set_linkgroup(struct ast_channel *chan, struct ast_channel *pee
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0 //BUGBUG setting up peeraccount needs to be removed.
|
|
|
|
|
/* copy accountcode and peeraccount across during a link */
|
|
|
|
|
static void ast_set_owners_and_peers(struct ast_channel *chan1,
|
|
|
|
|
struct ast_channel *chan2)
|
|
|
|
|
@ -6690,6 +6691,7 @@ static void ast_set_owners_and_peers(struct ast_channel *chan1,
|
|
|
|
|
ast_channel_peeraccount_set(chan1, ast_channel_accountcode(chan2));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif //BUGBUG
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
@ -7322,251 +7324,6 @@ struct ast_channel *ast_bridged_channel(struct ast_channel *chan)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void bridge_playfile(struct ast_channel *chan, struct ast_channel *peer, const char *sound, int remain)
|
|
|
|
|
{
|
|
|
|
|
int check;
|
|
|
|
|
|
|
|
|
|
check = ast_autoservice_start(peer);
|
|
|
|
|
if (check) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!strcmp(sound, "timeleft")) { /* Queue support */
|
|
|
|
|
int min = 0;
|
|
|
|
|
int sec = 0;
|
|
|
|
|
|
|
|
|
|
if (remain > 0) {
|
|
|
|
|
if (remain / 60 > 1) {
|
|
|
|
|
min = remain / 60;
|
|
|
|
|
sec = remain % 60;
|
|
|
|
|
} else {
|
|
|
|
|
sec = remain;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_stream_and_wait(chan, "vm-youhave", "");
|
|
|
|
|
if (min) {
|
|
|
|
|
ast_say_number(chan, min, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
|
|
|
|
|
ast_stream_and_wait(chan, "queue-minutes", "");
|
|
|
|
|
}
|
|
|
|
|
if (sec) {
|
|
|
|
|
ast_say_number(chan, sec, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
|
|
|
|
|
ast_stream_and_wait(chan, "queue-seconds", "");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ast_stream_and_wait(chan, sound, "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_autoservice_stop(peer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct ast_channel *c1,
|
|
|
|
|
struct ast_bridge_config *config, struct ast_frame **fo,
|
|
|
|
|
struct ast_channel **rc)
|
|
|
|
|
{
|
|
|
|
|
/* Copy voice back and forth between the two channels. */
|
|
|
|
|
struct ast_channel *cs[3];
|
|
|
|
|
struct ast_frame *f;
|
|
|
|
|
enum ast_bridge_result res = AST_BRIDGE_COMPLETE;
|
|
|
|
|
struct ast_format_cap *o0nativeformats;
|
|
|
|
|
struct ast_format_cap *o1nativeformats;
|
|
|
|
|
int watch_c0_dtmf;
|
|
|
|
|
int watch_c1_dtmf;
|
|
|
|
|
void *pvt0, *pvt1;
|
|
|
|
|
/* Indicates whether a frame was queued into a jitterbuffer */
|
|
|
|
|
int frame_put_in_jb = 0;
|
|
|
|
|
int jb_in_use;
|
|
|
|
|
int to;
|
|
|
|
|
|
|
|
|
|
o0nativeformats = ast_format_cap_dup(ast_channel_nativeformats(c0));
|
|
|
|
|
o1nativeformats = ast_format_cap_dup(ast_channel_nativeformats(c1));
|
|
|
|
|
|
|
|
|
|
if (!o0nativeformats || !o1nativeformats) {
|
|
|
|
|
ast_format_cap_destroy(o0nativeformats); /* NULL safe */
|
|
|
|
|
ast_format_cap_destroy(o1nativeformats); /* NULL safe */
|
|
|
|
|
return AST_BRIDGE_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cs[0] = c0;
|
|
|
|
|
cs[1] = c1;
|
|
|
|
|
pvt0 = ast_channel_tech_pvt(c0);
|
|
|
|
|
pvt1 = ast_channel_tech_pvt(c1);
|
|
|
|
|
watch_c0_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_0;
|
|
|
|
|
watch_c1_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_1;
|
|
|
|
|
|
|
|
|
|
/* Check the need of a jitterbuffer for each channel */
|
|
|
|
|
jb_in_use = ast_jb_do_usecheck(c0, c1);
|
|
|
|
|
if (jb_in_use)
|
|
|
|
|
ast_jb_empty_and_reset(c0, c1);
|
|
|
|
|
|
|
|
|
|
ast_poll_channel_add(c0, c1);
|
|
|
|
|
|
|
|
|
|
if (config->feature_timer > 0 && ast_tvzero(config->nexteventts)) {
|
|
|
|
|
/* nexteventts is not set when the bridge is not scheduled to
|
|
|
|
|
* break, so calculate when the bridge should possibly break
|
|
|
|
|
* if a partial feature match timed out */
|
|
|
|
|
config->nexteventts = ast_tvadd(ast_tvnow(), ast_samp2tv(config->feature_timer, 1000));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
struct ast_channel *who, *other;
|
|
|
|
|
|
|
|
|
|
if ((ast_channel_tech_pvt(c0) != pvt0) || (ast_channel_tech_pvt(c1) != pvt1) ||
|
|
|
|
|
(!ast_format_cap_identical(o0nativeformats, ast_channel_nativeformats(c0))) ||
|
|
|
|
|
(!ast_format_cap_identical(o1nativeformats, ast_channel_nativeformats(c1)))) {
|
|
|
|
|
/* Check for Masquerade, codec changes, etc */
|
|
|
|
|
res = AST_BRIDGE_RETRY;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!ast_tvzero(config->nexteventts)) {
|
|
|
|
|
to = ast_tvdiff_ms(config->nexteventts, ast_tvnow());
|
|
|
|
|
if (to <= 0) {
|
|
|
|
|
if (config->feature_timer) {
|
|
|
|
|
/* feature timer expired - make sure we do not play warning */
|
|
|
|
|
ast_clear_flag(config, AST_FEATURE_WARNING_ACTIVE);
|
|
|
|
|
/* Indicate a feature timeout. */
|
|
|
|
|
res = AST_BRIDGE_RETRY;
|
|
|
|
|
} else if (config->timelimit
|
|
|
|
|
&& !ast_test_flag(config, AST_FEATURE_WARNING_ACTIVE)) {
|
|
|
|
|
/* generic bridge ending to play warning */
|
|
|
|
|
ast_set_flag(config, AST_FEATURE_WARNING_ACTIVE);
|
|
|
|
|
res = AST_BRIDGE_RETRY;
|
|
|
|
|
} else {
|
|
|
|
|
res = AST_BRIDGE_COMPLETE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
to = -1;
|
|
|
|
|
}
|
|
|
|
|
/* Calculate the appropriate max sleep interval - in general, this is the time,
|
|
|
|
|
left to the closest jb delivery moment */
|
|
|
|
|
if (jb_in_use)
|
|
|
|
|
to = ast_jb_get_when_to_wakeup(c0, c1, to);
|
|
|
|
|
who = ast_waitfor_n(cs, 2, &to);
|
|
|
|
|
if (!who) {
|
|
|
|
|
/* No frame received within the specified timeout - check if we have to deliver now */
|
|
|
|
|
if (jb_in_use)
|
|
|
|
|
ast_jb_get_and_deliver(c0, c1);
|
|
|
|
|
if ((ast_channel_softhangup_internal_flag(c0) | ast_channel_softhangup_internal_flag(c1)) & AST_SOFTHANGUP_UNBRIDGE) {/* Bit operators are intentional. */
|
|
|
|
|
if (ast_channel_softhangup_internal_flag(c0) & AST_SOFTHANGUP_UNBRIDGE) {
|
|
|
|
|
ast_channel_clear_softhangup(c0, AST_SOFTHANGUP_UNBRIDGE);
|
|
|
|
|
}
|
|
|
|
|
if (ast_channel_softhangup_internal_flag(c1) & AST_SOFTHANGUP_UNBRIDGE) {
|
|
|
|
|
ast_channel_clear_softhangup(c1, AST_SOFTHANGUP_UNBRIDGE);
|
|
|
|
|
}
|
|
|
|
|
ast_channel_lock_both(c0, c1);
|
|
|
|
|
ast_channel_internal_bridged_channel_set(c0, c1);
|
|
|
|
|
ast_channel_internal_bridged_channel_set(c1, c0);
|
|
|
|
|
ast_channel_unlock(c0);
|
|
|
|
|
ast_channel_unlock(c1);
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
f = ast_read(who);
|
|
|
|
|
if (!f) {
|
|
|
|
|
*fo = NULL;
|
|
|
|
|
*rc = who;
|
|
|
|
|
ast_debug(1, "Didn't get a frame from channel: %s\n", ast_channel_name(who));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
other = (who == c0) ? c1 : c0; /* the 'other' channel */
|
|
|
|
|
/* Try add the frame info the who's bridged channel jitterbuff */
|
|
|
|
|
if (jb_in_use)
|
|
|
|
|
frame_put_in_jb = !ast_jb_put(other, f);
|
|
|
|
|
|
|
|
|
|
if ((f->frametype == AST_FRAME_CONTROL) && !(config->flags & AST_BRIDGE_IGNORE_SIGS)) {
|
|
|
|
|
int bridge_exit = 0;
|
|
|
|
|
|
|
|
|
|
switch (f->subclass.integer) {
|
|
|
|
|
case AST_CONTROL_PVT_CAUSE_CODE:
|
|
|
|
|
case AST_CONTROL_AOC:
|
|
|
|
|
case AST_CONTROL_MCID:
|
|
|
|
|
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
|
|
|
|
|
break;
|
|
|
|
|
case AST_CONTROL_REDIRECTING:
|
|
|
|
|
if (ast_channel_redirecting_sub(who, other, f, 1) &&
|
|
|
|
|
ast_channel_redirecting_macro(who, other, f, other == c0, 1)) {
|
|
|
|
|
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AST_CONTROL_CONNECTED_LINE:
|
|
|
|
|
if (ast_channel_connected_line_sub(who, other, f, 1) &&
|
|
|
|
|
ast_channel_connected_line_macro(who, other, f, other == c0, 1)) {
|
|
|
|
|
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AST_CONTROL_HOLD:
|
|
|
|
|
case AST_CONTROL_UNHOLD:
|
|
|
|
|
case AST_CONTROL_VIDUPDATE:
|
|
|
|
|
case AST_CONTROL_SRCUPDATE:
|
|
|
|
|
case AST_CONTROL_SRCCHANGE:
|
|
|
|
|
case AST_CONTROL_T38_PARAMETERS:
|
|
|
|
|
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
|
|
|
|
|
if (jb_in_use) {
|
|
|
|
|
ast_jb_empty_and_reset(c0, c1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
*fo = f;
|
|
|
|
|
*rc = who;
|
|
|
|
|
bridge_exit = 1;
|
|
|
|
|
ast_debug(1, "Got a FRAME_CONTROL (%d) frame on channel %s\n", f->subclass.integer, ast_channel_name(who));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (bridge_exit)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if ((f->frametype == AST_FRAME_VOICE) ||
|
|
|
|
|
(f->frametype == AST_FRAME_DTMF_BEGIN) ||
|
|
|
|
|
(f->frametype == AST_FRAME_DTMF) ||
|
|
|
|
|
(f->frametype == AST_FRAME_VIDEO) ||
|
|
|
|
|
(f->frametype == AST_FRAME_IMAGE) ||
|
|
|
|
|
(f->frametype == AST_FRAME_HTML) ||
|
|
|
|
|
(f->frametype == AST_FRAME_MODEM) ||
|
|
|
|
|
(f->frametype == AST_FRAME_TEXT)) {
|
|
|
|
|
/* monitored dtmf causes exit from bridge */
|
|
|
|
|
int monitored_source = (who == c0) ? watch_c0_dtmf : watch_c1_dtmf;
|
|
|
|
|
|
|
|
|
|
if (monitored_source &&
|
|
|
|
|
(f->frametype == AST_FRAME_DTMF_END ||
|
|
|
|
|
f->frametype == AST_FRAME_DTMF_BEGIN)) {
|
|
|
|
|
*fo = f;
|
|
|
|
|
*rc = who;
|
|
|
|
|
ast_debug(1, "Got DTMF %s on channel (%s)\n",
|
|
|
|
|
f->frametype == AST_FRAME_DTMF_END ? "end" : "begin",
|
|
|
|
|
ast_channel_name(who));
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* Write immediately frames, not passed through jb */
|
|
|
|
|
if (!frame_put_in_jb)
|
|
|
|
|
ast_write(other, f);
|
|
|
|
|
|
|
|
|
|
/* Check if we have to deliver now */
|
|
|
|
|
if (jb_in_use)
|
|
|
|
|
ast_jb_get_and_deliver(c0, c1);
|
|
|
|
|
}
|
|
|
|
|
/* XXX do we want to pass on also frames not matched above ? */
|
|
|
|
|
ast_frfree(f);
|
|
|
|
|
|
|
|
|
|
#ifndef HAVE_EPOLL
|
|
|
|
|
/* Swap who gets priority */
|
|
|
|
|
cs[2] = cs[0];
|
|
|
|
|
cs[0] = cs[1];
|
|
|
|
|
cs[1] = cs[2];
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_poll_channel_del(c0, c1);
|
|
|
|
|
|
|
|
|
|
ast_format_cap_destroy(o0nativeformats);
|
|
|
|
|
ast_format_cap_destroy(o1nativeformats);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Bridge two channels together (early) */
|
|
|
|
|
int ast_channel_early_bridge(struct ast_channel *c0, struct ast_channel *c1)
|
|
|
|
|
{
|
|
|
|
|
@ -7577,382 +7334,6 @@ int ast_channel_early_bridge(struct ast_channel *c0, struct ast_channel *c1)
|
|
|
|
|
return ast_channel_tech(c0)->early_bridge(c0, c1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Send manager event for bridge link and unlink events.
|
|
|
|
|
* \param onoff Link/Unlinked
|
|
|
|
|
* \param type 1 for core, 2 for native
|
|
|
|
|
* \param c0 first channel in bridge
|
|
|
|
|
* \param c1 second channel in bridge
|
|
|
|
|
*/
|
|
|
|
|
static void manager_bridge_event(int onoff, int type, struct ast_channel *c0, struct ast_channel *c1)
|
|
|
|
|
{
|
|
|
|
|
struct ast_channel *chans[2] = { c0, c1 };
|
|
|
|
|
/*** DOCUMENTATION
|
|
|
|
|
<managerEventInstance>
|
|
|
|
|
<synopsis>Raised when a bridge changes between two channels.</synopsis>
|
|
|
|
|
<syntax>
|
|
|
|
|
<parameter name="Bridgestate">
|
|
|
|
|
<enumlist>
|
|
|
|
|
<enum name="Link"/>
|
|
|
|
|
<enum name="Unlink"/>
|
|
|
|
|
</enumlist>
|
|
|
|
|
</parameter>
|
|
|
|
|
<parameter name="Bridgetype">
|
|
|
|
|
<enumlist>
|
|
|
|
|
<enum name="core"/>
|
|
|
|
|
<enum name="native"/>
|
|
|
|
|
</enumlist>
|
|
|
|
|
</parameter>
|
|
|
|
|
</syntax>
|
|
|
|
|
</managerEventInstance>
|
|
|
|
|
***/
|
|
|
|
|
ast_manager_event_multichan(EVENT_FLAG_CALL, "Bridge", 2, chans,
|
|
|
|
|
"Bridgestate: %s\r\n"
|
|
|
|
|
"Bridgetype: %s\r\n"
|
|
|
|
|
"Channel1: %s\r\n"
|
|
|
|
|
"Channel2: %s\r\n"
|
|
|
|
|
"Uniqueid1: %s\r\n"
|
|
|
|
|
"Uniqueid2: %s\r\n"
|
|
|
|
|
"CallerID1: %s\r\n"
|
|
|
|
|
"CallerID2: %s\r\n",
|
|
|
|
|
onoff ? "Link" : "Unlink",
|
|
|
|
|
type == 1 ? "core" : "native",
|
|
|
|
|
ast_channel_name(c0), ast_channel_name(c1),
|
|
|
|
|
ast_channel_uniqueid(c0), ast_channel_uniqueid(c1),
|
|
|
|
|
S_COR(ast_channel_caller(c0)->id.number.valid, ast_channel_caller(c0)->id.number.str, ""),
|
|
|
|
|
S_COR(ast_channel_caller(c1)->id.number.valid, ast_channel_caller(c1)->id.number.str, ""));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void update_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid)
|
|
|
|
|
{
|
|
|
|
|
if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BRIDGEPEER"))) {
|
|
|
|
|
pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name);
|
|
|
|
|
}
|
|
|
|
|
if (pvtid) {
|
|
|
|
|
pbx_builtin_setvar_helper(chan, "BRIDGEPVTCALLID", pvtid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void update_bridge_vars(struct ast_channel *c0, struct ast_channel *c1)
|
|
|
|
|
{
|
|
|
|
|
const char *c0_name;
|
|
|
|
|
const char *c1_name;
|
|
|
|
|
const char *c0_pvtid = NULL;
|
|
|
|
|
const char *c1_pvtid = NULL;
|
|
|
|
|
#define UPDATE_BRIDGE_VARS_GET(chan, name, pvtid) \
|
|
|
|
|
do { \
|
|
|
|
|
name = ast_strdupa(ast_channel_name(chan)); \
|
|
|
|
|
if (ast_channel_tech(chan)->get_pvt_uniqueid) { \
|
|
|
|
|
pvtid = ast_strdupa(ast_channel_tech(chan)->get_pvt_uniqueid(chan)); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
ast_channel_lock(c1);
|
|
|
|
|
UPDATE_BRIDGE_VARS_GET(c1, c1_name, c1_pvtid);
|
|
|
|
|
ast_channel_unlock(c1);
|
|
|
|
|
|
|
|
|
|
ast_channel_lock(c0);
|
|
|
|
|
update_bridge_vars_set(c0, c1_name, c1_pvtid);
|
|
|
|
|
UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid);
|
|
|
|
|
ast_channel_unlock(c0);
|
|
|
|
|
|
|
|
|
|
ast_channel_lock(c1);
|
|
|
|
|
update_bridge_vars_set(c1, c0_name, c0_pvtid);
|
|
|
|
|
ast_channel_unlock(c1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void bridge_play_sounds(struct ast_channel *c0, struct ast_channel *c1)
|
|
|
|
|
{
|
|
|
|
|
const char *s, *sound;
|
|
|
|
|
|
|
|
|
|
/* See if we need to play an audio file to any side of the bridge */
|
|
|
|
|
|
|
|
|
|
ast_channel_lock(c0);
|
|
|
|
|
if ((s = pbx_builtin_getvar_helper(c0, "BRIDGE_PLAY_SOUND"))) {
|
|
|
|
|
sound = ast_strdupa(s);
|
|
|
|
|
ast_channel_unlock(c0);
|
|
|
|
|
bridge_playfile(c0, c1, sound, 0);
|
|
|
|
|
pbx_builtin_setvar_helper(c0, "BRIDGE_PLAY_SOUND", NULL);
|
|
|
|
|
} else {
|
|
|
|
|
ast_channel_unlock(c0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_channel_lock(c1);
|
|
|
|
|
if ((s = pbx_builtin_getvar_helper(c1, "BRIDGE_PLAY_SOUND"))) {
|
|
|
|
|
sound = ast_strdupa(s);
|
|
|
|
|
ast_channel_unlock(c1);
|
|
|
|
|
bridge_playfile(c1, c0, sound, 0);
|
|
|
|
|
pbx_builtin_setvar_helper(c1, "BRIDGE_PLAY_SOUND", NULL);
|
|
|
|
|
} else {
|
|
|
|
|
ast_channel_unlock(c1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* BUGBUG ast_channel_bridge() and anything that only it calls will be removed. */
|
|
|
|
|
/*! \brief Bridge two channels together */
|
|
|
|
|
enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1,
|
|
|
|
|
struct ast_bridge_config *config, struct ast_frame **fo, struct ast_channel **rc)
|
|
|
|
|
{
|
|
|
|
|
enum ast_bridge_result res;
|
|
|
|
|
struct ast_format_cap *o0nativeformats;
|
|
|
|
|
struct ast_format_cap *o1nativeformats;
|
|
|
|
|
long time_left_ms=0;
|
|
|
|
|
char caller_warning = 0;
|
|
|
|
|
char callee_warning = 0;
|
|
|
|
|
|
|
|
|
|
*fo = NULL;
|
|
|
|
|
|
|
|
|
|
if (ast_channel_internal_bridged_channel(c0)) {
|
|
|
|
|
ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
|
|
|
|
|
ast_channel_name(c0), ast_channel_name(ast_channel_internal_bridged_channel(c0)));
|
|
|
|
|
return AST_BRIDGE_FAILED;
|
|
|
|
|
}
|
|
|
|
|
if (ast_channel_internal_bridged_channel(c1)) {
|
|
|
|
|
ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
|
|
|
|
|
ast_channel_name(c1), ast_channel_name(ast_channel_internal_bridged_channel(c1)));
|
|
|
|
|
return AST_BRIDGE_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Stop if we're a zombie or need a soft hangup */
|
|
|
|
|
if (ast_test_flag(ast_channel_flags(c0), AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) ||
|
|
|
|
|
ast_test_flag(ast_channel_flags(c1), AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1))
|
|
|
|
|
return AST_BRIDGE_FAILED;
|
|
|
|
|
|
|
|
|
|
o0nativeformats = ast_format_cap_dup(ast_channel_nativeformats(c0));
|
|
|
|
|
o1nativeformats = ast_format_cap_dup(ast_channel_nativeformats(c1));
|
|
|
|
|
if (!o0nativeformats || !o1nativeformats) {
|
|
|
|
|
ast_format_cap_destroy(o0nativeformats);
|
|
|
|
|
ast_format_cap_destroy(o1nativeformats);
|
|
|
|
|
ast_log(LOG_WARNING, "failed to copy native formats\n");
|
|
|
|
|
return AST_BRIDGE_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
caller_warning = ast_test_flag(&config->features_caller, AST_FEATURE_PLAY_WARNING);
|
|
|
|
|
callee_warning = ast_test_flag(&config->features_callee, AST_FEATURE_PLAY_WARNING);
|
|
|
|
|
|
|
|
|
|
if (ast_tvzero(config->start_time)) {
|
|
|
|
|
config->start_time = ast_tvnow();
|
|
|
|
|
if (config->start_sound) {
|
|
|
|
|
if (caller_warning) {
|
|
|
|
|
bridge_playfile(c0, c1, config->start_sound, config->timelimit / 1000);
|
|
|
|
|
}
|
|
|
|
|
if (callee_warning) {
|
|
|
|
|
bridge_playfile(c1, c0, config->start_sound, config->timelimit / 1000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Keep track of bridge */
|
|
|
|
|
ast_channel_lock_both(c0, c1);
|
|
|
|
|
ast_channel_internal_bridged_channel_set(c0, c1);
|
|
|
|
|
ast_channel_internal_bridged_channel_set(c1, c0);
|
|
|
|
|
ast_channel_unlock(c0);
|
|
|
|
|
ast_channel_unlock(c1);
|
|
|
|
|
|
|
|
|
|
ast_set_owners_and_peers(c0, c1);
|
|
|
|
|
|
|
|
|
|
if (config->feature_timer && !ast_tvzero(config->nexteventts)) {
|
|
|
|
|
config->nexteventts = ast_tvadd(config->feature_start_time, ast_samp2tv(config->feature_timer, 1000));
|
|
|
|
|
} else if (config->timelimit) {
|
|
|
|
|
time_left_ms = config->timelimit - ast_tvdiff_ms(ast_tvnow(), config->start_time);
|
|
|
|
|
config->nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
|
|
|
|
|
if ((caller_warning || callee_warning) && config->play_warning) {
|
|
|
|
|
long next_warn = config->play_warning;
|
|
|
|
|
if (time_left_ms < config->play_warning && config->warning_freq > 0) {
|
|
|
|
|
/* At least one warning was played, which means we are returning after feature */
|
|
|
|
|
long warns_passed = (config->play_warning - time_left_ms) / config->warning_freq;
|
|
|
|
|
/* It is 'warns_passed * warning_freq' NOT '(warns_passed + 1) * warning_freq',
|
|
|
|
|
because nexteventts will be updated once again in the 'if (!to)' block */
|
|
|
|
|
next_warn = config->play_warning - warns_passed * config->warning_freq;
|
|
|
|
|
}
|
|
|
|
|
config->nexteventts = ast_tvsub(config->nexteventts, ast_samp2tv(next_warn, 1000));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
config->nexteventts = ast_tv(0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ast_channel_tech(c0)->send_digit_begin)
|
|
|
|
|
ast_set_flag(ast_channel_flags(c1), AST_FLAG_END_DTMF_ONLY);
|
|
|
|
|
if (!ast_channel_tech(c1)->send_digit_begin)
|
|
|
|
|
ast_set_flag(ast_channel_flags(c0), AST_FLAG_END_DTMF_ONLY);
|
|
|
|
|
manager_bridge_event(1, 1, c0, c1);
|
|
|
|
|
|
|
|
|
|
/* Before we enter in and bridge these two together tell them both the source of audio has changed */
|
|
|
|
|
ast_indicate(c0, AST_CONTROL_SRCUPDATE);
|
|
|
|
|
ast_indicate(c1, AST_CONTROL_SRCUPDATE);
|
|
|
|
|
|
|
|
|
|
for (/* ever */;;) {
|
|
|
|
|
struct timeval now = { 0, };
|
|
|
|
|
int to;
|
|
|
|
|
|
|
|
|
|
if (!ast_tvzero(config->nexteventts)) {
|
|
|
|
|
now = ast_tvnow();
|
|
|
|
|
to = ast_tvdiff_ms(config->nexteventts, now);
|
|
|
|
|
if (to <= 0) {
|
|
|
|
|
if (!config->timelimit) {
|
|
|
|
|
res = AST_BRIDGE_COMPLETE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
to = 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
to = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (config->timelimit) {
|
|
|
|
|
time_left_ms = config->timelimit - ast_tvdiff_ms(now, config->start_time);
|
|
|
|
|
if (time_left_ms < to)
|
|
|
|
|
to = time_left_ms;
|
|
|
|
|
|
|
|
|
|
if (time_left_ms <= 0) {
|
|
|
|
|
if (caller_warning && config->end_sound)
|
|
|
|
|
bridge_playfile(c0, c1, config->end_sound, 0);
|
|
|
|
|
if (callee_warning && config->end_sound)
|
|
|
|
|
bridge_playfile(c1, c0, config->end_sound, 0);
|
|
|
|
|
*fo = NULL;
|
|
|
|
|
res = AST_BRIDGE_COMPLETE;
|
|
|
|
|
ast_test_suite_event_notify("BRIDGE_TIMELIMIT",
|
|
|
|
|
"Channel1: %s\r\n"
|
|
|
|
|
"Channel2: %s",
|
|
|
|
|
ast_channel_name(c0), ast_channel_name(c1));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!to) {
|
|
|
|
|
if (time_left_ms >= 5000 && config->warning_sound && config->play_warning && ast_test_flag(config, AST_FEATURE_WARNING_ACTIVE)) {
|
|
|
|
|
int t = (time_left_ms + 500) / 1000; /* round to nearest second */
|
|
|
|
|
if (caller_warning)
|
|
|
|
|
bridge_playfile(c0, c1, config->warning_sound, t);
|
|
|
|
|
if (callee_warning)
|
|
|
|
|
bridge_playfile(c1, c0, config->warning_sound, t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (config->warning_freq && (time_left_ms > (config->warning_freq + 5000))) {
|
|
|
|
|
config->nexteventts = ast_tvadd(config->nexteventts, ast_samp2tv(config->warning_freq, 1000));
|
|
|
|
|
} else {
|
|
|
|
|
config->nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ast_clear_flag(config, AST_FEATURE_WARNING_ACTIVE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((ast_channel_softhangup_internal_flag(c0) | ast_channel_softhangup_internal_flag(c1)) & AST_SOFTHANGUP_UNBRIDGE) {/* Bit operators are intentional. */
|
|
|
|
|
if (ast_channel_softhangup_internal_flag(c0) & AST_SOFTHANGUP_UNBRIDGE) {
|
|
|
|
|
ast_channel_clear_softhangup(c0, AST_SOFTHANGUP_UNBRIDGE);
|
|
|
|
|
}
|
|
|
|
|
if (ast_channel_softhangup_internal_flag(c1) & AST_SOFTHANGUP_UNBRIDGE) {
|
|
|
|
|
ast_channel_clear_softhangup(c1, AST_SOFTHANGUP_UNBRIDGE);
|
|
|
|
|
}
|
|
|
|
|
ast_channel_lock_both(c0, c1);
|
|
|
|
|
ast_channel_internal_bridged_channel_set(c0, c1);
|
|
|
|
|
ast_channel_internal_bridged_channel_set(c1, c0);
|
|
|
|
|
ast_channel_unlock(c0);
|
|
|
|
|
ast_channel_unlock(c1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Stop if we're a zombie or need a soft hangup */
|
|
|
|
|
if (ast_test_flag(ast_channel_flags(c0), AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) ||
|
|
|
|
|
ast_test_flag(ast_channel_flags(c1), AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1)) {
|
|
|
|
|
*fo = NULL;
|
|
|
|
|
res = AST_BRIDGE_COMPLETE;
|
|
|
|
|
ast_debug(1, "Bridge stops because we're zombie or need a soft hangup: c0=%s, c1=%s, flags: %s,%s,%s,%s\n",
|
|
|
|
|
ast_channel_name(c0), ast_channel_name(c1),
|
|
|
|
|
ast_test_flag(ast_channel_flags(c0), AST_FLAG_ZOMBIE) ? "Yes" : "No",
|
|
|
|
|
ast_check_hangup(c0) ? "Yes" : "No",
|
|
|
|
|
ast_test_flag(ast_channel_flags(c1), AST_FLAG_ZOMBIE) ? "Yes" : "No",
|
|
|
|
|
ast_check_hangup(c1) ? "Yes" : "No");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update_bridge_vars(c0, c1);
|
|
|
|
|
|
|
|
|
|
bridge_play_sounds(c0, c1);
|
|
|
|
|
|
|
|
|
|
if (ast_channel_tech(c0)->bridge &&
|
|
|
|
|
/* if < 1 ms remains use generic bridging for accurate timing */
|
|
|
|
|
(!config->timelimit || to > 1000 || to == 0) &&
|
|
|
|
|
(ast_channel_tech(c0)->bridge == ast_channel_tech(c1)->bridge) &&
|
|
|
|
|
!ast_channel_monitor(c0) && !ast_channel_monitor(c1) &&
|
|
|
|
|
!ast_channel_audiohooks(c0) && !ast_channel_audiohooks(c1) &&
|
|
|
|
|
ast_framehook_list_is_empty(ast_channel_framehooks(c0)) && ast_framehook_list_is_empty(ast_channel_framehooks(c1)) &&
|
|
|
|
|
!ast_channel_masq(c0) && !ast_channel_masqr(c0) && !ast_channel_masq(c1) && !ast_channel_masqr(c1)) {
|
|
|
|
|
int timeoutms = to - 1000 > 0 ? to - 1000 : to;
|
|
|
|
|
|
|
|
|
|
/* Looks like they share a bridge method and nothing else is in the way */
|
|
|
|
|
ast_set_flag(ast_channel_flags(c0), AST_FLAG_NBRIDGE);
|
|
|
|
|
ast_set_flag(ast_channel_flags(c1), AST_FLAG_NBRIDGE);
|
|
|
|
|
res = ast_channel_tech(c0)->bridge(c0, c1, config->flags, fo, rc, timeoutms);
|
|
|
|
|
ast_clear_flag(ast_channel_flags(c0), AST_FLAG_NBRIDGE);
|
|
|
|
|
ast_clear_flag(ast_channel_flags(c1), AST_FLAG_NBRIDGE);
|
|
|
|
|
if (res == AST_BRIDGE_COMPLETE) {
|
|
|
|
|
ast_debug(1, "Returning from native bridge, channels: %s, %s\n", ast_channel_name(c0), ast_channel_name(c1));
|
|
|
|
|
|
|
|
|
|
if ((ast_channel_softhangup_internal_flag(c0) | ast_channel_softhangup_internal_flag(c1)) & AST_SOFTHANGUP_UNBRIDGE) {/* Bit operators are intentional. */
|
|
|
|
|
ast_debug(1, "Unbridge signal received. Ending native bridge.\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
switch (res) {
|
|
|
|
|
case AST_BRIDGE_RETRY:
|
|
|
|
|
if (config->play_warning) {
|
|
|
|
|
ast_set_flag(config, AST_FEATURE_WARNING_ACTIVE);
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
default:
|
|
|
|
|
ast_verb(3, "Native bridging %s and %s ended\n", ast_channel_name(c0), ast_channel_name(c1));
|
|
|
|
|
/* fallthrough */
|
|
|
|
|
case AST_BRIDGE_FAILED_NOWARN:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (((ast_format_cmp(ast_channel_readformat(c1), ast_channel_writeformat(c0)) == AST_FORMAT_CMP_NOT_EQUAL) ||
|
|
|
|
|
(ast_format_cmp(ast_channel_readformat(c0), ast_channel_writeformat(c1)) == AST_FORMAT_CMP_NOT_EQUAL) ||
|
|
|
|
|
!ast_format_cap_identical(ast_channel_nativeformats(c0), o0nativeformats) ||
|
|
|
|
|
!ast_format_cap_identical(ast_channel_nativeformats(c1), o1nativeformats)) &&
|
|
|
|
|
!(ast_channel_generator(c0) || ast_channel_generator(c1))) {
|
|
|
|
|
if (ast_channel_make_compatible(c0, c1)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", ast_channel_name(c0), ast_channel_name(c1));
|
|
|
|
|
res = AST_BRIDGE_FAILED;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_format_cap_copy(o0nativeformats, ast_channel_nativeformats(c0));
|
|
|
|
|
ast_format_cap_copy(o1nativeformats, ast_channel_nativeformats(c1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update_bridge_vars(c0, c1);
|
|
|
|
|
|
|
|
|
|
res = ast_generic_bridge(c0, c1, config, fo, rc);
|
|
|
|
|
if (res != AST_BRIDGE_RETRY) {
|
|
|
|
|
break;
|
|
|
|
|
} else if (config->feature_timer) {
|
|
|
|
|
/* feature timer expired but has not been updated, sending to ast_bridge_call to do so */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_clear_flag(ast_channel_flags(c0), AST_FLAG_END_DTMF_ONLY);
|
|
|
|
|
ast_clear_flag(ast_channel_flags(c1), AST_FLAG_END_DTMF_ONLY);
|
|
|
|
|
|
|
|
|
|
/* Now that we have broken the bridge the source will change yet again */
|
|
|
|
|
ast_indicate(c0, AST_CONTROL_SRCUPDATE);
|
|
|
|
|
ast_indicate(c1, AST_CONTROL_SRCUPDATE);
|
|
|
|
|
|
|
|
|
|
ast_channel_lock_both(c0, c1);
|
|
|
|
|
ast_channel_internal_bridged_channel_set(c0, NULL);
|
|
|
|
|
ast_channel_internal_bridged_channel_set(c1, NULL);
|
|
|
|
|
ast_channel_unlock(c0);
|
|
|
|
|
ast_channel_unlock(c1);
|
|
|
|
|
|
|
|
|
|
manager_bridge_event(0, 1, c0, c1);
|
|
|
|
|
ast_debug(1, "Bridge stops bridging channels %s and %s\n", ast_channel_name(c0), ast_channel_name(c1));
|
|
|
|
|
|
|
|
|
|
ast_format_cap_destroy(o0nativeformats);
|
|
|
|
|
ast_format_cap_destroy(o1nativeformats);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Sets an option on a channel */
|
|
|
|
|
int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block)
|
|
|
|
|
{
|
|
|
|
|
|