[Asterisk-Dev] Manager API Bridge channels help

mattf mattf at vicimarketing.com
Fri May 13 11:57:51 MST 2005


Hello,

I've seen the concept of this batted around a few times in the last
year(taking 2 existing conversations, removing one party from each and
Bridging them together by way of a Manager API Action: Bridge), but noone
seems to have gotten it to fully work. I took several pointers from several
people, but now my lack of knowledge about the core channel handling in
Asterisk has stopped me.

In the code below you can see that I MASQ both channels I want to bridge
together and drop the other two, and as far as the talking parties can tell
they are then connected and the other two lines are dead, (and 'show
channels' reflects this point as well.). The problem is that one or both of
the dropped off channels will be stuck in ast_channel_walk_locked mode
unless you do a hard hangup(in which case the do not disappear from 'show
channels') and even though the 2 bridged lines can converse and show
channels says they are together, the manager does not return a success or
failure and it seems to be stuck at the ast_channel_bridge function.

Any help would be greatly appreciated.

MATT---

Here's the code (I just add it into
res_features.c)----------------------------------------------------

static int action_bridge(struct mansession *s, struct message *m)
{
	char *name = astman_get_header(m, "ChannelA");
	char *name2 = astman_get_header(m, "ChannelB");
	struct ast_channel *chan, *chan2, *tmpchan, *tmpchan2, *who = NULL;
	struct ast_bridge_config config;
	struct ast_frame *f;
	struct timeval start, end;
	int pi = 0;
	int sleepms = 500;
	int res;
	int diff;
	int allowredir_in=1;
	int allowredir_out=1;
	int masqa=0;
	int masqb=0;
	if (!name || ast_strlen_zero(name)) {
		astman_send_error(s, m, "ChannelA not specified");
		return 0;
	}
	if (!name2 || ast_strlen_zero(name2)) {
		astman_send_error(s, m, "ChannelB not specified");
		return 0;
	}
	chan = ast_get_channel_by_name_locked(name);
	if (!chan) {
		astman_send_error(s, m, "ChannelA not existant");
		return 0;
	}
	chan2 = ast_get_channel_by_name_locked(name2);
	if (!chan2) {
		astman_send_error(s, m, "ChannelB not existant");
		return 0;
	}

	if (chan) {
		ast_log(LOG_NOTICE, "Bridge: ChannelA %s is live and ready
to Masq\n", chan->name);
	ast_mutex_lock(&chan->lock);
		/* In order to Bridge the channel to another live channel,
we have to
		   make a new channel, masquerade, put it to sleep while we
do the same
		   with the other channel, then we can Bridge them together
*/
		tmpchan = ast_channel_alloc(0);
		if (tmpchan) {
			snprintf(tmpchan->name, sizeof(tmpchan->name),
"BridgeGoto/%s", chan->name);
			ast_setstate(tmpchan, chan->_state);
			/* Make formats okay */
			tmpchan->readformat = chan->readformat;
			tmpchan->writeformat = chan->writeformat;
			/* Masquerade into temp channel */
			ast_channel_masquerade(tmpchan, chan);		
			/* Grab the locks and get going */
			ast_mutex_lock(&tmpchan->lock);
			ast_do_masquerade(tmpchan);
			ast_mutex_unlock(&tmpchan->lock);
			/* Send on our stolen channel to sleep as we wait to
Bridge it */
			if (ast_safe_sleep(tmpchan, sleepms)) {
				ast_log(LOG_WARNING, "Unable to send %s to
sleep\n", tmpchan->name);
				ast_hangup(tmpchan);
				res = -1;
			}
			ast_log(LOG_NOTICE, "Bridge ChannelA %s Masq
Successful to %s\n", chan->name, tmpchan->name);
			masqa=1;
		} else {
			res = -1;
			ast_log(LOG_NOTICE, "Bridge ChannelA %s Masq Failed
to %s\n", chan->name, tmpchan->name);
		}
	ast_mutex_unlock(&chan->lock);
	ast_deactivate_generator(chan);
	ast_autoservice_stop(chan);
	ast_channel_unregister(chan);
	}

	if (chan2) {
		ast_log(LOG_NOTICE, "Bridge: ChannelB %s is live and ready
to Masq\n", chan2->name);
	ast_mutex_lock(&chan2->lock);
		/* In order to Bridge the channel to another live channel,
we have to
		   make a new channel, masquerade, put it to sleep while we
do the same
		   with the other channel, then we can Bridge them together
*/
		tmpchan2 = ast_channel_alloc(0);
		if (tmpchan2) {
			snprintf(tmpchan2->name, sizeof(tmpchan2->name),
"BridgeGoto/%s", chan2->name);
			ast_setstate(tmpchan2, chan2->_state);
			/* Make formats okay */
			tmpchan2->readformat = chan2->readformat;
			tmpchan2->writeformat = chan2->writeformat;
			/* Masquerade into temp channel */
			ast_channel_masquerade(tmpchan2, chan2);

			/* Grab the locks and get going */
			ast_mutex_lock(&tmpchan2->lock);
			ast_do_masquerade(tmpchan2);
			ast_mutex_unlock(&tmpchan2->lock);
			/* Send on our stolen channel to sleep as we wait to
Bridge it */
			if (ast_safe_sleep(tmpchan2, sleepms)) {
				ast_log(LOG_WARNING, "Unable to send %s to
sleep\n", tmpchan2->name);
				ast_hangup(tmpchan2);
				res = -1;
			}
			ast_log(LOG_NOTICE, "Bridge ChannelB %s Masq
Successful to %s\n", chan2->name, tmpchan2->name);
			masqa=1;
		} else {
			res = -1;
			ast_log(LOG_NOTICE, "Bridge ChannelB %s Masq Failed
to %s\n", chan2->name, tmpchan2->name);
		}
	ast_mutex_unlock(&chan2->lock);
	ast_deactivate_generator(chan2);
	ast_autoservice_stop(chan2);
	ast_channel_unregister(chan2);
	}

	memset(&config,0,sizeof(struct ast_bridge_config));
	config.allowredirect_in = allowredir_in;
	config.allowredirect_out = allowredir_out;
	config.timelimit = 0;

		res = ast_channel_make_compatible(tmpchan, tmpchan2);
		if (!res) {
			ast_log(LOG_NOTICE, "ChannelA %s and ChannelB %s are
compatible\n", tmpchan->name, tmpchan2->name);
				tmpchan->appl = "Bridged Call";
				tmpchan->data = tmpchan2->name;
				tmpchan2->appl = "Bridged Call";
				tmpchan2->data = tmpchan->name;
			/* Bridge the two channels now */
			for (;;) {
				if (config.timelimit)
					gettimeofday(&start, NULL);
				res =
ast_channel_bridge(tmpchan,tmpchan2,&config,&f, &who);
				if (config.timelimit) {
					/* Update time limit for next pass
*/
					gettimeofday(&end, NULL);
					diff = (end.tv_sec - start.tv_sec) *
1000;
					diff += (end.tv_usec -
start.tv_usec) / 1000;
					config.timelimit -= diff;
					if (config.timelimit <=0) {
						/* We ran out of time */
						config.timelimit = 0;
						who = tmpchan;
						f = NULL;
						res = 0;
					}
				}
			}
			if (!res) {
				astman_send_ack(s, m, "Bridge successful");
			} else
				astman_send_error(s, m, "Bridge failed
ast_channel_bridge");
		} else
			astman_send_error(s, m, "Bridge failed
make_compatible");

	if (tmpchan)
		ast_mutex_unlock(&tmpchan->lock);
	if (tmpchan2)
		ast_mutex_unlock(&tmpchan2->lock);
	return 0;
}



int load_module(void)
{
	ast_manager_register( "Bridge", EVENT_FLAG_CALL, action_bridge,
"Bridge 2 live channels together" );
}



More information about the asterisk-dev mailing list