Newer
Older
else
ast_write(c0, f);
}
}
ast_frfree(f);
/* Swap who gets priority */
cs[2] = cs[0];
cs[0] = cs[1];
cs[1] = cs[2];
}
return res;
}
/*--- ast_channel_bridge: Bridge two channels together */
int ast_channel_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. Give the peer
the ability to transfer calls with '#<extension' syntax. */
struct ast_channel *cs[3];
struct ast_channel *who = NULL;
int res=0;
int o0nativeformats;
int o1nativeformats;
Kevin P. Fleming
committed
struct timeval start_time;
long elapsed_ms=0, time_left_ms=0;
int playit=0, playitagain=1, first_time=1;
firstpass = config->firstpass;
config->firstpass = 0;
/* timestamp */
Kevin P. Fleming
committed
start_time = ast_tvnow();
time_left_ms = config->timelimit;
if ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) && config->start_sound && firstpass)
Anthony Minessale II
committed
bridge_playfile(c0,c1,config->start_sound,time_left_ms / 1000);
if ((ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING)) && config->start_sound && firstpass)
Anthony Minessale II
committed
bridge_playfile(c1,c0,config->start_sound,time_left_ms / 1000);
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) || ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1))
Mark Spencer
committed
if (c0->_bridge) {
ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
Mark Spencer
committed
c0->name, c0->_bridge->name);
Mark Spencer
committed
if (c1->_bridge) {
ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
Mark Spencer
committed
c1->name, c1->_bridge->name);
return -1;
}
/* Keep track of bridge */
Mark Spencer
committed
c0->_bridge = c1;
c1->_bridge = c0;
manager_event(EVENT_FLAG_CALL, "Link",
"Channel1: %s\r\n"
James Golovich
committed
"Channel2: %s\r\n"
"Uniqueid1: %s\r\n"
"Uniqueid2: %s\r\n"
"CallerID1: %s\r\n"
"CallerID2: %s\r\n",
c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
o1nativeformats = c1->nativeformats;
o0nativeformats = c0->nativeformats;
/* timestamp */
Kevin P. Fleming
committed
elapsed_ms = ast_tvdiff_ms(ast_tvnow(), start_time);
time_left_ms = config->timelimit - elapsed_ms;
if (playitagain && ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) || (ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING))) && (config->play_warning && time_left_ms <= config->play_warning)) {
/* narrowing down to the end */
playit = 1;
first_time=0;
playitagain=0;
playit = 1;
first_time=0;
} else {
if ((time_left_ms % config->warning_freq) <= 50) {
playit = 1;
}
}
}
if ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) && config->end_sound)
Anthony Minessale II
committed
bridge_playfile(c0,c1,config->end_sound,0);
if ((ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING)) && config->end_sound)
Anthony Minessale II
committed
bridge_playfile(c1,c0,config->end_sound,0);
if (who)
*rc = who;
break;
}
if (time_left_ms >= 5000 && playit) {
if ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) && config->warning_sound && config->play_warning)
Anthony Minessale II
committed
bridge_playfile(c0,c1,config->warning_sound,time_left_ms / 1000);
if ((ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING)) && config->warning_sound && config->play_warning)
Anthony Minessale II
committed
bridge_playfile(c1,c0,config->warning_sound,time_left_ms / 1000);
playit = 0;
}
}
if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
c0->_softhangup = 0;
if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
c1->_softhangup = 0;
c0->_bridge = c1;
c1->_bridge = c0;
ast_log(LOG_DEBUG, "Unbridge signal received. Ending native bridge.\n");
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) || ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1)) {
if (who)
*rc = who;
ast_log(LOG_DEBUG, "Bridge stops because we're zombie or need a soft hangup: c0=%s, c1=%s, flags: %s,%s,%s,%s\n",c0->name,c1->name,ast_test_flag(c0, AST_FLAG_ZOMBIE)?"Yes":"No",ast_check_hangup(c0)?"Yes":"No",ast_test_flag(c1, AST_FLAG_ZOMBIE)?"Yes":"No",ast_check_hangup(c1)?"Yes":"No");
if (c0->tech->bridge && config->timelimit==0 &&
(c0->tech->bridge == c1->tech->bridge) && !nativefailed && !c0->monitor && !c1->monitor && !c0->spiers && !c1->spiers) {
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Attempting native bridge of %s and %s\n", c0->name, c1->name);
ast_set_flag(c0, AST_FLAG_NBRIDGE);
ast_set_flag(c1, AST_FLAG_NBRIDGE);
if (!(res = c0->tech->bridge(c0, c1, config->flags, fo, rc))) {
Mark Spencer
committed
c0->_bridge = NULL;
c1->_bridge = NULL;
manager_event(EVENT_FLAG_CALL, "Unlink",
"Channel1: %s\r\n"
James Golovich
committed
"Channel2: %s\r\n"
"Uniqueid1: %s\r\n"
"Uniqueid2: %s\r\n"
"CallerID1: %s\r\n"
"CallerID2: %s\r\n",
c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
ast_log(LOG_DEBUG, "Returning from native bridge, channels: %s, %s\n",c0->name ,c1->name);
ast_clear_flag(c0, AST_FLAG_NBRIDGE);
ast_clear_flag(c1, AST_FLAG_NBRIDGE);
if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
c0->_bridge = c1;
c1->_bridge = c0;
continue;
}
else
} else {
ast_clear_flag(c0, AST_FLAG_NBRIDGE);
ast_clear_flag(c1, AST_FLAG_NBRIDGE);
/* If they return non-zero then continue on normally. Let "-2" mean don't worry about
my not wanting to bridge */
if ((res != -2) && (res != -3))
ast_log(LOG_WARNING, "Private bridge between %s and %s failed\n", c0->name, c1->name);
if (((c0->writeformat != c1->readformat) || (c0->readformat != c1->writeformat) || (c0->nativeformats != o0nativeformats) || (c1->nativeformats != o1nativeformats)) &&
if (ast_channel_make_compatible(c0, c1)) {
ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", c0->name, c1->name);
manager_event(EVENT_FLAG_CALL, "Unlink",
"Channel1: %s\r\n"
"Channel2: %s\r\n"
"Uniqueid1: %s\r\n"
"Uniqueid2: %s\r\n"
"CallerID1: %s\r\n"
"CallerID2: %s\r\n",
c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
o0nativeformats = c0->nativeformats;
o1nativeformats = c1->nativeformats;
res = ast_generic_bridge(&playitagain, &playit, &start_time, c0, c1, config, fo, rc);
if (res != -3)
Mark Spencer
committed
c0->_bridge = NULL;
c1->_bridge = NULL;
manager_event(EVENT_FLAG_CALL, "Unlink",
James Golovich
committed
"Channel2: %s\r\n"
"Uniqueid1: %s\r\n"
"Uniqueid2: %s\r\n"
"CallerID1: %s\r\n"
"CallerID2: %s\r\n",
c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
ast_log(LOG_DEBUG, "Bridge stops bridging channels %s and %s\n",c0->name,c1->name);
/*--- ast_channel_setoption: Sets an option on a channel */
int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block)
{
int res;
if (chan->tech->setoption) {
res = chan->tech->setoption(chan, option, data, datalen);
if (res < 0)
return res;
} else {
errno = ENOSYS;
return -1;
}
if (block) {
/* XXX Implement blocking -- just wait for our option frame reply, discarding
intermediate packets. XXX */
ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
return -1;
}
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
struct tonepair_def {
int freq1;
int freq2;
int duration;
int vol;
};
struct tonepair_state {
float freq1;
float freq2;
float vol;
int duration;
int pos;
int origwfmt;
struct ast_frame f;
unsigned char offset[AST_FRIENDLY_OFFSET];
short data[4000];
};
static void tonepair_release(struct ast_channel *chan, void *params)
{
struct tonepair_state *ts = params;
Mark Spencer
committed
ast_set_write_format(chan, ts->origwfmt);
static void *tonepair_alloc(struct ast_channel *chan, void *params)
{
struct tonepair_state *ts;
struct tonepair_def *td = params;
ts = malloc(sizeof(struct tonepair_state));
if (!ts)
return NULL;
memset(ts, 0, sizeof(struct tonepair_state));
ts->origwfmt = chan->writeformat;
Mark Spencer
committed
if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
tonepair_release(NULL, ts);
ts = NULL;
} else {
ts->freq1 = td->freq1;
ts->freq2 = td->freq2;
ts->duration = td->duration;
ts->vol = td->vol;
}
/* Let interrupts interrupt :) */
static int tonepair_generator(struct ast_channel *chan, void *data, int len, int samples)
{
struct tonepair_state *ts = data;
int x;
/* we need to prepare a frame with 16 * timelen samples as we're
* generating SLIN audio
*/
len = samples * 2;
if (len > sizeof(ts->data) / 2 - 1) {
ast_log(LOG_WARNING, "Can't generate that much data!\n");
return -1;
}
memset(&ts->f, 0, sizeof(ts->f));
ts->data[x] = ts->vol * (
sin((ts->freq1 * 2.0 * M_PI / 8000.0) * (ts->pos + x)) +
sin((ts->freq2 * 2.0 * M_PI / 8000.0) * (ts->pos + x))
);
}
ts->f.frametype = AST_FRAME_VOICE;
ts->f.subclass = AST_FORMAT_SLINEAR;
ts->f.datalen = len;
ts->f.offset = AST_FRIENDLY_OFFSET;
ts->f.data = ts->data;
ast_write(chan, &ts->f);
ts->pos += x;
if (ts->duration > 0) {
if (ts->pos >= ts->duration * 8)
return -1;
}
return 0;
}
static struct ast_generator tonepair = {
alloc: tonepair_alloc,
release: tonepair_release,
generate: tonepair_generator,
};
int ast_tonepair_start(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
{
struct tonepair_def d = { 0, };
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
d.freq1 = freq1;
d.freq2 = freq2;
d.duration = duration;
if (vol < 1)
d.vol = 8192;
else
d.vol = vol;
if (ast_activate_generator(chan, &tonepair, &d))
return -1;
return 0;
}
void ast_tonepair_stop(struct ast_channel *chan)
{
ast_deactivate_generator(chan);
}
int ast_tonepair(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
{
struct ast_frame *f;
int res;
if ((res = ast_tonepair_start(chan, freq1, freq2, duration, vol)))
return res;
/* Give us some wiggle room */
while(chan->generatordata && (ast_waitfor(chan, 100) >= 0)) {
f = ast_read(chan);
if (f)
ast_frfree(f);
else
return -1;
}
return 0;
}
ast_group_t ast_get_group(char *s)
{
char *copy;
char *piece;
char *c=NULL;
copy = ast_strdupa(s);
if (!copy) {
ast_log(LOG_ERROR, "Out of memory\n");
return 0;
}
c = copy;
while((piece = strsep(&c, ","))) {
if (sscanf(piece, "%d-%d", &start, &finish) == 2) {
/* Range */
} else if (sscanf(piece, "%d", &start)) {
/* Just one */
finish = start;
} else {
ast_log(LOG_ERROR, "Syntax error parsing group configuration '%s' at '%s'. Ignoring.\n", s, piece);
continue;
if ((x > 63) || (x < 0)) {
ast_log(LOG_WARNING, "Ignoring invalid group %d (maximum group is 63)\n", x);
group |= ((ast_group_t) 1 << x);
}
}
return group;
}
static int (*ast_moh_start_ptr)(struct ast_channel *, char *) = NULL;
static void (*ast_moh_stop_ptr)(struct ast_channel *) = NULL;
Mark Spencer
committed
static void (*ast_moh_cleanup_ptr)(struct ast_channel *) = NULL;
void ast_install_music_functions(int (*start_ptr)(struct ast_channel *, char *),
Mark Spencer
committed
void (*stop_ptr)(struct ast_channel *),
void (*cleanup_ptr)(struct ast_channel *)
)
{
ast_moh_start_ptr = start_ptr;
ast_moh_stop_ptr = stop_ptr;
Mark Spencer
committed
ast_moh_cleanup_ptr = cleanup_ptr;
}
void ast_uninstall_music_functions(void)
{
ast_moh_start_ptr = NULL;
ast_moh_stop_ptr = NULL;
Mark Spencer
committed
ast_moh_cleanup_ptr = NULL;
/*! Turn on music on hold on a given channel */
int ast_moh_start(struct ast_channel *chan, char *mclass)
{
return ast_moh_start_ptr(chan, mclass);
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Music class %s requested but no musiconhold loaded.\n", mclass ? mclass : "default");
return 0;
}
/*! Turn off music on hold on a given channel */
void ast_moh_stop(struct ast_channel *chan)
{
if(ast_moh_stop_ptr)
ast_moh_stop_ptr(chan);
}
Mark Spencer
committed
void ast_moh_cleanup(struct ast_channel *chan)
{
if(ast_moh_cleanup_ptr)
ast_moh_cleanup_ptr(chan);
}
void ast_channels_init(void)
{
ast_cli_register(&cli_show_channeltypes);
}
/*--- ast_print_group: Print call group and pickup group ---*/
char *ast_print_group(char *buf, int buflen, ast_group_t group)
{
unsigned int i;
int first=1;
char num[3];
buf[0] = '\0';
if (!group) /* Return empty string if no group */
return(buf);
for (i=0; i<=63; i++) { /* Max group is 63 */
if (group & ((ast_group_t) 1 << i)) {
if (!first) {
strncat(buf, ", ", buflen);
} else {
first=0;
}
snprintf(num, sizeof(num), "%u", i);
strncat(buf, num, buflen);
}
}
return(buf);
}
void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars)
{
struct ast_variable *cur;
for (cur = vars; cur; cur = cur->next)
pbx_builtin_setvar_helper(chan, cur->name, cur->value);
}