From 7d9871b3940fa50e85039aef6a8fb9870a7615b9 Mon Sep 17 00:00:00 2001 From: Matthew Jordan Date: Tue, 22 Jan 2013 15:16:20 +0000 Subject: [PATCH] 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 --- CHANGES | 9 +++ apps/app_controlplayback.c | 127 +++++++++++++++++++++++++++++++++++- apps/app_playback.c | 17 +++-- funcs/func_frame_trace.c | 17 ++++- include/asterisk/file.h | 32 ++++----- include/asterisk/frame.h | 10 +++ main/app.c | 32 ++++++--- main/channel.c | 21 ++++++ main/file.c | 130 +++++++++++++++++++++++++++++++------ res/res_agi.c | 50 ++++++++++++++ 10 files changed, 392 insertions(+), 53 deletions(-) diff --git a/CHANGES b/CHANGES index 2600c05eba..6e78fb02d5 100644 --- a/CHANGES +++ b/CHANGES @@ -32,6 +32,15 @@ AMI (Asterisk Manager Interface) 'Manager Show Command' now displays the privileges needed for using a given manager command instead. + * Added new action "ControlPlayback". The ControlPlayback action allows an AMI + client to manipulate audio currently being played back on a channel. The + supported operations 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. + Channel Drivers ------------------ diff --git a/apps/app_controlplayback.c b/apps/app_controlplayback.c index 1e2e6fbc20..c27fd1c52c 100644 --- a/apps/app_controlplayback.c +++ b/apps/app_controlplayback.c @@ -36,6 +36,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/app.h" #include "asterisk/module.h" +#include "asterisk/manager.h" +#include "asterisk/utils.h" +#include "asterisk/astobj2.h" /*** DOCUMENTATION @@ -82,6 +85,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") Contains the status of the attempt as a text string + @@ -95,6 +99,69 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + + + Control the playback of a file being played to a channel. + + + + + The name of the channel that currently has a file being played back to it. + + + + + Stop the playback operation. + + + Move the current position in the media forward. The amount + of time that the stream moves forward is determined by the + skipms value passed to the application + that initiated the playback. + + The default skipms value is 3000 ms. + + + + Move the current position in the media backward. The amount + of time that the stream moves backward is determined by the + skipms value passed to the application + that initiated the playback. + + The default skipms value is 3000 ms. + + + + Pause/unpause the playback operation, if supported. + If not supported, stop the playback. + + + Restart the playback operation, if supported. + If not supported, stop the playback. + + + + + + Control the operation of a media file being played back to a channel. + Note that this AMI action does not initiate playback of media to channel, but + rather controls the operation of a media operation that was already initiated + on the channel. + + The pause and restart + Control options will stop a playback + operation if that operation was not initiated from the + ControlPlayback application or the + control stream file AGI command. + + + + Playback + ControlPlayback + stream file + control stream file + + ***/ static const char app[] = "ControlPlayback"; @@ -201,6 +268,9 @@ static int controlplayback_exec(struct ast_channel *chan, const char *data) snprintf(stopkeybuf, sizeof(stopkeybuf), "%c", res); pbx_builtin_setvar_helper(chan, "CPLAYBACKSTOPKEY", stopkeybuf); res = 0; + } else if (res > 0 && res == AST_CONTROL_STREAM_STOP) { + pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "REMOTESTOPPED"); + res = 0; } else { if (res < 0) { res = 0; @@ -215,16 +285,67 @@ static int controlplayback_exec(struct ast_channel *chan, const char *data) return res; } +static int controlplayback_manager(struct mansession *s, const struct message *m) +{ + const char *channel_name = astman_get_header(m, "Channel"); + const char *control_type = astman_get_header(m, "Control"); + struct ast_channel *chan; + + if (ast_strlen_zero(channel_name)) { + astman_send_error(s, m, "Channel not specified"); + return 0; + } + + if (ast_strlen_zero(control_type)) { + astman_send_error(s, m, "Control not specified"); + return 0; + } + + chan = ast_channel_get_by_name(channel_name); + if (!chan) { + astman_send_error(s, m, "No such channel"); + return 0; + } + + if (!strcasecmp(control_type, "stop")) { + ast_queue_control(chan, AST_CONTROL_STREAM_STOP); + } else if (!strcasecmp(control_type, "forward")) { + ast_queue_control(chan, AST_CONTROL_STREAM_FORWARD); + } else if (!strcasecmp(control_type, "reverse")) { + ast_queue_control(chan, AST_CONTROL_STREAM_REVERSE); + } else if (!strcasecmp(control_type, "pause")) { + ast_queue_control(chan, AST_CONTROL_STREAM_SUSPEND); + } else if (!strcasecmp(control_type, "restart")) { + ast_queue_control(chan, AST_CONTROL_STREAM_RESTART); + } else { + astman_send_error(s, m, "Unknown control type"); + chan = ast_channel_unref(chan); + return 0; + } + + chan = ast_channel_unref(chan); + astman_send_ack(s, m, NULL); + return 0; +} + static int unload_module(void) { - int res; - res = ast_unregister_application(app); + int res = 0; + + res |= ast_unregister_application(app); + res |= ast_manager_unregister("ControlPlayback"); + return res; } static int load_module(void) { - return ast_register_application_xml(app, controlplayback_exec); + int res = 0; + + res |= ast_register_application_xml(app, controlplayback_exec); + res |= ast_manager_register_xml("ControlPlayback", EVENT_FLAG_CALL, controlplayback_manager); + + return res; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Control Playback Application"); diff --git a/apps/app_playback.c b/apps/app_playback.c index 18d4c8eb55..12b1ff6981 100644 --- a/apps/app_playback.c +++ b/apps/app_playback.c @@ -82,6 +82,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") See Also: Background (application) -- for playing sound files that are interruptible WaitExten (application) -- wait for digits from caller, optionally play music on hold + + Background + WaitExten + ControlPlayback + stream file + control stream file + ControlPlayback + ***/ @@ -473,11 +481,12 @@ static int playback_exec(struct ast_channel *chan, const char *data) res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1); else res = ast_streamfile(chan, front, ast_channel_language(chan)); - if (!res) { - res = ast_waitstream(chan, ""); + if (!res) { + res = ast_waitstream(chan, ""); ast_stopstream(chan); - } else { - ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", ast_channel_name(chan), (char *)data); + } + if (res) { + ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data); res = 0; mres = 1; } diff --git a/funcs/func_frame_trace.c b/funcs/func_frame_trace.c index 8e12aafb35..45da9691a6 100644 --- a/funcs/func_frame_trace.c +++ b/funcs/func_frame_trace.c @@ -327,8 +327,23 @@ static void print_frame(struct ast_frame *frame) case AST_CONTROL_PVT_CAUSE_CODE: ast_verbose("SubClass: PVT_CAUSE_CODE\n"); break; + case AST_CONTROL_STREAM_STOP: + ast_verbose("SubClass: STREAM_STOP\n"); + break; + case AST_CONTROL_STREAM_SUSPEND: + ast_verbose("SubClass: STREAM_SUSPEND\n"); + break; + case AST_CONTROL_STREAM_RESTART: + ast_verbose("SubClass: STREAM_RESTART\n"); + break; + case AST_CONTROL_STREAM_REVERSE: + ast_verbose("SubClass: STREAM_REVERSE\n"); + break; + case AST_CONTROL_STREAM_FORWARD: + ast_verbose("SubClass: STREAM_FORWARD\n"); + break; } - + if (frame->subclass.integer == -1) { ast_verbose("SubClass: %d\n", frame->subclass.integer); } diff --git a/include/asterisk/file.h b/include/asterisk/file.h index ec2a38e1f7..0b2f913ad8 100644 --- a/include/asterisk/file.h +++ b/include/asterisk/file.h @@ -137,46 +137,47 @@ int ast_filedelete(const char *filename, const char *fmt); */ int ast_filecopy(const char *oldname, const char *newname, const char *fmt); -/*! +/*! * \brief Waits for a stream to stop or digit to be pressed * \param c channel to waitstream on * \param breakon string of DTMF digits to break upon * Begins playback of a stream... - * Wait for a stream to stop or for any one of a given digit to arrive, + * Wait for a stream to stop or for any one of a given digit to arrive, * \retval 0 if the stream finishes - * \retval the character if it was interrupted, - * \retval -1 on error + * \retval the character if it was interrupted by the channel. + * \retval -1 on error */ int ast_waitstream(struct ast_channel *c, const char *breakon); -/*! - * \brief Waits for a stream to stop or digit matching a valid one digit exten to be pressed +/*! + * \brief Waits for a stream to stop or digit matching a valid one digit exten to be pressed * \param c channel to waitstream on * \param context string of context to match digits to break upon * Begins playback of a stream... - * Wait for a stream to stop or for any one of a valid extension digit to arrive, + * Wait for a stream to stop or for any one of a valid extension digit to arrive, * \retval 0 if the stream finishes. * \retval the character if it was interrupted. * \retval -1 on error. */ int ast_waitstream_exten(struct ast_channel *c, const char *context); -/*! - * \brief Same as waitstream but allows stream to be forwarded or rewound +/*! + * \brief Same as waitstream but allows stream to be forwarded or rewound * \param c channel to waitstream on * \param breakon string of DTMF digits to break upon * \param forward DTMF digit to fast forward upon * \param rewind DTMF digit to rewind upon * \param ms How many miliseconds to skip forward/back * Begins playback of a stream... - * Wait for a stream to stop or for any one of a given digit to arrive, + * Wait for a stream to stop or for any one of a given digit to arrive, * \retval 0 if the stream finishes. - * \retval the character if it was interrupted. + * \retval the character if it was interrupted, + * \retval the value of the control frame if it was interrupted by some other party, * \retval -1 on error. */ int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms); -/*! +/*! * \brief Same as waitstream_fr but allows a callback to be alerted when a user * fastforwards or rewinds the file. * \param c channel to waitstream on @@ -184,11 +185,12 @@ int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *fo * \param forward DTMF digit to fast forward upon * \param rewind DTMF digit to rewind upon * \param ms How many milliseconds to skip forward/back - * \param cb to call when rewind or fastfoward occurs. + * \param cb to call when rewind or fastfoward occurs. * Begins playback of a stream... - * Wait for a stream to stop or for any one of a given digit to arrive, + * Wait for a stream to stop or for any one of a given digit to arrive, * \retval 0 if the stream finishes. - * \retval the character if it was interrupted. + * \retval the character if it was interrupted, + * \retval the value of the control frame if it was interrupted by some other party, * \retval -1 on error. */ int ast_waitstream_fr_w_cb(struct ast_channel *c, diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 01aa27b616..5e81b4e18c 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -267,6 +267,16 @@ enum ast_control_frame_type { AST_CONTROL_MCID = 31, /*!< Indicate that the caller is being malicious. */ AST_CONTROL_UPDATE_RTP_PEER = 32, /*!< Interrupt the bridge and have it update the peer */ AST_CONTROL_PVT_CAUSE_CODE = 33, /*!< Contains an update to the protocol-specific cause-code stored for branching dials */ + + /* Control frames used to manipulate a stream on a channel. The values for these + * must be greater than the allowed value for a 8-bit char, so that they avoid + * conflicts with DTMF values. */ + AST_CONTROL_STREAM_STOP = 1000, /*!< Indicate to a channel in playback to stop the stream */ + AST_CONTROL_STREAM_SUSPEND = 1001, /*!< Indicate to a channel in playback to suspend the stream */ + AST_CONTROL_STREAM_RESTART = 1002, /*!< Indicate to a channel in playback to restart the stream */ + AST_CONTROL_STREAM_REVERSE = 1003, /*!< Indicate to a channel in playback to rewind */ + AST_CONTROL_STREAM_FORWARD = 1004, /*!< Indicate to a channel in playback to fast forward */ + }; enum ast_frame_read_action { diff --git a/main/app.c b/main/app.c index 208db4b838..6db65f3711 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 048975d124..dee6fe3211 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 db8fd5c02d..79b4e84869 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); } /* diff --git a/res/res_agi.c b/res/res_agi.c index b92ccdbdc2..0a20bbdf95 100644 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -160,6 +160,24 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") permitted. Returns 0 if playback completes without a digit being pressed, or the ASCII numerical value of the digit if one was pressed, or -1 on error or if the channel was disconnected. + It sets the following channel variables upon completion: + + + Contains the status of the attempt as a text string + + + + + + + Contains the offset in ms into the file where playback + was at when it stopped. -1 is end of file. + + + If the playback is stopped by the user this variable contains + the key that was pressed. + + @@ -652,6 +670,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") or -1 on error or if the channel was disconnected. If musiconhold is playing before calling stream file it will be automatically stopped and will not be restarted after completion. + It sets the following channel variables upon completion: + + + The status of the playback attempt as a text string. + + + + control stream file @@ -1984,6 +2010,9 @@ static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc { int res = 0, skipms = 3000; const char *fwd = "#", *rev = "*", *suspend = NULL, *stop = NULL; /* Default values */ + char stopkeybuf[2]; + long offsetms = 0; + char offsetbuf[20]; if (argc < 5 || argc > 9) { return RESULT_SHOWUSAGE; @@ -2011,6 +2040,25 @@ static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, suspend, NULL, skipms, NULL); + /* If we stopped on one of our stop keys, return 0 */ + if (res > 0 && stop && strchr(stop, res)) { + pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED"); + snprintf(stopkeybuf, sizeof(stopkeybuf), "%c", res); + pbx_builtin_setvar_helper(chan, "CPLAYBACKSTOPKEY", stopkeybuf); + } else if (res > 0 && res == AST_CONTROL_STREAM_STOP) { + pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "REMOTESTOPPED"); + res = 0; + } else { + if (res < 0) { + pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR"); + } else { + pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS"); + } + } + + snprintf(offsetbuf, sizeof(offsetbuf), "%ld", offsetms); + pbx_builtin_setvar_helper(chan, "CPLAYBACKOFFSET", offsetbuf); + ast_agi_send(agi->fd, chan, "200 result=%d\n", res); return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; @@ -2068,6 +2116,8 @@ static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, const return RESULT_SUCCESS; } ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", res, sample_offset); + pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS"); + return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; }