summaryrefslogtreecommitdiff
path: root/main/file.c
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/file.c
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/file.c')
-rw-r--r--main/file.c130
1 files changed, 110 insertions, 20 deletions
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);
}
/*