summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorMatthew Jordan <mjordan@digium.com>2013-01-22 15:16:20 +0000
committerMatthew Jordan <mjordan@digium.com>2013-01-22 15:16:20 +0000
commit7d9871b3940fa50e85039aef6a8fb9870a7615b9 (patch)
tree05484de8505feb85d1e304b8868a87e29c0ca9f5 /main
parent985ea8b2c96ff16b5cbe4cd102b9224e171b2984 (diff)
Add ControlPlayback manager action
This patch adds the capability for asynchronous manipulation of audio being played back to a channel though a new AMI action "ControlPlayback". The ControlPlayback action supports a number of operations, the availability of which depend on the application being used to send audio to the channel. When the audio playback was initiated using the ControlPlayback application or CONTROL STREAM FILE AGI command, the audio can be paused, stopped, restarted, reversed, or skipped forward. When initiated by other mechanisms (such as the Playback application), the audio can be stopped, reversed, or skipped forward. Review: https://reviewboard.asterisk.org/r/2265/ (closes issue ASTERISK-20882) Reported by: mjordan git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@379830 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r--main/app.c32
-rw-r--r--main/channel.c21
-rw-r--r--main/file.c130
3 files changed, 153 insertions, 30 deletions
diff --git a/main/app.c b/main/app.c
index 208db4b83..6db65f371 100644
--- a/main/app.c
+++ b/main/app.c
@@ -1004,24 +1004,37 @@ static int control_streamfile(struct ast_channel *chan,
}
/* We go at next loop if we got the restart char */
- if (restart && strchr(restart, res)) {
+ if ((restart && strchr(restart, res)) || res == AST_CONTROL_STREAM_RESTART) {
ast_debug(1, "we'll restart the stream here at next loop\n");
pause_restart_point = 0;
+ ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
+ "Control: %s\r\n",
+ ast_channel_name(chan),
+ "Restart");
continue;
}
- if (suspend && strchr(suspend, res)) {
+ if ((suspend && strchr(suspend, res)) || res == AST_CONTROL_STREAM_SUSPEND) {
pause_restart_point = ast_tellstream(ast_channel_stream(chan));
+ ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
+ "Control: %s\r\n",
+ ast_channel_name(chan),
+ "Pause");
for (;;) {
ast_stopstream(chan);
if (!(res = ast_waitfordigit(chan, 1000))) {
continue;
- } else if (res == -1 || strchr(suspend, res) || (stop && strchr(stop, res))) {
+ } else if (res == -1 || (suspend && strchr(suspend, res)) || (stop && strchr(stop, res))
+ || res == AST_CONTROL_STREAM_SUSPEND || res == AST_CONTROL_STREAM_STOP) {
break;
}
}
- if (res == *suspend) {
+ if ((suspend && (res == *suspend)) || res == AST_CONTROL_STREAM_SUSPEND) {
res = 0;
+ ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
+ "Control: %s\r\n",
+ ast_channel_name(chan),
+ "Unpause");
continue;
}
}
@@ -1031,7 +1044,11 @@ static int control_streamfile(struct ast_channel *chan,
}
/* if we get one of our stop chars, return it to the calling function */
- if (stop && strchr(stop, res)) {
+ if ((stop && strchr(stop, res)) || res == AST_CONTROL_STREAM_STOP) {
+ ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
+ "Control: %s\r\n",
+ ast_channel_name(chan),
+ "Stop");
break;
}
}
@@ -1050,11 +1067,6 @@ static int control_streamfile(struct ast_channel *chan,
*offsetms = offset / 8; /* samples --> ms ... XXX Assumes 8 kHz */
}
- /* If we are returning a digit cast it as char */
- if (res > 0 || ast_channel_stream(chan)) {
- res = (char)res;
- }
-
ast_stopstream(chan);
return res;
diff --git a/main/channel.c b/main/channel.c
index 048975d12..dee6fe321 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -3700,6 +3700,17 @@ int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, in
ast_frfree(f);
ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
return -1;
+ case AST_CONTROL_STREAM_STOP:
+ case AST_CONTROL_STREAM_SUSPEND:
+ case AST_CONTROL_STREAM_RESTART:
+ case AST_CONTROL_STREAM_REVERSE:
+ case AST_CONTROL_STREAM_FORWARD:
+ /* Fall-through and treat as if it were a DTMF signal. Items
+ * that perform stream control will handle this. */
+ res = f->subclass.integer;
+ ast_frfree(f);
+ ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
+ return res;
case AST_CONTROL_PVT_CAUSE_CODE:
case AST_CONTROL_RINGING:
case AST_CONTROL_ANSWER:
@@ -4454,6 +4465,11 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
case AST_CONTROL_MCID:
case AST_CONTROL_UPDATE_RTP_PEER:
case AST_CONTROL_PVT_CAUSE_CODE:
+ 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:
break;
case AST_CONTROL_INCOMPLETE:
@@ -4661,6 +4677,11 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
case AST_CONTROL_END_OF_Q:
case AST_CONTROL_MCID:
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:
/* Nothing left to do for these. */
res = 0;
break;
diff --git a/main/file.c b/main/file.c
index db8fd5c02..79b4e8486 100644
--- a/main/file.c
+++ b/main/file.c
@@ -1240,6 +1240,45 @@ struct ast_filestream *ast_writefile(const char *filename, const char *type, con
return fs;
}
+static void waitstream_control(struct ast_channel *c,
+ enum ast_waitstream_fr_cb_values type,
+ ast_waitstream_fr_cb cb,
+ int skip_ms)
+{
+ switch (type)
+ {
+ case AST_WAITSTREAM_CB_FASTFORWARD:
+ {
+ int eoftest;
+ ast_stream_fastforward(ast_channel_stream(c), skip_ms);
+ eoftest = fgetc(ast_channel_stream(c)->f);
+ if (feof(ast_channel_stream(c)->f)) {
+ ast_stream_rewind(ast_channel_stream(c), skip_ms);
+ } else {
+ ungetc(eoftest, ast_channel_stream(c)->f);
+ }
+ }
+ break;
+ case AST_WAITSTREAM_CB_REWIND:
+ ast_stream_rewind(ast_channel_stream(c), skip_ms);
+ break;
+ default:
+ break;
+ }
+
+ if (cb) {
+ long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_rate(&ast_channel_stream(c)->fmt->format) / 1000);
+ cb(c, ms_len, type);
+ }
+
+ ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
+ "Control: %s\r\n"
+ "SkipMs: %d\r\n",
+ ast_channel_name(c),
+ (type == AST_WAITSTREAM_CB_FASTFORWARD) ? "FastForward" : "Rewind",
+ skip_ms);
+}
+
/*!
* \brief the core of all waitstream() functions
*/
@@ -1336,34 +1375,49 @@ static int waitstream_core(struct ast_channel *c,
return res;
}
} else {
- enum ast_waitstream_fr_cb_values cb_val = 0;
res = fr->subclass.integer;
if (strchr(forward, res)) {
- int eoftest;
- ast_stream_fastforward(ast_channel_stream(c), skip_ms);
- eoftest = fgetc(ast_channel_stream(c)->f);
- if (feof(ast_channel_stream(c)->f)) {
- ast_stream_rewind(ast_channel_stream(c), skip_ms);
- } else {
- ungetc(eoftest, ast_channel_stream(c)->f);
- }
- cb_val = AST_WAITSTREAM_CB_FASTFORWARD;
+ waitstream_control(c, AST_WAITSTREAM_CB_FASTFORWARD, cb, skip_ms);
} else if (strchr(reverse, res)) {
- ast_stream_rewind(ast_channel_stream(c), skip_ms);
- cb_val = AST_WAITSTREAM_CB_REWIND;
+ waitstream_control(c, AST_WAITSTREAM_CB_REWIND, cb, skip_ms);
} else if (strchr(breakon, res)) {
+ ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
+ "Control: %s\r\n",
+ ast_channel_name(c),
+ "Break");
+
ast_frfree(fr);
ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
return res;
}
- if (cb_val && cb) {
- long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_rate(&ast_channel_stream(c)->fmt->format) / 1000);
- cb(c, ms_len, cb_val);
- }
}
break;
case AST_FRAME_CONTROL:
switch (fr->subclass.integer) {
+ case AST_CONTROL_STREAM_STOP:
+ case AST_CONTROL_STREAM_SUSPEND:
+ case AST_CONTROL_STREAM_RESTART:
+ /* Fall-through and break out */
+ ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
+ "Control: %s\r\n",
+ ast_channel_name(c),
+ "Break");
+ res = fr->subclass.integer;
+ ast_frfree(fr);
+ ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
+ return res;
+ case AST_CONTROL_STREAM_REVERSE:
+ if (!skip_ms) {
+ skip_ms = 3000;
+ }
+ waitstream_control(c, AST_WAITSTREAM_CB_REWIND, cb, skip_ms);
+ break;
+ case AST_CONTROL_STREAM_FORWARD:
+ if (!skip_ms) {
+ skip_ms = 3000;
+ }
+ waitstream_control(c, AST_WAITSTREAM_CB_FASTFORWARD, cb, skip_ms);
+ break;
case AST_CONTROL_HANGUP:
case AST_CONTROL_BUSY:
case AST_CONTROL_CONGESTION:
@@ -1427,26 +1481,62 @@ int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *fo
-1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */, NULL /* no callback */);
}
+/*! \internal
+ * \brief Clean up the return value of a waitstream call
+ *
+ * It's possible for a control frame to come in from an external source and break the
+ * playback. From a consumer of most ast_waitstream_* function callers, this should
+ * appear like normal playback termination, i.e., return 0 and not the value of the
+ * control frame.
+ */
+static int sanitize_waitstream_return(int return_value)
+{
+ switch (return_value) {
+ case AST_CONTROL_STREAM_STOP:
+ case AST_CONTROL_STREAM_SUSPEND:
+ case AST_CONTROL_STREAM_RESTART:
+ /* Fall through and set return_value to 0 */
+ return_value = 0;
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ return return_value;
+}
+
int ast_waitstream(struct ast_channel *c, const char *breakon)
{
- return waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL, NULL /* no callback */);
+ int res;
+
+ res = waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL, NULL /* no callback */);
+
+ return sanitize_waitstream_return(res);
}
int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
{
- return waitstream_core(c, breakon, NULL, NULL, 0,
+ int res;
+
+ res = waitstream_core(c, breakon, NULL, NULL, 0,
audiofd, cmdfd, NULL /* no context */, NULL /* no callback */);
+
+ return sanitize_waitstream_return(res);
}
int ast_waitstream_exten(struct ast_channel *c, const char *context)
{
+ int res;
+
/* Waitstream, with return in the case of a valid 1 digit extension */
/* in the current or specified context being pressed */
-
if (!context)
context = ast_channel_context(c);
- return waitstream_core(c, NULL, NULL, NULL, 0,
+ res = waitstream_core(c, NULL, NULL, NULL, 0,
-1, -1, context, NULL /* no callback */);
+
+ return sanitize_waitstream_return(res);
}
/*