[svn-commits] russell: branch russell/chan_console r49091 - in
/team/russell/chan_console: ...
svn-commits at lists.digium.com
svn-commits at lists.digium.com
Mon Jan 1 11:38:31 MST 2007
Author: russell
Date: Mon Jan 1 12:38:29 2007
New Revision: 49091
URL: http://svn.digium.com/view/asterisk?view=rev&rev=49091
Log:
- Add support for the "autoanswer" option
- Add support for playing various sounds to the console for ringing, busy,
congestion, etc.
Modified:
team/russell/chan_console/channels/chan_console.c
team/russell/chan_console/configs/console.conf.sample
Modified: team/russell/chan_console/channels/chan_console.c
URL: http://svn.digium.com/view/asterisk/team/russell/chan_console/channels/chan_console.c?view=diff&rev=49091&r1=49090&r2=49091
==============================================================================
--- team/russell/chan_console/channels/chan_console.c (original)
+++ team/russell/chan_console/channels/chan_console.c Mon Jan 1 12:38:29 2007
@@ -77,7 +77,14 @@
/*! \brief Mono Output */
#define OUTPUT_CHANNELS 1
-#define CONFIG_FILE "console.conf"
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+static const char *config_file = "console.conf";
/*!
* Each sound is made of 'datalen' samples of sound, repeated as needed to
@@ -135,10 +142,14 @@
/*! The number of samples sent for the current sound, mod the total number
* of samples for the sound plus trailing silence. */
unsigned int sampsent;
+ /*! Running = 1, Not running = 0 */
+ unsigned int streamstate:1;
/*! On-hook = 0, Off-hook = 1 */
unsigned int hookstate:1;
/*! Unmuted = 0, Muted = 1 */
unsigned int muted:1;
+ /*! Automatically answer incoming calls */
+ unsigned int autoanswer:1;
/*! Ignore context in the console dial CLI command */
unsigned int overridecontext:1;
/*! The PortAudio callback is being executed */
@@ -204,6 +215,9 @@
{
PaError res;
+ if (pvt.streamstate)
+ return 0;
+
res = Pa_OpenDefaultStream(&pvt.stream, INPUT_CHANNELS, OUTPUT_CHANNELS,
paInt16, SAMPLE_RATE, NUM_SAMPLES, console_callback, NULL);
if (res != paNoError) {
@@ -219,6 +233,8 @@
return -1;
}
+ pvt.streamstate = 1;
+
return 0;
}
@@ -227,10 +243,14 @@
*/
static int stop_stream(void)
{
+ if (!pvt.streamstate)
+ return 0;
+
ast_mutex_lock(&pvt.lock);
Pa_AbortStream(pvt.stream);
Pa_CloseStream(pvt.stream);
pvt.stream = NULL;
+ pvt.streamstate = 0;
ast_cond_signal(&pvt.cond);
ast_mutex_unlock(&pvt.lock);
@@ -270,11 +290,14 @@
ast_jb_configure(chan, &global_jbconf);
- if (state != AST_STATE_DOWN && ast_pbx_start(chan)) {
- ast_log(LOG_WARNING, "Unable to start PBX on %s\n", chan->name);
- chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
- ast_hangup(chan);
- chan = NULL;
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(chan)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", chan->name);
+ chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ ast_hangup(chan);
+ chan = NULL;
+ } else
+ start_stream();
}
return chan;
@@ -323,14 +346,34 @@
return 0;
}
+static void set_cursound(struct console_pvt *pvt, int sound)
+{
+ unsigned int i = 0;
+
+ if (sound == -1) {
+ pvt->cursound = -1;
+ ast_verbose(" --- <(\"<) --- Sound stopped --- (>\")> ---\n");
+ return;
+ }
+
+ for (; sounds[i].ind != -1; i++) {
+ if (sounds[i].ind == sound)
+ pvt->cursound = i;
+ }
+
+ if (pvt->cursound > -1)
+ ast_verbose(" --- <(\"<) --- Sound playing should be '%s' --- (>\")> ---\n",
+ sounds[pvt->cursound].desc);
+ else
+ ast_verbose(" --- <(\"<) --- Unknown sound requested on the console! --- (>\")> ---\n");
+}
+
static int console_hangup(struct ast_channel *c)
{
- int res = 0;
-
ast_verbose(" --- <(\"<) --- Hangup on Console --- (>\")> ---\n");
pvt.hookstate = 0;
- pvt.cursound = -1;
+ set_cursound(&pvt, -1);
c->tech_pvt = NULL;
pvt.owner = NULL;
@@ -345,18 +388,18 @@
stop_stream();
- return res;
+ return 0;
}
static int console_answer(struct ast_channel *c)
{
- int res = 0;
+ int res;
ast_verbose(" --- <(\"<) --- Call from Console has been Answered --- (>\")> ---\n");
ast_mutex_lock(&pvt.lock);
ast_setstate(c, AST_STATE_UP);
- pvt.cursound = -1;
+ set_cursound(&pvt, -1);
res = start_stream();
ast_mutex_unlock(&pvt.lock);
@@ -373,19 +416,25 @@
static int console_call(struct ast_channel *c, char *dest, int timeout)
{
struct ast_frame f = { 0, };
- int res = 0;
ast_verbose(" --- <(\"<) --- Call to device '%s' on console from '%s' <%s> --- (>\")> ---\n",
dest, c->cid.cid_name, c->cid.cid_num);
- ast_verbose(" --- <(\"<) --- Auto-answered --- (>\")> --- \n");
- pvt.hookstate = 1;
- f.frametype = AST_FRAME_CONTROL;
- f.subclass = AST_CONTROL_ANSWER;
- ast_queue_frame(c, &f);
-
- res = start_stream();
-
- return res;
+
+ if (pvt.autoanswer) {
+ ast_verbose(" --- <(\"<) --- Auto-answered --- (>\")> --- \n");
+ pvt.hookstate = 1;
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_ANSWER;
+ ast_queue_frame(c, &f);
+ } else {
+ ast_verbose(" --- <(\"<) --- Type 'answer' to answer, or use 'autoanswer' for future calls --- (>\")> ---\n");
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_RINGING;
+ ast_queue_frame(c, &f);
+ set_cursound(&pvt, AST_CONTROL_RING);
+ }
+
+ return start_stream();
}
static int console_write(struct ast_channel *chan, struct ast_frame *f)
@@ -404,18 +453,21 @@
static int console_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
{
+ struct console_pvt *pvt = chan->tech_pvt;
+
switch (cond) {
case AST_CONTROL_BUSY:
case AST_CONTROL_CONGESTION:
case AST_CONTROL_RINGING:
- /* \todo Play appropriate sound to console */
+ set_cursound(pvt, cond);
break;
case AST_CONTROL_VIDUPDATE:
case -1:
+ set_cursound(pvt, -1);
break;
case AST_CONTROL_HOLD:
ast_verbose(" --- <(\"<) --- Console Has Been Placed on Hold --- (>\")> --- \n");
- ast_moh_start(chan, data, pvt.mohinterpret);
+ ast_moh_start(chan, data, pvt->mohinterpret);
break;
case AST_CONTROL_UNHOLD:
ast_verbose(" --- <(\"<) --- Console Has Been Retrieved from Hold --- (>\")> --- \n");
@@ -438,60 +490,121 @@
return 0;
}
-static int handle_output(void *output, unsigned long frame_count)
+/*!
+ * \brief Play the current sound
+ *
+ * Builds a frame from the high level description of the sounds,
+ * and passes it to the audio device.
+ * The actual sound is made of 1 or more sequences of sound samples
+ * (s->datalen, repeated to make s->samplen samples) followed by
+ * s->silencelen samples of silence. The position in the sequence is stored
+ * in pvt->sampsent, which goes between 0 .. s->samplen+s->silencelen.
+ * In case we fail to write a frame, don't update pvt->sampsent.
+ */
+static void send_sound(struct console_pvt *pvt, void *output, unsigned long frame_count)
+{
+ int ofs, l, start;
+ int l_sampsent = pvt->sampsent;
+ struct sound *s;
+
+ if (pvt->cursound < 0) /* no sound to send */
+ return;
+
+ s = &sounds[pvt->cursound];
+
+ for (ofs = 0; ofs < frame_count; ofs += l) {
+ l = s->samplen - l_sampsent; /* # of available samples */
+ if (l > 0) {
+ start = l_sampsent % s->datalen; /* source offset */
+ l = MIN(l, frame_count - ofs); /* don't overflow the frame */
+ l = MIN(l, s->datalen - start); /* don't overflow the source */
+ bcopy(s->data + start, output + ofs, l * 2);
+ if (0)
+ ast_log(LOG_WARNING, "send_sound sound %d/%d of %d into %d\n", l_sampsent, l, s->samplen, ofs);
+ l_sampsent += l;
+ } else { /* end of samples, maybe some silence */
+ static const short silence[NUM_SAMPLES] = { 0, };
+
+ l += s->silencelen;
+ if (l > 0) {
+ l = MIN(l, frame_count - ofs);
+ bcopy(silence, output + ofs, l * 2);
+ l_sampsent += l;
+ } else { /* silence is over, restart sound if loop */
+ if (s->repeat == 0) { /* last block */
+ pvt->cursound = -1;
+ if (ofs < frame_count) /* pad with silence */
+ bcopy(silence, output + ofs, (frame_count - ofs) * 2);
+ }
+ l_sampsent = 0;
+ }
+ }
+ }
+ if (l > 0)
+ pvt->sampsent = l_sampsent; /* update status */
+}
+static int handle_output(struct console_pvt *pvt, void *output, unsigned long frame_count)
{
unsigned int src = 0;
unsigned int num_samples;
struct ast_frame *fr;
- ast_mutex_lock(&pvt.lock);
- if (pvt.write_dst) {
- num_samples = (pvt.write_dst * 2) > frame_count ? pvt.write_dst * 2 : frame_count;
- memcpy(output, &pvt.write_buf, num_samples * 2);
- pvt.write_dst -= num_samples * 2;
+ ast_mutex_lock(&pvt->lock);
+ /* Fill the buffer with the current sound if one is set */
+ if (pvt->cursound != -1) {
+ send_sound(pvt, output, frame_count);
+ frame_count = 0;
+ }
+ /* Empty the write buffer */
+ if (frame_count && pvt->write_dst) {
+ num_samples = MIN(pvt->write_dst * 2, frame_count);
+ memcpy(output, pvt->write_buf, num_samples * 2);
+ pvt->write_dst -= num_samples * 2;
src += num_samples * 2;
frame_count -= num_samples;
}
+ /* Get the rest from queued frames */
while (frame_count) {
- while (!(fr = AST_LIST_REMOVE_HEAD(&pvt.frames, frame_list))) {
- ast_cond_wait(&pvt.cond, &pvt.lock);
- if (!pvt.hookstate) {
+ while (!(fr = AST_LIST_REMOVE_HEAD(&pvt->frames, frame_list))) {
+ ast_cond_wait(&pvt->cond, &pvt->lock);
+ if (!pvt->hookstate) {
if (fr)
ast_frfree(fr);
- ast_mutex_unlock(&pvt.lock);
- pvt.incallback = 0;
+ ast_mutex_unlock(&pvt->lock);
+ pvt->incallback = 0;
return paAbort;
}
}
- num_samples = fr->samples > frame_count ? frame_count : fr->samples;
+ num_samples = MIN(fr->samples, frame_count);
memcpy(output + src, fr->data, num_samples * 2);
frame_count -= num_samples;
+ /* Write what is left over to the write buffer */
if (num_samples != fr->samples) {
- memcpy(&pvt.write_buf + pvt.write_dst, fr->data + num_samples * 2, (fr->samples - num_samples) * 2);
- pvt.write_dst += (fr->samples - num_samples) * 2;
+ memcpy(&pvt->write_buf + pvt->write_dst, fr->data + num_samples * 2, (fr->samples - num_samples) * 2);
+ pvt->write_dst += (fr->samples - num_samples) * 2;
}
ast_frfree(fr);
}
- ast_mutex_unlock(&pvt.lock);
+ ast_mutex_unlock(&pvt->lock);
return paContinue;
}
-static int handle_input(const void *input, unsigned long frame_count)
-{
- struct ast_frame *fr = &pvt.fr;
+static int handle_input(struct console_pvt *pvt, const void *input, unsigned long frame_count)
+{
+ struct ast_frame *fr = &pvt->fr;
unsigned int count = 0;
memset(fr, 0, sizeof(*fr));
- memset(pvt.read_buf, 0, NUM_SAMPLES * 2 + AST_FRIENDLY_OFFSET);
- fr->data = pvt.read_buf + AST_FRIENDLY_OFFSET;
+ memset(pvt->read_buf, 0, frame_count * 2 + AST_FRIENDLY_OFFSET);
+ fr->data = pvt->read_buf + AST_FRIENDLY_OFFSET;
fr->offset = AST_FRIENDLY_OFFSET;
fr->frametype = AST_FRAME_VOICE;
fr->subclass = AST_FORMAT_SLINEAR;
- fr->samples = NUM_SAMPLES;
- fr->datalen = NUM_SAMPLES * 2;
- if (!pvt.muted) /* If muted, just leave the data all zeros */
- memcpy(fr->data, input, NUM_SAMPLES * 2);
+ fr->samples = frame_count;
+ fr->datalen = frame_count * 2;
+ if (!pvt->muted) /* If muted, just leave the data all zeros */
+ memcpy(fr->data, input, frame_count * 2);
/* If the channel is in the process of being hung up, then it will be
* locked while calling the console_hangup callback, and that could be
@@ -499,16 +612,16 @@
* function returns. Thus, locking the channel could result in a
* deadlock. So, we use the fact that the locks are recursive to test
* locking the channel before queueing the frame. */
- while (ast_channel_trylock(pvt.owner)) {
+ while (ast_channel_trylock(pvt->owner)) {
if (++count == 100) {
ast_log(LOG_DEBUG, "Dropping frame\n");
- pvt.incallback = 0;
+ pvt->incallback = 0;
return paContinue;
}
usleep(10);
}
- ast_queue_frame(pvt.owner, fr);
- ast_channel_unlock(pvt.owner);
+ ast_queue_frame(pvt->owner, fr);
+ ast_channel_unlock(pvt->owner);
return paContinue;
}
@@ -518,15 +631,16 @@
PaStreamCallbackFlags flags, void *userData)
{
int res = paContinue;
-
- pvt.incallback = 1;
+ struct console_pvt *p = &pvt;
+
+ p->incallback = 1;
if (output)
- res = handle_output(output, frame_count);
+ res = handle_output(p, output, frame_count);
if (res != paAbort && input)
- res = handle_input(input, frame_count);
-
- pvt.incallback = 0;
+ res = handle_input(p, input, frame_count);
+
+ p->incallback = 0;
return res;
}
@@ -632,7 +746,7 @@
return CLI_FAILURE;
}
pvt.hookstate = 0;
- pvt.cursound = -1;
+ set_cursound(&pvt, -1);
if (pvt.owner)
ast_queue_hangup(pvt.owner);
@@ -734,9 +848,10 @@
pvt.cid_num[0] = '\0';
pvt.cid_name[0] = '\0';
pvt.overridecontext = 0;
-
- if (!(cfg = ast_config_load(CONFIG_FILE))) {
- ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", CONFIG_FILE);
+ pvt.autoanswer = 0;
+
+ if (!(cfg = ast_config_load(config_file))) {
+ ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", config_file);
return -1;
}
v = ast_variable_browse(cfg, "general");
@@ -749,17 +864,18 @@
ast_copy_string(pvt.exten, v->value, sizeof(pvt.exten));
else if (!strcasecmp(v->name, "mohinterpret"))
ast_copy_string(pvt.mohinterpret, v->value, sizeof(pvt.mohinterpret));
+ else if (!strcasecmp(v->name, "language"))
+ ast_copy_string(pvt.language, v->value, sizeof(pvt.language));
+ else if (!strcasecmp(v->name, "callerid"))
+ ast_callerid_split(v->value, pvt.cid_name, sizeof(pvt.cid_name),
+ pvt.cid_num, sizeof(pvt.cid_num));
else if (!strcasecmp(v->name, "overridecontext"))
pvt.overridecontext = ast_true(v->value) ? 1 : 0;
- else if (!strcasecmp(v->name, "language"))
- ast_copy_string(pvt.language, v->value, sizeof(pvt.language));
- else if (!strcasecmp(v->name, "callerid")) {
- ast_callerid_split(v->value, pvt.cid_name, sizeof(pvt.cid_name),
- pvt.cid_num, sizeof(pvt.cid_num));
- } else {
+ else if (!strcasecmp(v->name, "autoanswer"))
+ pvt.autoanswer = ast_true(v->value) ? 1 : 0;
+ else
ast_log(LOG_WARNING, "Unknown option '%s' on line '%d' of '%s'!\n",
- v->name, v->lineno, CONFIG_FILE);
- }
+ v->name, v->lineno, config_file);
}
ast_config_destroy(cfg);
Modified: team/russell/chan_console/configs/console.conf.sample
URL: http://svn.digium.com/view/asterisk/team/russell/chan_console/configs/console.conf.sample?view=diff&rev=49091&r1=49090&r2=49091
==============================================================================
--- team/russell/chan_console/configs/console.conf.sample (original)
+++ team/russell/chan_console/configs/console.conf.sample Mon Jan 1 12:38:29 2007
@@ -4,27 +4,34 @@
[general]
+; Set this option to "yes" to enable automatically answering calls on the
+; console. This is very useful if the console is used as an intercom.
+; The default value is "no".
+;
+; autoanswer = no
+
; Set the default context to use for outgoing calls. This can be overridden by
; dialing some extension at context, unless the overridecontext option is enabled.
+; The default is "default".
;
-; context=default
+; context = default
; Set the default extension to use for outgoing calls. The default is "s".
;
-; extension=s
+; extension = s
; Set the default CallerID for created channels.
;
-; callerid=MyName Here <(256) 428-6000>
+; callerid = MyName Here <(256) 428-6000>
; Set the default language for created channels.
;
-; language=en
+; language = en
; If you set overridecontext to 'yes', then the whole dial string
; will be interpreted as an extension, which is extremely useful
; to dial SIP, IAX and other extensions which use the '@' character.
-; The default is 'no'.
+; The default is "no".
;
; overridecontext = no ; if 'no', the last @ will start the context
; if 'yes' the whole string is an extension.
More information about the svn-commits
mailing list