Newer
Older
/* And of course, so does our current state. Note we need not
call ast_setstate since the event manager doesn't really consider
these separate. We do this early so that the clone has the proper
state of the original channel. */
origstate = original->_state;
original->_state = clone->_state;
clone->_state = origstate;
if (clone->tech->fixup){
res = clone->tech->fixup(original, clone);
if (res)
ast_log(LOG_WARNING, "Fixup failed on channel %s, strange things may happen.\n", clone->name);
}
/* Start by disconnecting the original's physical side */
if (clone->tech->hangup)
res = clone->tech->hangup(clone);
if (res) {
ast_log(LOG_WARNING, "Hangup failed! Strange things may happen!\n");
Mark Spencer
committed
ast_mutex_unlock(&clone->lock);
snprintf(zombn, sizeof(zombn), "%s<ZOMBIE>", orig);
ast_string_field_set(clone, name, zombn);
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", masqn, zombn, clone->uniqueid);
t_pvt = original->monitor;
original->monitor = clone->monitor;
clone->monitor = t_pvt;
ast_string_field_set(original, language, clone->language);
/* Copy the FD's other than the generator fd */
if (x != AST_GENERATOR_FD)
original->fds[x] = clone->fds[x];
clone_variables(original, clone);
Kevin P. Fleming
committed
AST_LIST_HEAD_INIT_NOLOCK(&clone->varshead);
/* Presense of ADSI capable CPE follows clone */
original->adsicpe = clone->adsicpe;
/* Bridge remains the same */
/* CDR fields remain the same */
/* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */
/* Application and data remain the same */
/* Clone exception becomes real one, as with fdno */
ast_copy_flags(original, clone, AST_FLAG_EXCEPTION);
original->fdno = clone->fdno;
/* Schedule context remains the same */
/* Stream stuff stays the same */
/* Keep the original state. The fixup code will need to work with it most likely */
/* Just swap the whole structures, nevermind the allocations, they'll work themselves
out. */
tmpcid = original->cid;
original->cid = clone->cid;
clone->cid = tmpcid;
/* Restore original timing file descriptor */
original->fds[AST_TIMING_FD] = original->timingfd;
/* Our native formats are different now */
original->nativeformats = clone->nativeformats;
/* Context, extension, priority, app data, jump table, remain the same */
/* pvt switches. pbx stays the same, as does next */
/* Set the write format */
Mark Spencer
committed
ast_set_write_format(original, wformat);
Mark Spencer
committed
ast_set_read_format(original, rformat);
ast_string_field_set(original, musicclass, clone->musicclass);
ast_log(LOG_DEBUG, "Putting channel %s in %d/%d formats\n", original->name, wformat, rformat);
/* Okay. Last thing is to let the channel driver know about all this mess, so he
can fix up everything as best as possible */
if (original->tech->fixup) {
res = original->tech->fixup(clone, original);
ast_log(LOG_WARNING, "Channel for type '%s' could not fixup channel %s\n",
original->tech->type, original->name);
Mark Spencer
committed
ast_mutex_unlock(&clone->lock);
ast_log(LOG_WARNING, "Channel type '%s' does not have a fixup routine (for %s)! Bad things may happen.\n",
original->tech->type, original->name);
/* Now, at this point, the "clone" channel is totally F'd up. We mark it as
a zombie so nothing tries to touch it. If it's already been marked as a
zombie, then free it now (since it already is considered invalid). */
if (ast_test_flag(clone, AST_FLAG_ZOMBIE)) {
ast_log(LOG_DEBUG, "Destroying channel clone '%s'\n", clone->name);
Mark Spencer
committed
ast_mutex_unlock(&clone->lock);
manager_event(EVENT_FLAG_CALL, "Hangup",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
"Cause: %d\r\n"
"Cause-txt: %s\r\n",
clone->name,
clone->uniqueid,
clone->hangupcause,
ast_cause2str(clone->hangupcause)
);
BJ Weschke
committed
ast_channel_free(clone);
} else {
ast_log(LOG_DEBUG, "Released clone lock on '%s'\n", clone->name);
ast_queue_frame(clone, &ast_null_frame);
Mark Spencer
committed
ast_mutex_unlock(&clone->lock);
if (ast_test_flag(original, AST_FLAG_BLOCKING))
ast_log(LOG_DEBUG, "Done Masquerading %s (%d)\n", original->name, original->_state);
void ast_set_callerid(struct ast_channel *chan, const char *callerid, const char *calleridname, const char *ani)
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
if (chan->cid.cid_num)
free(chan->cid.cid_num);
if (ast_strlen_zero(callerid))
chan->cid.cid_num = NULL;
else
chan->cid.cid_num = strdup(callerid);
}
if (calleridname) {
if (chan->cid.cid_name)
free(chan->cid.cid_name);
if (ast_strlen_zero(calleridname))
chan->cid.cid_name = NULL;
else
chan->cid.cid_name = strdup(calleridname);
}
if (ani) {
if (chan->cid.cid_ani)
free(chan->cid.cid_ani);
if (ast_strlen_zero(ani))
chan->cid.cid_ani = NULL;
else
chan->cid.cid_ani = strdup(ani);
}
if (chan->cdr)
ast_cdr_setcid(chan->cdr, chan);
manager_event(EVENT_FLAG_CALL, "Newcallerid",
"Channel: %s\r\n"
"CallerID: %s\r\n"
"CallerIDName: %s\r\n"
"Uniqueid: %s\r\n"
"CID-CallingPres: %d (%s)\r\n",
chan->name, chan->cid.cid_num ?
chan->cid.cid_num : "<Unknown>",
chan->cid.cid_name ?
chan->cid.cid_name : "<Unknown>",
chan->uniqueid,
chan->cid.cid_pres,
ast_describe_caller_presentation(chan->cid.cid_pres)
);
}
int ast_setstate(struct ast_channel *chan, int state)
{
int oldstate = chan->_state;
if (oldstate == state)
return 0;
chan->_state = state;
Mark Spencer
committed
ast_device_state_changed_literal(chan->name);
manager_event(EVENT_FLAG_CALL,
(oldstate == AST_STATE_DOWN) ? "Newchannel" : "Newstate",
"Channel: %s\r\n"
"State: %s\r\n"
"CallerID: %s\r\n"
"CallerIDName: %s\r\n"
"Uniqueid: %s\r\n",
chan->name, ast_state2str(chan->_state),
chan->cid.cid_num ? chan->cid.cid_num : "<unknown>",
chan->cid.cid_name ? chan->cid.cid_name : "<unknown>",
chan->uniqueid);
Mark Spencer
committed
struct ast_channel *ast_bridged_channel(struct ast_channel *chan)
{
struct ast_channel *bridged;
bridged = chan->_bridge;
if (bridged && bridged->tech->bridged_channel)
bridged = bridged->tech->bridged_channel(chan, bridged);
Mark Spencer
committed
return bridged;
}
static void bridge_playfile(struct ast_channel *chan, struct ast_channel *peer, const char *sound, int remain)
Anthony Minessale II
committed
check = ast_autoservice_start(peer);
Anthony Minessale II
committed
return;
if (remain > 0) {
if (remain / 60 > 1) {
min = remain / 60;
sec = remain % 60;
sec = remain;
}
}
if (!strcmp(sound,"timeleft")) { /* Queue support */
ast_streamfile(chan, "vm-youhave", chan->language);
ast_waitstream(chan, "");
ast_say_number(chan, min, AST_DIGIT_ANY, chan->language, (char *) NULL);
ast_streamfile(chan, "queue-minutes", chan->language);
ast_waitstream(chan, "");
ast_say_number(chan, sec, AST_DIGIT_ANY, chan->language, (char *) NULL);
ast_streamfile(chan, "queue-seconds", chan->language);
ast_waitstream(chan, "");
ast_streamfile(chan, sound, chan->language);
ast_waitstream(chan, "");
Anthony Minessale II
committed
check = 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, struct timeval bridge_end)
/* 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;
int o0nativeformats;
int o1nativeformats;
Kevin P. Fleming
committed
void *pvt0, *pvt1;
Kevin P. Fleming
committed
pvt0 = c0->tech_pvt;
pvt1 = c1->tech_pvt;
o0nativeformats = c0->nativeformats;
o1nativeformats = c1->nativeformats;
watch_c0_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_0;
watch_c1_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_1;
Kevin P. Fleming
committed
if ((c0->tech_pvt != pvt0) || (c1->tech_pvt != pvt1) ||
(o0nativeformats != c0->nativeformats) ||
(o1nativeformats != c1->nativeformats)) {
/* Check for Masquerade, codec changes, etc */
if (bridge_end.tv_sec) {
to = ast_tvdiff_ms(bridge_end, ast_tvnow());
if (to <= 0) {
res = AST_BRIDGE_RETRY;
break;
}
} else
to = -1;
who = ast_waitfor_n(cs, 2, &to);
if (!who) {
ast_log(LOG_DEBUG, "Nobody there, continuing...\n");
if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
Mark Spencer
committed
c0->_softhangup = 0;
if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
c1->_softhangup = 0;
c0->_bridge = c1;
c1->_bridge = c0;
}
continue;
}
f = ast_read(who);
if (!f) {
*fo = NULL;
*rc = who;
ast_log(LOG_DEBUG, "Didn't get a frame from channel: %s\n",who->name);
break;
}
if ((f->frametype == AST_FRAME_CONTROL) && !(config->flags & AST_BRIDGE_IGNORE_SIGS)) {
if ((f->subclass == AST_CONTROL_HOLD) || (f->subclass == AST_CONTROL_UNHOLD) ||
(f->subclass == AST_CONTROL_VIDUPDATE)) {
ast_indicate(who == c0 ? c1 : c0, f->subclass);
} else {
*fo = f;
*rc = who;
ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", f->subclass, who->name);
break;
}
}
if ((f->frametype == AST_FRAME_VOICE) ||
(f->frametype == AST_FRAME_DTMF) ||
(f->frametype == AST_FRAME_VIDEO) ||
(f->frametype == AST_FRAME_IMAGE) ||
(f->frametype == AST_FRAME_HTML) ||
#if defined(T38_SUPPORT)
(f->frametype == AST_FRAME_MODEM) ||
#endif
(f->frametype == AST_FRAME_TEXT)) {
if (f->frametype == AST_FRAME_DTMF) {
if (((who == c0) && watch_c0_dtmf) ||
((who == c1) && watch_c1_dtmf)) {
*rc = who;
*fo = f;
res = AST_BRIDGE_COMPLETE;
ast_log(LOG_DEBUG, "Got DTMF on channel (%s)\n", who->name);
break;
} else {
goto tackygoto;
}
} else {
#if 0
ast_log(LOG_DEBUG, "Read from %s\n", who->name);
if (who == last)
ast_log(LOG_DEBUG, "Servicing channel %s twice in a row?\n", last->name);
last = who;
#endif
tackygoto:
}
}
ast_frfree(f);
/* Swap who gets priority */
cs[2] = cs[0];
cs[0] = cs[1];
cs[1] = cs[2];
}
return res;
}
/*! \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 = AST_BRIDGE_COMPLETE;
int o0nativeformats;
int o1nativeformats;
long time_left_ms=0;
struct timeval nexteventts = { 0, };
char caller_warning = 0;
char callee_warning = 0;
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);
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
/* 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))
return -1;
*fo = NULL;
firstpass = config->firstpass;
config->firstpass = 0;
if (ast_tvzero(config->start_time))
config->start_time = ast_tvnow();
time_left_ms = config->timelimit;
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 (config->start_sound && firstpass) {
if (caller_warning)
bridge_playfile(c0, c1, config->start_sound, time_left_ms / 1000);
if (callee_warning)
bridge_playfile(c1, c0, config->start_sound, time_left_ms / 1000);
}
Mark Spencer
committed
c0->_bridge = c1;
c1->_bridge = c0;
"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;
if (config->timelimit) {
nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
if (caller_warning || callee_warning)
nexteventts = ast_tvsub(nexteventts, ast_samp2tv(config->play_warning, 1000));
}
struct timeval now;
now = ast_tvnow();
to = ast_tvdiff_ms(nexteventts, now);
if (to < 0)
to = 0;
time_left_ms = config->timelimit - ast_tvdiff_ms(now, config->start_time);
if (time_left_ms < to)
to = time_left_ms;
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);
if (who)
*rc = who;
break;
}
if (!to) {
if (time_left_ms >= 5000) {
/* force the time left to round up if appropriate */
if (caller_warning && config->warning_sound && config->play_warning)
bridge_playfile(c0, c1, config->warning_sound,
(time_left_ms + 500) / 1000);
if (callee_warning && config->warning_sound && config->play_warning)
bridge_playfile(c1, c0, config->warning_sound,
(time_left_ms + 500) / 1000);
}
if (config->warning_freq) {
nexteventts = ast_tvadd(nexteventts, ast_samp2tv(config->warning_freq, 1000));
} else
nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
}
}
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) &&
Kevin P. Fleming
committed
!nativefailed && !c0->monitor && !c1->monitor &&
!c0->spies && !c1->spies) {
/* Looks like they share a bridge method and nothing else is in the way */
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, to)) == AST_BRIDGE_COMPLETE) {
"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);
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 = NULL;
c1->_bridge = NULL;
return res;
} else {
ast_clear_flag(c0, AST_FLAG_NBRIDGE);
ast_clear_flag(c1, AST_FLAG_NBRIDGE);
Kevin P. Fleming
committed
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s ended\n",
Kevin P. Fleming
committed
c0->name, c1->name);
/* fallthrough */
case AST_BRIDGE_FAILED_NOWARN:
if (((c0->writeformat != c1->readformat) || (c0->readformat != c1->writeformat) ||
(c0->nativeformats != o0nativeformats) || (c1->nativeformats != o1nativeformats)) &&
!(c0->generator || c1->generator)) {
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);
return AST_BRIDGE_FAILED;
o0nativeformats = c0->nativeformats;
o1nativeformats = c1->nativeformats;
res = ast_generic_bridge(c0, c1, config, fo, rc, nexteventts);
Mark Spencer
committed
c0->_bridge = NULL;
c1->_bridge = NULL;
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);
ast_log(LOG_DEBUG, "Bridge stops bridging channels %s and %s\n", c0->name, c1->name);
/*! \brief 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;
}
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
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;
if (!ts)
return NULL;
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, };
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
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;
c = copy = ast_strdupa(s);
if (!copy)
Russell Bryant
committed
return 0;
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 *, const 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 *, const char *),
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;
/*! \brief Turn on music on hold on a given channel */
int ast_moh_start(struct ast_channel *chan, const 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;
}
/*! \brief Turn off music on hold on a given channel */
void ast_moh_stop(struct ast_channel *chan)
{
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);
Mark Spencer
committed
}
void ast_channels_init(void)
{
ast_cli_register(&cli_show_channeltypes);
ast_cli_register(&cli_show_channeltype);
}
/*! \brief 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 */
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);
}
}
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);
}
Kevin P. Fleming
committed
static void copy_data_from_queue(struct ast_channel_spy_queue *queue, short *buf, unsigned int samples)
{
struct ast_frame *f;
int tocopy;
int bytestocopy;
while (samples) {
f = queue->head;
if (!f) {
ast_log(LOG_ERROR, "Ran out of frames before buffer filled!\n");
break;
}
tocopy = (f->samples > samples) ? samples : f->samples;
bytestocopy = ast_codec_get_len(queue->format, tocopy);
Kevin P. Fleming
committed
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
memcpy(buf, f->data, bytestocopy);
samples -= tocopy;
buf += tocopy;
f->samples -= tocopy;
f->data += bytestocopy;
f->datalen -= bytestocopy;
f->offset += bytestocopy;
queue->samples -= tocopy;
if (!f->samples) {
queue->head = f->next;
ast_frfree(f);
}
}
}
struct ast_frame *ast_channel_spy_read_frame(struct ast_channel_spy *spy, unsigned int samples)
{
struct ast_frame *result;
/* buffers are allocated to hold SLINEAR, which is the largest format */
short read_buf[samples];
short write_buf[samples];
struct ast_frame *read_frame;
struct ast_frame *write_frame;
int need_dup;
struct ast_frame stack_read_frame = { .frametype = AST_FRAME_VOICE,
.subclass = spy->read_queue.format,
.data = read_buf,
.samples = samples,
.datalen = ast_codec_get_len(spy->read_queue.format, samples),
};
struct ast_frame stack_write_frame = { .frametype = AST_FRAME_VOICE,
.subclass = spy->write_queue.format,
.data = write_buf,
.samples = samples,
.datalen = ast_codec_get_len(spy->write_queue.format, samples),
};
/* if a flush has been requested, dump everything in whichever queue is larger */
if (ast_test_flag(spy, CHANSPY_TRIGGER_FLUSH)) {
if (spy->read_queue.samples > spy->write_queue.samples) {
if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST)) {
for (result = spy->read_queue.head; result; result = result->next)
ast_frame_adjust_volume(result, spy->read_vol_adjustment);
}
result = spy->read_queue.head;
spy->read_queue.head = NULL;
spy->read_queue.samples = 0;
ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
return result;
} else {
if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST)) {
for (result = spy->write_queue.head; result; result = result->next)
ast_frame_adjust_volume(result, spy->write_vol_adjustment);
}
result = spy->write_queue.head;
spy->write_queue.head = NULL;
spy->write_queue.samples = 0;
ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
return result;
}
}
if ((spy->read_queue.samples < samples) || (spy->write_queue.samples < samples))
return NULL;
/* short-circuit if both head frames have exactly what we want */
if ((spy->read_queue.head->samples == samples) &&
(spy->write_queue.head->samples == samples)) {
read_frame = spy->read_queue.head;
spy->read_queue.head = read_frame->next;
read_frame->next = NULL;
write_frame = spy->write_queue.head;
spy->write_queue.head = write_frame->next;
write_frame->next = NULL;
spy->read_queue.samples -= samples;
spy->write_queue.samples -= samples;
need_dup = 0;
} else {
copy_data_from_queue(&spy->read_queue, read_buf, samples);
copy_data_from_queue(&spy->write_queue, write_buf, samples);
read_frame = &stack_read_frame;
write_frame = &stack_write_frame;
need_dup = 1;
}
if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST))
ast_frame_adjust_volume(read_frame, spy->read_vol_adjustment);
if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST))
ast_frame_adjust_volume(write_frame, spy->write_vol_adjustment);
if (ast_test_flag(spy, CHANSPY_MIXAUDIO)) {
ast_frame_slinear_sum(read_frame, write_frame);
if (need_dup)
result = ast_frdup(read_frame);
Kevin P. Fleming
committed
else {
Kevin P. Fleming
committed
result = read_frame;
Kevin P. Fleming
committed
ast_frfree(write_frame);
}
Kevin P. Fleming
committed
} else {
if (need_dup) {
result = ast_frdup(read_frame);
result->next = ast_frdup(write_frame);
} else {
result = read_frame;
result->next = write_frame;
}
}
return result;
}
static void *silence_generator_alloc(struct ast_channel *chan, void *data)
{
/* just store the data pointer in the channel structure */
return data;
}