Newer
Older
f = &ast_null_frame;
Mark Spencer
committed
} else {
/*
* Mark when outgoing channel answered so we can know how
* long the channel has been up.
*/
set_channel_answer_time(chan);
Mark Spencer
committed
ast_setstate(chan, AST_STATE_UP);
Kevin P. Fleming
committed
}
} else if (f->subclass.integer == AST_CONTROL_READ_ACTION) {
read_action_payload = f->data.ptr;
switch (read_action_payload->action) {
case AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO:
ast_party_connected_line_init(&connected);
ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
if (ast_connected_line_parse_data(read_action_payload->payload,
read_action_payload->payload_size, &connected)) {
ast_party_connected_line_free(&connected);
ast_channel_unlock(chan);
if (ast_channel_connected_line_sub(NULL, chan, &connected, 0) &&
ast_channel_connected_line_macro(NULL, chan, &connected, 1, 0)) {
ast_indicate_data(chan, AST_CONTROL_CONNECTED_LINE,
read_action_payload->payload,
read_action_payload->payload_size);
ast_party_connected_line_free(&connected);
ast_channel_lock(chan);
break;
}
ast_frfree(f);
f = &ast_null_frame;
Kevin P. Fleming
committed
}
break;
case AST_FRAME_DTMF_END:
send_dtmf_end_event(chan, DTMF_RECEIVED, f->subclass.integer, f->len);
ast_log(LOG_DTMF, "DTMF end '%c' received on %s, duration %ld ms\n", f->subclass.integer, ast_channel_name(chan), f->len);
/* Queue it up if DTMF is deferred, or if DTMF emulation is forced. */
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF) || ast_test_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF)) {
f = &ast_null_frame;
} else if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF | AST_FLAG_END_DTMF_ONLY)) {
if (!ast_tvzero(*ast_channel_dtmf_tv(chan)) &&
ast_tvdiff_ms(ast_tvnow(), *ast_channel_dtmf_tv(chan)) < AST_MIN_DTMF_GAP) {
/* If it hasn't been long enough, defer this digit */
ast_frfree(f);
f = &ast_null_frame;
} else {
/* There was no begin, turn this into a begin and send the end later */
f->frametype = AST_FRAME_DTMF_BEGIN;
ast_set_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
ast_channel_dtmf_digit_to_emulate_set(chan, f->subclass.integer);
ast_channel_dtmf_tv_set(chan, &tv);
if (f->len > option_dtmfminduration)
ast_channel_emulate_dtmf_duration_set(chan, f->len);
ast_channel_emulate_dtmf_duration_set(chan, option_dtmfminduration);
ast_channel_emulate_dtmf_duration_set(chan, AST_DEFAULT_EMULATE_DTMF_DURATION);
ast_log(LOG_DTMF, "DTMF begin emulation of '%c' with duration %u queued on %s\n", f->subclass.integer, ast_channel_emulate_dtmf_duration(chan), ast_channel_name(chan));
if (ast_channel_audiohooks(chan)) {
Joshua Colp
committed
struct ast_frame *old_frame = f;
/*!
* \todo XXX It is possible to write a digit to the audiohook twice
* if the digit was originally read while the channel was in autoservice. */
f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
Joshua Colp
committed
if (old_frame != f)
ast_frfree(old_frame);
}
struct timeval now = ast_tvnow();
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF)) {
ast_log(LOG_DTMF, "DTMF end accepted with begin '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF);
f->len = ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan));
/* detect tones that were received on
* the wire with durations shorter than
* option_dtmfminduration and set f->len
* to the actual duration of the DTMF
* frames on the wire. This will cause
* dtmf emulation to be triggered later
* on.
*/
if (ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan)) < option_dtmfminduration) {
f->len = ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan));
ast_log(LOG_DTMF, "DTMF end '%c' detected to have actual duration %ld on the wire, emulation will be triggered on %s\n", f->subclass.integer, f->len, ast_channel_name(chan));
ast_log(LOG_DTMF, "DTMF end accepted without begin '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));
f->len = option_dtmfminduration;
if (f->len < option_dtmfminduration && !ast_test_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY)) {
ast_log(LOG_DTMF, "DTMF end '%c' has duration %ld but want minimum %u, emulating on %s\n", f->subclass.integer, f->len, option_dtmfminduration, ast_channel_name(chan));
ast_set_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
ast_channel_dtmf_digit_to_emulate_set(chan, f->subclass.integer);
ast_channel_emulate_dtmf_duration_set(chan, option_dtmfminduration - f->len);
ast_log(LOG_DTMF, "DTMF end passthrough '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));
if (f->len < option_dtmfminduration) {
f->len = option_dtmfminduration;
ast_channel_dtmf_tv_set(chan, &now);
if (ast_channel_audiohooks(chan)) {
Joshua Colp
committed
struct ast_frame *old_frame = f;
f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
Joshua Colp
committed
if (old_frame != f)
ast_frfree(old_frame);
}
Kevin P. Fleming
committed
break;
case AST_FRAME_DTMF_BEGIN:
send_dtmf_begin_event(chan, DTMF_RECEIVED, f->subclass.integer);
ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass.integer, ast_channel_name(chan));
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF | AST_FLAG_END_DTMF_ONLY | AST_FLAG_EMULATE_DTMF) ||
(!ast_tvzero(*ast_channel_dtmf_tv(chan)) &&
ast_tvdiff_ms(ast_tvnow(), *ast_channel_dtmf_tv(chan)) < AST_MIN_DTMF_GAP) ) {
ast_log(LOG_DTMF, "DTMF begin ignored '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));
ast_frfree(f);
f = &ast_null_frame;
struct timeval now = ast_tvnow();
ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF);
ast_channel_dtmf_tv_set(chan, &now);
ast_log(LOG_DTMF, "DTMF begin passthrough '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));
Kevin P. Fleming
committed
break;
/* The EMULATE_DTMF flag must be cleared here as opposed to when the duration
* is reached , because we want to make sure we pass at least one
* voice frame through before starting the next digit, to ensure a gap
* between DTMF digits. */
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF)) {
struct timeval now = ast_tvnow();
if (!ast_channel_emulate_dtmf_duration(chan)) {
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
ast_channel_dtmf_digit_to_emulate_set(chan, 0);
} else if (ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan)) >= ast_channel_emulate_dtmf_duration(chan)) {
ast_channel_emulate_dtmf_duration_set(chan, 0);
f->frametype = AST_FRAME_DTMF_END;
f->subclass.integer = ast_channel_dtmf_digit_to_emulate(chan);
f->len = ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan));
ast_channel_dtmf_tv_set(chan, &now);
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
ast_channel_dtmf_digit_to_emulate_set(chan, 0);
ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass.integer, ast_channel_name(chan));
if (ast_channel_audiohooks(chan)) {
struct ast_frame *old_frame = f;
f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
if (old_frame != f) {
ast_frfree(old_frame);
}
}
Kevin P. Fleming
committed
case AST_FRAME_VOICE:
/* The EMULATE_DTMF flag must be cleared here as opposed to when the duration
* is reached , because we want to make sure we pass at least one
* voice frame through before starting the next digit, to ensure a gap
* between DTMF digits. */
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF) && !ast_channel_emulate_dtmf_duration(chan)) {
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
ast_channel_dtmf_digit_to_emulate_set(chan, 0);
}
if (dropaudio || ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF)) {
if (dropaudio)
ast_read_generator_actions(chan, f);
Kevin P. Fleming
committed
ast_frfree(f);
f = &ast_null_frame;
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF) && !ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF)) {
struct timeval now = ast_tvnow();
if (ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan)) >= ast_channel_emulate_dtmf_duration(chan)) {
ast_channel_emulate_dtmf_duration_set(chan, 0);
f->frametype = AST_FRAME_DTMF_END;
f->subclass.integer = ast_channel_dtmf_digit_to_emulate(chan);
f->len = ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan));
ast_channel_dtmf_tv_set(chan, &now);
if (ast_channel_audiohooks(chan)) {
Joshua Colp
committed
struct ast_frame *old_frame = f;
f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
Joshua Colp
committed
if (old_frame != f)
ast_frfree(old_frame);
}
ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass.integer, ast_channel_name(chan));
} else {
/* Drop voice frames while we're still in the middle of the digit */
ast_frfree(f);
f = &ast_null_frame;
}
Richard Mudgett
committed
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
break;
}
if (f->frametype != AST_FRAME_VOICE) {
break;
}
if (ast_format_cmp(f->subclass.format, ast_channel_rawreadformat(chan)) != AST_FORMAT_CMP_EQUAL
&& ast_format_cmp(f->subclass.format, ast_channel_readformat(chan)) != AST_FORMAT_CMP_EQUAL) {
struct ast_format *core_format;
/*
* Note: This frame may not be one of the current native
* formats. We may have gotten it out of the read queue from
* a previous multi-frame translation, from a framehook
* injected frame, or the device we're talking to isn't
* respecting negotiated formats. Regardless we will accept
* all frames.
*
* Update the read translation path to handle the new format
* that just came in. If the core wants slinear we need to
* setup a new translation path because the core is usually
* doing something with the audio itself and may not handle
* any other format. e.g., Softmix bridge, holding bridge
* announcer channel, recording, AMD... Otherwise, we'll
* setup to pass the frame as is to the core. In this case
* the core doesn't care. The channel is likely in
* autoservice, safesleep, or the channel is in a bridge.
* Let the bridge technology deal with format compatibility
* between the channels in the bridge.
*
* Beware of the transcode_via_slin and genericplc options as
* they force any transcoding to go through slin on a bridge.
* Unfortunately transcode_via_slin is enabled by default and
* genericplc is enabled in the codecs.conf.sample file.
*
* XXX Only updating translation to slinear frames has some
* corner cases if slinear is one of the native formats and
* there are different sample rates involved. We might wind
* up with conflicting translation paths between channels
* where the read translation path on this channel reduces
* the sample rate followed by a write translation path on
* the peer channel that increases the sample rate.
*/
core_format = ast_channel_readformat(chan);
if (!ast_format_cache_is_slinear(core_format)) {
core_format = f->subclass.format;
}
if (ast_set_read_format_path(chan, f->subclass.format, core_format)) {
/* Drop frame. We couldn't make it compatible with the core. */
ast_frfree(f);
f = &ast_null_frame;
break;
Joshua Colp
committed
}
Richard Mudgett
committed
}
/*
* Send frame to audiohooks if present, if frametype is linear, to preserve
* functional compatibility with previous behavior. If not linear, hold off
* until transcoding is done where we are more likely to have a linear frame
*/
if (ast_channel_audiohooks(chan) && ast_format_cache_is_slinear(f->subclass.format)) {
/* Place hooked after declaration */
Richard Mudgett
committed
struct ast_frame *old_frame = f;
hooked = 1;
Richard Mudgett
committed
f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
if (old_frame != f) {
ast_frfree(old_frame);
}
}
Richard Mudgett
committed
if (ast_channel_monitor(chan) && ast_channel_monitor(chan)->read_stream) {
/* XXX what does this do ? */
Richard Mudgett
committed
int jump = ast_channel_outsmpl(chan) - ast_channel_insmpl(chan) - 4 * f->samples;
if (jump >= 0) {
jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
ast_format_get_sample_rate(f->subclass.format),
ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump, SEEK_FORCECUR) == -1) {
ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
Richard Mudgett
committed
ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + (ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)) + f->samples);
} else {
ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + f->samples);
}
Richard Mudgett
committed
int jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
ast_format_get_sample_rate(f->subclass.format),
Richard Mudgett
committed
ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
if (jump - MONITOR_DELAY >= 0) {
if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump - f->samples, SEEK_FORCECUR) == -1) {
ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
Kevin P. Fleming
committed
}
Richard Mudgett
committed
ast_channel_insmpl(chan) += ast_channel_outsmpl(chan) - ast_channel_insmpl(chan);
} else {
ast_channel_insmpl(chan) += f->samples;
Kevin P. Fleming
committed
}
Richard Mudgett
committed
#endif
if (ast_channel_monitor(chan)->state == AST_MONITOR_RUNNING) {
if (ast_writestream(ast_channel_monitor(chan)->read_stream, f) < 0)
ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
}
}
Kevin P. Fleming
committed
Richard Mudgett
committed
if (ast_channel_readtrans(chan)
&& ast_format_cmp(f->subclass.format, ast_channel_rawreadformat(chan)) == AST_FORMAT_CMP_EQUAL) {
f = ast_translate(ast_channel_readtrans(chan), f, 1);
if (!f) {
Richard Mudgett
committed
}
/* Second chance at hooking a linear frame, also the last chance */
if (ast_channel_audiohooks(chan) && !hooked) {
struct ast_frame *old_frame = f;
f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
if (old_frame != f) {
ast_frfree(old_frame);
}
}
Richard Mudgett
committed
/*
* It is possible for the translation process on the channel to have
* produced multiple frames from the single input frame we passed it; if
* this happens, queue the additional frames *before* the frames we may
* have queued earlier. if the readq was empty, put them at the head of
* the queue, and if it was not, put them just after the frame that was
* at the end of the queue.
*/
if (AST_LIST_NEXT(f, frame_list)) {
struct ast_frame *cur, *multi_frame = AST_LIST_NEXT(f, frame_list);
/* Mark these frames as being re-queued */
for (cur = multi_frame; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
ast_set_flag(cur, AST_FRFLAG_REQUEUED);
}
Richard Mudgett
committed
if (!readq_tail) {
ast_queue_frame_head(chan, multi_frame);
Richard Mudgett
committed
} else {
__ast_queue_frame(chan, multi_frame, 0, readq_tail);
ast_frfree(multi_frame);
Richard Mudgett
committed
AST_LIST_NEXT(f, frame_list) = NULL;
Richard Mudgett
committed
/*
* Run generator sitting on the line if timing device not available
* and synchronous generation of outgoing frames is necessary
*/
ast_read_generator_actions(chan, f);
default:
/* Just pass it on! */
break;
Kevin P. Fleming
committed
} else {
/* Make sure we always return NULL in the future */
if (!ast_channel_softhangup_internal_flag(chan)) {
ast_channel_softhangup_internal_flag_add(chan, AST_SOFTHANGUP_DEV);
if (cause)
ast_channel_hangupcause_set(chan, cause);
if (ast_channel_generator(chan))
/* We no longer End the CDR here */
Kevin P. Fleming
committed
/* High bit prints debugging */
if (ast_channel_fin(chan) & DEBUGCHAN_FLAG)
ast_frame_dump(ast_channel_name(chan), f, "<<");
ast_channel_fin_set(chan, FRAMECOUNT_INC(ast_channel_fin(chan)));
Kevin P. Fleming
committed
done:
if (ast_channel_music_state(chan) && ast_channel_generator(chan) && ast_channel_generator(chan)->digit && f && f->frametype == AST_FRAME_DTMF_END)
ast_channel_generator(chan)->digit(chan, f->subclass.integer);
if (ast_channel_audiohooks(chan) && ast_audiohook_write_list_empty(ast_channel_audiohooks(chan))) {
/* The list gets recreated if audiohooks are added again later */
ast_audiohook_detach_list(ast_channel_audiohooks(chan));
ast_channel_audiohooks_set(chan, NULL);
struct ast_frame *ast_read(struct ast_channel *chan)
{
return __ast_read(chan, 0);
}
struct ast_frame *ast_read_noaudio(struct ast_channel *chan)
{
return __ast_read(chan, 1);
}
int ast_indicate(struct ast_channel *chan, int condition)
Kevin P. Fleming
committed
{
return ast_indicate_data(chan, condition, NULL, 0);
}
static int attribute_const is_visible_indication(enum ast_control_frame_type condition)
/* Don't include a default case here so that we get compiler warnings
* when a new type is added. */
switch (condition) {
case AST_CONTROL_PROGRESS:
case AST_CONTROL_PROCEEDING:
case AST_CONTROL_VIDUPDATE:
case AST_CONTROL_SRCUPDATE:
case AST_CONTROL_SRCCHANGE:
case AST_CONTROL_RADIO_KEY:
case AST_CONTROL_RADIO_UNKEY:
case AST_CONTROL_OPTION:
case AST_CONTROL_WINK:
case AST_CONTROL_FLASH:
case AST_CONTROL_OFFHOOK:
case AST_CONTROL_TAKEOFFHOOK:
case AST_CONTROL_ANSWER:
case AST_CONTROL_HANGUP:
Mark Michelson
committed
case AST_CONTROL_CONNECTED_LINE:
case AST_CONTROL_REDIRECTING:
Joshua Colp
committed
case AST_CONTROL_TRANSFER:
Joshua Colp
committed
case AST_CONTROL_T38_PARAMETERS:
Kevin P. Fleming
committed
case _XXX_AST_CONTROL_T38:
case AST_CONTROL_READ_ACTION:
case AST_CONTROL_UPDATE_RTP_PEER:
case AST_CONTROL_PVT_CAUSE_CODE:
case AST_CONTROL_MASQUERADE_NOTIFY:
case AST_CONTROL_STREAM_STOP:
case AST_CONTROL_STREAM_SUSPEND:
case AST_CONTROL_STREAM_REVERSE:
case AST_CONTROL_STREAM_FORWARD:
case AST_CONTROL_STREAM_RESTART:
case AST_CONTROL_RECORD_CANCEL:
case AST_CONTROL_RECORD_STOP:
case AST_CONTROL_RECORD_SUSPEND:
case AST_CONTROL_RECORD_MUTE:
Mark Michelson
committed
break;
case AST_CONTROL_INCOMPLETE:
case AST_CONTROL_CONGESTION:
case AST_CONTROL_BUSY:
case AST_CONTROL_RINGING:
case AST_CONTROL_RING:
case AST_CONTROL_HOLD:
case AST_CONTROL_UNHOLD:
/* This is a special case. You stop hearing this. */
break;
}
return 0;
}
void ast_channel_hangupcause_hash_set(struct ast_channel *chan, const struct ast_control_pvt_cause_code *cause_code, int datalen)
{
char causevar[256];
if (ast_channel_dialed_causes_add(chan, cause_code, datalen)) {
ast_log(LOG_WARNING, "Unable to store hangup cause for %s on %s\n", cause_code->chan_name, ast_channel_name(chan));
}
if (cause_code->emulate_sip_cause) {
snprintf(causevar, sizeof(causevar), "HASH(SIP_CAUSE,%s)", cause_code->chan_name);
ast_func_write(chan, causevar, cause_code->code);
}
}
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
enum ama_flags ast_channel_string2amaflag(const char *flag)
{
if (!strcasecmp(flag, "default"))
return DEFAULT_AMA_FLAGS;
if (!strcasecmp(flag, "omit"))
return AST_AMA_OMIT;
if (!strcasecmp(flag, "billing"))
return AST_AMA_BILLING;
if (!strcasecmp(flag, "documentation"))
return AST_AMA_DOCUMENTATION;
return AST_AMA_NONE;
}
const char *ast_channel_amaflags2string(enum ama_flags flag)
{
switch (flag) {
case AST_AMA_OMIT:
return "OMIT";
case AST_AMA_BILLING:
return "BILLING";
case AST_AMA_DOCUMENTATION:
return "DOCUMENTATION";
default:
return "Unknown";
}
}
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
/*!
* \internal
* \brief Preprocess connected line update.
* \since 12.0.0
*
* \param chan channel to change the indication
* \param data pointer to payload data
* \param datalen size of payload data
*
* \note This function assumes chan is locked.
*
* \retval 0 keep going.
* \retval -1 quit now.
*/
static int indicate_connected_line(struct ast_channel *chan, const void *data, size_t datalen)
{
struct ast_party_connected_line *chan_connected = ast_channel_connected(chan);
struct ast_party_connected_line *chan_indicated = ast_channel_connected_indicated(chan);
struct ast_party_connected_line connected;
unsigned char current[1024];
unsigned char proposed[1024];
int current_size;
int proposed_size;
int res;
ast_party_connected_line_set_init(&connected, chan_connected);
res = ast_connected_line_parse_data(data, datalen, &connected);
if (!res) {
ast_channel_set_connected_line(chan, &connected, NULL);
}
ast_party_connected_line_free(&connected);
if (res) {
return -1;
}
current_size = ast_connected_line_build_data(current, sizeof(current),
chan_indicated, NULL);
proposed_size = ast_connected_line_build_data(proposed, sizeof(proposed),
chan_connected, NULL);
if (current_size == -1 || proposed_size == -1) {
return -1;
}
if (current_size == proposed_size && !memcmp(current, proposed, current_size)) {
ast_debug(1, "%s: Dropping redundant connected line update \"%s\" <%s>.\n",
ast_channel_name(chan),
S_COR(chan_connected->id.name.valid, chan_connected->id.name.str, ""),
S_COR(chan_connected->id.number.valid, chan_connected->id.number.str, ""));
return -1;
}
ast_party_connected_line_copy(chan_indicated, chan_connected);
return 0;
}
/*!
* \internal
* \brief Preprocess redirecting update.
* \since 12.0.0
*
* \param chan channel to change the indication
* \param data pointer to payload data
* \param datalen size of payload data
*
* \note This function assumes chan is locked.
*
* \retval 0 keep going.
* \retval -1 quit now.
*/
static int indicate_redirecting(struct ast_channel *chan, const void *data, size_t datalen)
{
struct ast_party_redirecting redirecting;
int res;
ast_party_redirecting_set_init(&redirecting, ast_channel_redirecting(chan));
res = ast_redirecting_parse_data(data, datalen, &redirecting);
if (!res) {
ast_channel_set_redirecting(chan, &redirecting, NULL);
}
ast_party_redirecting_free(&redirecting);
return res ? -1 : 0;
}
static int indicate_data_internal(struct ast_channel *chan, int _condition, const void *data, size_t datalen)
/* By using an enum, we'll get compiler warnings for values not handled
* in switch statements. */
enum ast_control_frame_type condition = _condition;
struct ast_tone_zone_sound *ts = NULL;
const struct ast_control_t38_parameters *t38_parameters;
Mark Michelson
committed
int res;
Mark Michelson
committed
switch (condition) {
case AST_CONTROL_CONNECTED_LINE:
if (indicate_connected_line(chan, data, datalen)) {
res = 0;
return res;
Mark Michelson
committed
}
break;
case AST_CONTROL_REDIRECTING:
if (indicate_redirecting(chan, data, datalen)) {
res = 0;
return res;
Mark Michelson
committed
}
break;
case AST_CONTROL_HOLD:
case AST_CONTROL_UNHOLD:
ast_channel_hold_state_set(chan, _condition);
break;
case AST_CONTROL_T38_PARAMETERS:
t38_parameters = data;
switch (t38_parameters->request_response) {
case AST_T38_REQUEST_NEGOTIATE:
case AST_T38_NEGOTIATED:
ast_channel_set_is_t38_active_nolock(chan, 1);
break;
case AST_T38_REQUEST_TERMINATE:
case AST_T38_TERMINATED:
case AST_T38_REFUSED:
ast_channel_set_is_t38_active_nolock(chan, 0);
break;
default:
break;
}
break;
Mark Michelson
committed
default:
break;
}
Kevin P. Fleming
committed
if (is_visible_indication(condition)) {
/* A new visible indication is requested. */
ast_channel_visible_indication_set(chan, _condition);
} else if (condition == AST_CONTROL_UNHOLD || _condition < 0) {
/* Visible indication is cleared/stopped. */
ast_channel_visible_indication_set(chan, 0);
if (ast_channel_tech(chan)->indicate) {
/* See if the channel driver can handle this condition. */
res = ast_channel_tech(chan)->indicate(chan, _condition, data, datalen);
Mark Michelson
committed
} else {
res = -1;
Kevin P. Fleming
committed
if (!res) {
/* The channel driver successfully handled this indication */
return res;
}
/* The channel driver does not support this indication, let's fake
* it by doing our own tone generation if applicable. */
/*!\note If we compare the enumeration type, which does not have any
* negative constants, the compiler may optimize this code away.
* Therefore, we must perform an integer comparison here. */
if (_condition < 0) {
/* Stop any tones that are playing */
ast_playtones_stop(chan);
return res;
}
/* Handle conditions that we have tones for. */
switch (condition) {
Kevin P. Fleming
committed
case _XXX_AST_CONTROL_T38:
/* deprecated T.38 control frame */
return res;
Kevin P. Fleming
committed
case AST_CONTROL_T38_PARAMETERS:
/* there is no way to provide 'default' behavior for these
* control frames, so we need to return failure, but there
* is also no value in the log message below being emitted
* since failure to handle these frames is not an 'error'
* so just return right now. in addition, we want to return
* whatever value the channel driver returned, in case it
* has some meaning.*/
return res;
ts = ast_get_indication_tone(ast_channel_zone(chan), "ring");
/* It is common practice for channel drivers to return -1 if trying
* to indicate ringing on a channel which is up. The idea is to let the
* core generate the ringing inband. However, we don't want the
* warning message about not being able to handle the specific indication
* to print nor do we want ast_indicate_data to return an "error" for this
* condition
*/
if (ast_channel_state(chan) == AST_STATE_UP) {
break;
case AST_CONTROL_BUSY:
ts = ast_get_indication_tone(ast_channel_zone(chan), "busy");
case AST_CONTROL_INCOMPLETE:
case AST_CONTROL_CONGESTION:
ts = ast_get_indication_tone(ast_channel_zone(chan), "congestion");
case AST_CONTROL_PVT_CAUSE_CODE:
ast_channel_hangupcause_hash_set(chan, data, datalen);
res = 0;
break;
case AST_CONTROL_PROGRESS:
case AST_CONTROL_PROCEEDING:
case AST_CONTROL_VIDUPDATE:
case AST_CONTROL_SRCUPDATE:
case AST_CONTROL_SRCCHANGE:
case AST_CONTROL_RADIO_KEY:
case AST_CONTROL_RADIO_UNKEY:
case AST_CONTROL_OPTION:
case AST_CONTROL_WINK:
case AST_CONTROL_FLASH:
case AST_CONTROL_OFFHOOK:
case AST_CONTROL_TAKEOFFHOOK:
case AST_CONTROL_ANSWER:
case AST_CONTROL_HANGUP:
case AST_CONTROL_RING:
case AST_CONTROL_HOLD:
case AST_CONTROL_UNHOLD:
Joshua Colp
committed
case AST_CONTROL_TRANSFER:
Mark Michelson
committed
case AST_CONTROL_CONNECTED_LINE:
case AST_CONTROL_REDIRECTING:
case AST_CONTROL_READ_ACTION:
case AST_CONTROL_MASQUERADE_NOTIFY:
case AST_CONTROL_UPDATE_RTP_PEER:
case AST_CONTROL_STREAM_STOP:
case AST_CONTROL_STREAM_SUSPEND:
case AST_CONTROL_STREAM_REVERSE:
case AST_CONTROL_STREAM_FORWARD:
case AST_CONTROL_STREAM_RESTART:
case AST_CONTROL_RECORD_CANCEL:
case AST_CONTROL_RECORD_STOP:
case AST_CONTROL_RECORD_SUSPEND:
case AST_CONTROL_RECORD_MUTE:
/* Nothing left to do for these. */
res = 0;
break;
}
if (ts) {
/* We have a tone to play, yay. */
ast_debug(1, "Driver for channel '%s' does not support indication %u, emulating it\n", ast_channel_name(chan), condition);
res = ast_playtones_start(chan, 0, ts->data, 1);
if (!res) {
ast_test_suite_event_notify("RINGING_INBAND",
"Channel: %s\r\n",
ast_channel_name(chan));
}
ts = ast_tone_zone_sound_unref(ts);
}
if (res) {
/* not handled */
ast_log(LOG_WARNING, "Unable to handle indication %u for '%s'\n", condition, ast_channel_name(chan));
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
return res;
}
int ast_indicate_data(struct ast_channel *chan, int _condition, const void *data, size_t datalen)
{
int res;
/* this frame is used by framehooks. if it is set, we must free it at the end of this function */
struct ast_frame *awesome_frame = NULL;
ast_channel_lock(chan);
/* Don't bother if the channel is about to go away, anyway. */
if ((ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
|| (ast_check_hangup(chan) && !ast_channel_is_leaving_bridge(chan)))
&& _condition != AST_CONTROL_MASQUERADE_NOTIFY) {
res = -1;
goto indicate_cleanup;
}
if (!ast_framehook_list_is_empty(ast_channel_framehooks(chan))) {
/* Do framehooks now, do it, go, go now */
struct ast_frame frame = {
.frametype = AST_FRAME_CONTROL,
.subclass.integer = _condition,
.data.ptr = (void *) data, /* this cast from const is only okay because we do the ast_frdup below */
.datalen = datalen
};
/* we have now committed to freeing this frame */
awesome_frame = ast_frdup(&frame);
/* who knows what we will get back! the anticipation is killing me. */
awesome_frame = ast_framehook_list_write_event(ast_channel_framehooks(chan),
awesome_frame);
if (!awesome_frame
|| awesome_frame->frametype != AST_FRAME_CONTROL) {
res = 0;
goto indicate_cleanup;
}
_condition = awesome_frame->subclass.integer;
data = awesome_frame->data.ptr;
datalen = awesome_frame->datalen;
}
res = indicate_data_internal(chan, _condition, data, datalen);
if (awesome_frame) {
ast_frfree(awesome_frame);
}
int ast_recvchar(struct ast_channel *chan, int timeout)
{
int c;
char *buf = ast_recvtext(chan, timeout);
if (buf == NULL)
return -1; /* error or timeout */
c = *(unsigned char *)buf;
Tilghman Lesher
committed
ast_free(buf);
char *ast_recvtext(struct ast_channel *chan, int timeout)
{
struct timeval start = ast_tvnow();
int ms;
while ((ms = ast_remaining_ms(start, timeout))) {
}
res = ast_waitfor(chan, ms);
if (res <= 0) {/* timeout or error */
break; /* no frame */
}
if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_HANGUP) {
ast_frfree(f);
break;
} else if (f->frametype == AST_FRAME_TEXT) { /* what we want */
Michiel van Baak
committed
buf = ast_strndup((char *) f->data.ptr, f->datalen); /* dup and break */
}
ast_frfree(f);
}
int ast_sendtext(struct ast_channel *chan, const char *text)
ast_channel_lock(chan);
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
if (ast_strlen_zero(text)) {
ast_channel_unlock(chan);
return 0;
}
if (ast_channel_tech(chan)->write_text && (ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_MEDIA_TYPE_TEXT))) {
f.frametype = AST_FRAME_TEXT;
f.src = "DIALPLAN";
f.mallocd = AST_MALLOCD_DATA;
f.datalen = strlen(text);
f.data.ptr = ast_strdup(text);
f.subclass.format = ast_format_t140;
if (f.data.ptr) {
res = ast_channel_tech(chan)->write_text(chan, &f);
ast_frfree(&f);
}
} else if (ast_channel_tech(chan)->send_text) {
res = ast_channel_tech(chan)->send_text(chan, text);
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING);
int ast_senddigit_begin(struct ast_channel *chan, char digit)
/* Device does not support DTMF tones, lets fake
* it by doing our own generation. */
static const char * const dtmf_tones[] = {
"941+1336", /* 0 */
"697+1209", /* 1 */
"697+1336", /* 2 */
"697+1477", /* 3 */
"770+1209", /* 4 */
"770+1336", /* 5 */
"770+1477", /* 6 */
"852+1209", /* 7 */
"852+1336", /* 8 */
"852+1477", /* 9 */
"697+1633", /* A */
"770+1633", /* B */
"852+1633", /* C */
"941+1633", /* D */
"941+1209", /* * */
"941+1477" /* # */
};
if (!ast_channel_tech(chan)->send_digit_begin)
ast_channel_lock(chan);
ast_channel_sending_dtmf_digit_set(chan, digit);
ast_channel_sending_dtmf_tv_set(chan, ast_tvnow());
ast_channel_unlock(chan);
if (!ast_channel_tech(chan)->send_digit_begin(chan, digit))
return 0;
if (digit >= '0' && digit <='9')
ast_playtones_start(chan, 0, dtmf_tones[digit-'0'], 0);
else if (digit >= 'A' && digit <= 'D')
ast_playtones_start(chan, 0, dtmf_tones[digit-'A'+10], 0);
else if (digit == '*')
ast_playtones_start(chan, 0, dtmf_tones[14], 0);
else if (digit == '#')
ast_playtones_start(chan, 0, dtmf_tones[15], 0);
else {
/* not handled */
ast_debug(1, "Unable to generate DTMF tone '%c' for '%s'\n", digit, ast_channel_name(chan));
return 0;
}
int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duration)
{
int res = -1;
if (ast_channel_tech(chan)->send_digit_end)
res = ast_channel_tech(chan)->send_digit_end(chan, digit, duration);
ast_channel_lock(chan);
if (ast_channel_sending_dtmf_digit(chan) == digit) {
ast_channel_sending_dtmf_digit_set(chan, 0);
}
ast_channel_unlock(chan);
if (res && ast_channel_generator(chan))
ast_playtones_stop(chan);
Joshua Colp
committed
int ast_senddigit(struct ast_channel *chan, char digit, unsigned int duration)
if (ast_channel_tech(chan)->send_digit_begin) {
ast_senddigit_begin(chan, digit);
Joshua Colp
committed
ast_safe_sleep(chan, (duration >= AST_DEFAULT_EMULATE_DTMF_DURATION ? duration : AST_DEFAULT_EMULATE_DTMF_DURATION));
Joshua Colp
committed
return ast_senddigit_end(chan, digit, (duration >= AST_DEFAULT_EMULATE_DTMF_DURATION ? duration : AST_DEFAULT_EMULATE_DTMF_DURATION));
int ast_prod(struct ast_channel *chan)
{
struct ast_frame a = { AST_FRAME_VOICE };
char nothing[128];
/* Send an empty audio frame to get things moving */
if (ast_channel_state(chan) != AST_STATE_UP) {
ast_debug(1, "Prodding channel '%s'\n", ast_channel_name(chan));