[asterisk-commits] dlee: trunk r396331 - in /trunk: apps/ funcs/ include/asterisk/ main/ res/ re...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Aug 6 09:44:53 CDT 2013
Author: dlee
Date: Tue Aug 6 09:44:45 2013
New Revision: 396331
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=396331
Log:
ARI: Add recording controls
This patch implements the controls from ARI recordings. The controls
are:
* DELETE /recordings/live/{recordingName} - stop recording and
discard it
* POST /recordings/live/{recordingName}/stop - stop recording
* POST /recordings/live/{recordingName}/pause - pause recording
* POST /recordings/live/{recordingName}/unpause - resume recording
* POST /recordings/live/{recordingName}/mute - mute recording (record
silence to the file)
* POST /recordings/live/{recordingName}/unmute - unmute recording.
Since this underlying functionality did not already exist, is was
added to app.c by a set of control frames, similar to how playback
control works. The pause/mute control frames are toggles, even though
the ARI controls are idempotent, to be consistent with the playback
control frames.
(closes issue ASTERISK-22181)
Review: https://reviewboard.asterisk.org/r/2697/
Modified:
trunk/apps/app_minivm.c
trunk/apps/app_voicemail.c
trunk/funcs/func_frame_trace.c
trunk/include/asterisk/app.h
trunk/include/asterisk/frame.h
trunk/include/asterisk/stasis_app_recording.h
trunk/main/app.c
trunk/main/channel.c
trunk/res/ari/resource_recordings.c
trunk/res/ari/resource_recordings.h
trunk/res/res_ari_recordings.c
trunk/res/res_stasis_recording.c
trunk/rest-api/api-docs/recordings.json
Modified: trunk/apps/app_minivm.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_minivm.c?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/apps/app_minivm.c (original)
+++ trunk/apps/app_minivm.c Tue Aug 6 09:44:45 2013
@@ -1674,7 +1674,7 @@
ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
if (ast_test_flag(vmu, MVM_OPERATOR))
canceldtmf = "0";
- cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf, 0, AST_RECORD_IF_EXISTS_OVERWRITE);
+ cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf, 0, AST_RECORD_IF_EXISTS_OVERWRITE);
if (record_gain)
ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
if (cmd == -1) /* User has hung up, no options to give */
Modified: trunk/apps/app_voicemail.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_voicemail.c?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/apps/app_voicemail.c (original)
+++ trunk/apps/app_voicemail.c Tue Aug 6 09:44:45 2013
@@ -14682,7 +14682,7 @@
ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
if (ast_test_flag(vmu, VM_OPERATOR))
canceldtmf = "0";
- cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf, 0, AST_RECORD_IF_EXISTS_OVERWRITE);
+ cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf, 0, AST_RECORD_IF_EXISTS_OVERWRITE);
if (strchr(canceldtmf, cmd)) {
/* need this flag here to distinguish between pressing '0' during message recording or after */
canceleddtmf = 1;
Modified: trunk/funcs/func_frame_trace.c
URL: http://svnview.digium.com/svn/asterisk/trunk/funcs/func_frame_trace.c?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/funcs/func_frame_trace.c (original)
+++ trunk/funcs/func_frame_trace.c Tue Aug 6 09:44:45 2013
@@ -343,6 +343,18 @@
case AST_CONTROL_STREAM_FORWARD:
ast_verbose("SubClass: STREAM_FORWARD\n");
break;
+ case AST_CONTROL_RECORD_CANCEL:
+ ast_verbose("SubClass: RECORD_CANCEL\n");
+ break;
+ case AST_CONTROL_RECORD_STOP:
+ ast_verbose("SubClass: RECORD_STOP\n");
+ break;
+ case AST_CONTROL_RECORD_SUSPEND:
+ ast_verbose("SubClass: RECORD_SUSPEND\n");
+ break;
+ case AST_CONTROL_RECORD_MUTE:
+ ast_verbose("SubClass: RECORD_MUTE\n");
+ break;
}
if (frame->subclass.integer == -1) {
Modified: trunk/include/asterisk/app.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/app.h?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/include/asterisk/app.h (original)
+++ trunk/include/asterisk/app.h Tue Aug 6 09:44:45 2013
@@ -709,11 +709,12 @@
* skip_confirmation_sound is false.
*
* \param chan the channel being recorded
- * \param playfile Filename of sound to play before recording begins
+ * \param playfile Filename of sound to play before recording begins. A beep is also played when playfile completes, before the recording begins.
* \param recordfile Filename to save the recording
* \param maxtime_sec Longest possible message length in seconds
* \param fmt string containing all formats to be recorded delimited by '|'
* \param duration pointer to integer for storing length of the recording
+ * \param beep If true, play a beep before recording begins (and doesn't play \a playfile)
* \param sound_duration pointer to integer for storing length of the recording minus all silence
* \param silencethreshold tolerance of noise levels that can be considered silence for the purpose of silence timeout, -1 for default
* \param maxsilence_ms Length of time in milliseconds which will trigger a timeout from silence, -1 for default
@@ -728,7 +729,7 @@
* \retval 't' Recording ended from the message exceeding the maximum duration
* \retval dtmfchar Recording ended via the return value's DTMF character for either cancel or accept.
*/
-int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists);
+int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists);
/*!
* \brief Record a file based on input from a channel. Use default accept and cancel DTMF.
Modified: trunk/include/asterisk/frame.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/frame.h?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/include/asterisk/frame.h (original)
+++ trunk/include/asterisk/frame.h Tue Aug 6 09:44:45 2013
@@ -278,7 +278,11 @@
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 */
-
+ /* Control frames to manipulate recording on a channel. */
+ AST_CONTROL_RECORD_CANCEL = 1100, /*!< Indicated to a channel in record to stop recording and discard the file */
+ AST_CONTROL_RECORD_STOP = 1101, /*!< Indicated to a channel in record to stop recording */
+ AST_CONTROL_RECORD_SUSPEND = 1102, /*!< Indicated to a channel in record to suspend/unsuspend recording */
+ AST_CONTROL_RECORD_MUTE = 1103, /*!< Indicated to a channel in record to mute/unmute (i.e. write silence) recording */
};
enum ast_frame_read_action {
Modified: trunk/include/asterisk/stasis_app_recording.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/stasis_app_recording.h?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/include/asterisk/stasis_app_recording.h (original)
+++ trunk/include/asterisk/stasis_app_recording.h Tue Aug 6 09:44:45 2013
@@ -44,14 +44,30 @@
STASIS_APP_RECORDING_STATE_PAUSED,
/*! The media has stopped recording */
STASIS_APP_RECORDING_STATE_COMPLETE,
- /*! The media has stopped playing */
+ /*! The media has stopped recording, with error */
STASIS_APP_RECORDING_STATE_FAILED,
+ /*! The media has stopped recording, discard the recording file */
+ STASIS_APP_RECORDING_STATE_CANCELED,
+ /*! Sentinel */
+ STASIS_APP_RECORDING_STATE_MAX,
};
/*! Valid operation for controlling a recording. */
enum stasis_app_recording_media_operation {
- /*! Stop the recording operation. */
+ /*! Stop the recording, deleting the media file(s) */
+ STASIS_APP_RECORDING_CANCEL,
+ /*! Stop the recording. */
STASIS_APP_RECORDING_STOP,
+ /*! Pause the recording */
+ STASIS_APP_RECORDING_PAUSE,
+ /*! Unpause the recording */
+ STASIS_APP_RECORDING_UNPAUSE,
+ /*! Mute the recording (record silence) */
+ STASIS_APP_RECORDING_MUTE,
+ /*! Unmute the recording */
+ STASIS_APP_RECORDING_UNMUTE,
+ /*! Sentinel */
+ STASIS_APP_RECORDING_OPER_MAX,
};
#define STASIS_APP_RECORDING_TERMINATE_INVALID 0
Modified: trunk/main/app.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/app.c?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/main/app.c (original)
+++ trunk/main/app.c Tue Aug 6 09:44:45 2013
@@ -1145,6 +1145,78 @@
return d;
}
+/*!
+ * \brief Construct a silence frame of the same duration as \a orig.
+ *
+ * The \a orig frame must be \ref AST_FORMAT_SLINEAR.
+ *
+ * \param orig Frame as basis for silence to generate.
+ * \return New frame of silence; free with ast_frfree().
+ * \return \c NULL on error.
+ */
+static struct ast_frame *make_silence(const struct ast_frame *orig)
+{
+ struct ast_frame *silence;
+ size_t size;
+ size_t datalen;
+ size_t samples = 0;
+ struct ast_frame *next;
+
+ if (!orig) {
+ return NULL;
+ }
+
+ if (orig->subclass.format.id != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Attempting to silence non-slin frame\n");
+ return NULL;
+ }
+
+ for (next = AST_LIST_NEXT(orig, frame_list);
+ orig;
+ orig = next, next = orig ? AST_LIST_NEXT(orig, frame_list) : NULL) {
+ samples += orig->samples;
+ }
+
+ ast_verb(4, "Silencing %zd samples\n", samples);
+
+
+ datalen = sizeof(short) * samples;
+ size = sizeof(*silence) + datalen;
+ silence = ast_calloc(1, size);
+ if (!silence) {
+ return NULL;
+ }
+
+ silence->mallocd = AST_MALLOCD_HDR;
+ silence->frametype = AST_FRAME_VOICE;
+ silence->data.ptr = (void *)(silence + 1);
+ silence->samples = samples;
+ silence->datalen = datalen;
+
+ ast_format_set(&silence->subclass.format, AST_FORMAT_SLINEAR, 0);
+
+ return silence;
+}
+
+/*!
+ * \brief Sets a channel's read format to \ref AST_FORMAT_SLINEAR, recording
+ * its original format.
+ *
+ * \param chan Channel to modify.
+ * \param[out] orig_format Output variable to store channel's original read
+ * format.
+ * \return 0 on success.
+ * \return -1 on error.
+ */
+static int set_read_to_slin(struct ast_channel *chan, struct ast_format *orig_format)
+{
+ if (!chan || !orig_format) {
+ return -1;
+ }
+ ast_format_copy(orig_format, ast_channel_readformat(chan));
+ return ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR);
+}
+
static int global_silence_threshold = 128;
static int global_maxsilence = 0;
@@ -1274,8 +1346,7 @@
return -1;
}
ast_dsp_set_threshold(sildet, silencethreshold);
- ast_format_copy(&rfmt, ast_channel_readformat(chan));
- res = ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR);
+ res = set_read_to_slin(chan, &rfmt);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
ast_dsp_free(sildet);
@@ -1293,9 +1364,15 @@
}
if (x == fmtcnt) {
- /* Loop forever, writing the packets we read to the writer(s), until
- we read a digit or get a hangup */
+ /* Loop, writing the packets we read to the writer(s), until
+ * we have reason to stop. */
struct ast_frame *f;
+ int paused = 0;
+ int muted = 0;
+ time_t pause_start = 0;
+ int paused_secs = 0;
+ int pausedsilence = 0;
+
for (;;) {
if (!(res = ast_waitfor(chan, 2000))) {
ast_debug(1, "One waitfor failed, trying another\n");
@@ -1315,11 +1392,29 @@
}
if (f->frametype == AST_FRAME_VOICE) {
/* write each format */
- for (x = 0; x < fmtcnt; x++) {
- if (prepend && !others[x]) {
- break;
+ if (paused) {
+ /* It's all good */
+ res = 0;
+ } else {
+ RAII_VAR(struct ast_frame *, silence, NULL, ast_frame_dtor);
+ struct ast_frame *orig = f;
+
+ if (muted) {
+ silence = make_silence(orig);
+ if (!silence) {
+ ast_log(LOG_WARNING,
+ "Error creating silence\n");
+ break;
+ }
+ f = silence;
}
- res = ast_writestream(others[x], f);
+ for (x = 0; x < fmtcnt; x++) {
+ if (prepend && !others[x]) {
+ break;
+ }
+ res = ast_writestream(others[x], f);
+ }
+ f = orig;
}
/* Silence Detection */
@@ -1330,6 +1425,17 @@
totalsilence += olddspsilence;
}
olddspsilence = dspsilence;
+
+ if (paused) {
+ /* record how much silence there was while we are paused */
+ pausedsilence = dspsilence;
+ } else if (dspsilence > pausedsilence) {
+ /* ignore the paused silence */
+ dspsilence -= pausedsilence;
+ } else {
+ /* dspsilence has reset, reset pausedsilence */
+ pausedsilence = 0;
+ }
if (dspsilence > maxsilence) {
/* Ended happily with silence */
@@ -1362,15 +1468,51 @@
break;
}
if (strchr(canceldtmf, f->subclass.integer)) {
- ast_verb(3, "User cancelled message by pressing %c\n", f->subclass.integer);
+ ast_verb(3, "User canceled message by pressing %c\n", f->subclass.integer);
res = f->subclass.integer;
outmsg = 0;
break;
}
- }
- if (maxtime) {
+ } else if (f->frametype == AST_FRAME_CONTROL) {
+ if (f->subclass.integer == AST_CONTROL_RECORD_CANCEL) {
+ ast_verb(3, "Message canceled by control\n");
+ outmsg = 0; /* cancels the recording */
+ res = 0;
+ break;
+ } else if (f->subclass.integer == AST_CONTROL_RECORD_STOP) {
+ ast_verb(3, "Message ended by control\n");
+ res = 0;
+ break;
+ } else if (f->subclass.integer == AST_CONTROL_RECORD_SUSPEND) {
+ paused = !paused;
+ ast_verb(3, "Message %spaused by control\n",
+ paused ? "" : "un");
+ if (paused) {
+ pause_start = time(NULL);
+ } else {
+ paused_secs += time(NULL) - pause_start;
+ }
+ } else if (f->subclass.integer == AST_CONTROL_RECORD_MUTE) {
+ muted = !muted;
+ ast_verb(3, "Message %smuted by control\n",
+ muted ? "" : "un");
+ /* We can only silence slin frames, so
+ * set the mode, if we haven't already
+ * for sildet
+ */
+ if (muted && !rfmt.id) {
+ ast_verb(3, "Setting read format to linear mode\n");
+ res = set_read_to_slin(chan, &rfmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
+ break;
+ }
+ }
+ }
+ }
+ if (maxtime && !paused) {
end = time(NULL);
- if (maxtime < (end - start)) {
+ if (maxtime < (end - start - paused_secs)) {
ast_verb(3, "Took too long, cutting it short...\n");
res = 't';
outmsg = 2;
@@ -1493,9 +1635,9 @@
static const char default_acceptdtmf[] = "#";
static const char default_canceldtmf[] = "";
-int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists)
-{
- return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf), skip_confirmation_sound, if_exists);
+int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists)
+{
+ return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, beep, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf), skip_confirmation_sound, if_exists);
}
int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence, const char *path)
Modified: trunk/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/channel.c?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/main/channel.c (original)
+++ trunk/main/channel.c Tue Aug 6 09:44:45 2013
@@ -4303,6 +4303,10 @@
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:
break;
case AST_CONTROL_INCOMPLETE:
@@ -4561,6 +4565,10 @@
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;
Modified: trunk/res/ari/resource_recordings.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/ari/resource_recordings.c?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/res/ari/resource_recordings.c (original)
+++ trunk/res/ari/resource_recordings.c Tue Aug 6 09:44:45 2013
@@ -71,27 +71,81 @@
ast_ari_response_ok(response, ast_json_ref(json));
}
-void ast_ari_cancel_recording(struct ast_variable *headers, struct ast_cancel_recording_args *args, struct ast_ari_response *response)
+static void control_recording(const char *name,
+ enum stasis_app_recording_media_operation operation,
+ struct ast_ari_response *response)
{
- ast_log(LOG_ERROR, "TODO: ast_ari_cancel_recording\n");
+ RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
+ enum stasis_app_recording_oper_results res;
+
+ recording = stasis_app_recording_find_by_name(name);
+ if (recording == NULL) {
+ ast_ari_response_error(response, 404, "Not Found",
+ "Recording not found");
+ return;
+ }
+
+ res = stasis_app_recording_operation(recording, operation);
+
+ switch (res) {
+ case STASIS_APP_RECORDING_OPER_OK:
+ ast_ari_response_no_content(response);
+ return;
+ case STASIS_APP_RECORDING_OPER_FAILED:
+ ast_ari_response_error(response, 500,
+ "Internal Server Error", "Recording operation failed");
+ return;
+ case STASIS_APP_RECORDING_OPER_NOT_RECORDING:
+ ast_ari_response_error(response, 409,
+ "Conflict", "Recording not in session");
+ }
}
-void ast_ari_stop_recording(struct ast_variable *headers, struct ast_stop_recording_args *args, struct ast_ari_response *response)
+
+void ast_ari_cancel_recording(struct ast_variable *headers,
+ struct ast_cancel_recording_args *args,
+ struct ast_ari_response *response)
{
- ast_log(LOG_ERROR, "TODO: ast_ari_stop_recording\n");
+ control_recording(args->recording_name, STASIS_APP_RECORDING_CANCEL,
+ response);
}
-void ast_ari_pause_recording(struct ast_variable *headers, struct ast_pause_recording_args *args, struct ast_ari_response *response)
+
+void ast_ari_stop_recording(struct ast_variable *headers,
+ struct ast_stop_recording_args *args,
+ struct ast_ari_response *response)
{
- ast_log(LOG_ERROR, "TODO: ast_ari_pause_recording\n");
+ control_recording(args->recording_name, STASIS_APP_RECORDING_STOP,
+ response);
}
-void ast_ari_unpause_recording(struct ast_variable *headers, struct ast_unpause_recording_args *args, struct ast_ari_response *response)
+
+void ast_ari_pause_recording(struct ast_variable *headers,
+ struct ast_pause_recording_args *args,
+ struct ast_ari_response *response)
{
- ast_log(LOG_ERROR, "TODO: ast_ari_unpause_recording\n");
+ control_recording(args->recording_name, STASIS_APP_RECORDING_PAUSE,
+ response);
}
-void ast_ari_mute_recording(struct ast_variable *headers, struct ast_mute_recording_args *args, struct ast_ari_response *response)
+
+void ast_ari_unpause_recording(struct ast_variable *headers,
+ struct ast_unpause_recording_args *args,
+ struct ast_ari_response *response)
{
- ast_log(LOG_ERROR, "TODO: ast_ari_mute_recording\n");
+ control_recording(args->recording_name, STASIS_APP_RECORDING_UNPAUSE,
+ response);
}
-void ast_ari_unmute_recording(struct ast_variable *headers, struct ast_unmute_recording_args *args, struct ast_ari_response *response)
+
+void ast_ari_mute_recording(struct ast_variable *headers,
+ struct ast_mute_recording_args *args,
+ struct ast_ari_response *response)
{
- ast_log(LOG_ERROR, "TODO: ast_ari_unmute_recording\n");
+ control_recording(args->recording_name, STASIS_APP_RECORDING_MUTE,
+ response);
}
+
+void ast_ari_unmute_recording(struct ast_variable *headers,
+ struct ast_unmute_recording_args *args,
+ struct ast_ari_response *response)
+{
+ control_recording(args->recording_name, STASIS_APP_RECORDING_UNMUTE,
+ response);
+}
Modified: trunk/res/ari/resource_recordings.h
URL: http://svnview.digium.com/svn/asterisk/trunk/res/ari/resource_recordings.h?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/res/ari/resource_recordings.h (original)
+++ trunk/res/ari/resource_recordings.h Tue Aug 6 09:44:45 2013
@@ -134,7 +134,7 @@
/*!
* \brief Pause a live recording.
*
- * Pausing a recording suspends silence detection, which will be restarted when the recording is unpaused.
+ * Pausing a recording suspends silence detection, which will be restarted when the recording is unpaused. Paused time is not included in the accounting for maxDurationSeconds.
*
* \param headers HTTP headers
* \param args Swagger parameters
Modified: trunk/res/res_ari_recordings.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_ari_recordings.c?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/res/res_ari_recordings.c (original)
+++ trunk/res/res_ari_recordings.c Tue Aug 6 09:44:45 2013
@@ -295,6 +295,7 @@
break;
case 500: /* Internal Server Error */
case 501: /* Not Implemented */
+ case 404: /* Recording not found */
is_valid = 1;
break;
default:
@@ -351,6 +352,7 @@
break;
case 500: /* Internal Server Error */
case 501: /* Not Implemented */
+ case 404: /* Recording not found */
is_valid = 1;
break;
default:
@@ -407,6 +409,7 @@
break;
case 500: /* Internal Server Error */
case 501: /* Not Implemented */
+ case 404: /* Recording not found */
is_valid = 1;
break;
default:
@@ -463,6 +466,8 @@
break;
case 500: /* Internal Server Error */
case 501: /* Not Implemented */
+ case 404: /* Recording not found */
+ case 409: /* Recording not in session */
is_valid = 1;
break;
default:
@@ -519,6 +524,8 @@
break;
case 500: /* Internal Server Error */
case 501: /* Not Implemented */
+ case 404: /* Recording not found */
+ case 409: /* Recording not in session */
is_valid = 1;
break;
default:
@@ -575,6 +582,8 @@
break;
case 500: /* Internal Server Error */
case 501: /* Not Implemented */
+ case 404: /* Recording not found */
+ case 409: /* Recording not in session */
is_valid = 1;
break;
default:
@@ -631,6 +640,8 @@
break;
case 500: /* Internal Server Error */
case 501: /* Not Implemented */
+ case 404: /* Recording not found */
+ case 409: /* Recording not in session */
is_valid = 1;
break;
default:
Modified: trunk/res/res_stasis_recording.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_stasis_recording.c?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/res/res_stasis_recording.c (original)
+++ trunk/res/res_stasis_recording.c Tue Aug 6 09:44:45 2013
@@ -59,11 +59,13 @@
struct stasis_app_recording_options *options;
/*! Absolute path (minus extension) of the recording */
char *absolute_name;
- /*! Control object for the channel we're playing back to */
+ /*! Control object for the channel we're recording */
struct stasis_app_control *control;
/*! Current state of the recording. */
enum stasis_app_recording_state state;
+ /*! Indicates whether the recording is currently muted */
+ int muted:1;
};
static int recording_hash(const void *obj, int flags)
@@ -99,6 +101,10 @@
return "done";
case STASIS_APP_RECORDING_STATE_FAILED:
return "failed";
+ case STASIS_APP_RECORDING_STATE_CANCELED:
+ return "canceled";
+ case STASIS_APP_RECORDING_STATE_MAX:
+ return "?";
}
return "?";
@@ -253,12 +259,13 @@
}
ast_play_and_record_full(chan,
- recording->options->beep ? "beep" : NULL,
+ NULL, /* playfile */
recording->absolute_name,
recording->options->max_duration_seconds,
recording->options->format,
&duration,
NULL, /* sound_duration */
+ recording->options->beep,
-1, /* silencethreshold */
recording->options->max_silence_seconds * 1000,
NULL, /* path */
@@ -403,12 +410,127 @@
return ast_json_ref(json);
}
+typedef int (*recording_operation_cb)(struct stasis_app_recording *recording);
+
+static int recording_noop(struct stasis_app_recording *recording)
+{
+ return 0;
+}
+
+static int recording_disregard(struct stasis_app_recording *recording)
+{
+ recording->state = STASIS_APP_RECORDING_STATE_CANCELED;
+ return 0;
+}
+
+static int recording_cancel(struct stasis_app_recording *recording)
+{
+ int res = 0;
+ recording->state = STASIS_APP_RECORDING_STATE_CANCELED;
+ res |= stasis_app_control_queue_control(recording->control,
+ AST_CONTROL_RECORD_CANCEL);
+ res |= ast_filedelete(recording->absolute_name, NULL);
+ return res;
+}
+
+static int recording_stop(struct stasis_app_recording *recording)
+{
+ recording->state = STASIS_APP_RECORDING_STATE_COMPLETE;
+ return stasis_app_control_queue_control(recording->control,
+ AST_CONTROL_RECORD_STOP);
+}
+
+static int recording_pause(struct stasis_app_recording *recording)
+{
+ recording->state = STASIS_APP_RECORDING_STATE_PAUSED;
+ return stasis_app_control_queue_control(recording->control,
+ AST_CONTROL_RECORD_SUSPEND);
+}
+
+static int recording_unpause(struct stasis_app_recording *recording)
+{
+ recording->state = STASIS_APP_RECORDING_STATE_RECORDING;
+ return stasis_app_control_queue_control(recording->control,
+ AST_CONTROL_RECORD_SUSPEND);
+}
+
+static int recording_mute(struct stasis_app_recording *recording)
+{
+ if (recording->muted) {
+ /* already muted */
+ return 0;
+ }
+
+ recording->muted = 1;
+ return stasis_app_control_queue_control(recording->control,
+ AST_CONTROL_RECORD_MUTE);
+}
+
+static int recording_unmute(struct stasis_app_recording *recording)
+{
+ if (!recording->muted) {
+ /* already unmuted */
+ return 0;
+ }
+
+ return stasis_app_control_queue_control(recording->control,
+ AST_CONTROL_RECORD_MUTE);
+}
+
+recording_operation_cb operations[STASIS_APP_RECORDING_STATE_MAX][STASIS_APP_RECORDING_OPER_MAX] = {
+ [STASIS_APP_RECORDING_STATE_QUEUED][STASIS_APP_RECORDING_CANCEL] = recording_disregard,
+ [STASIS_APP_RECORDING_STATE_QUEUED][STASIS_APP_RECORDING_STOP] = recording_disregard,
+ [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_CANCEL] = recording_cancel,
+ [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_STOP] = recording_stop,
+ [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_PAUSE] = recording_pause,
+ [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_UNPAUSE] = recording_noop,
+ [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_MUTE] = recording_mute,
+ [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_UNMUTE] = recording_unmute,
+ [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_CANCEL] = recording_cancel,
+ [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_STOP] = recording_stop,
+ [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_PAUSE] = recording_noop,
+ [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_UNPAUSE] = recording_unpause,
+ [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_MUTE] = recording_mute,
+ [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_UNMUTE] = recording_unmute,
+};
+
enum stasis_app_recording_oper_results stasis_app_recording_operation(
struct stasis_app_recording *recording,
enum stasis_app_recording_media_operation operation)
{
- ast_assert(0); // TODO
- return STASIS_APP_RECORDING_OPER_FAILED;
+ recording_operation_cb cb;
+ SCOPED_AO2LOCK(lock, recording);
+
+ if (recording->state < 0 || recording->state >= STASIS_APP_RECORDING_STATE_MAX) {
+ ast_log(LOG_WARNING, "Invalid recording state %d\n",
+ recording->state);
+ return -1;
+ }
+
+ if (operation < 0 || operation >= STASIS_APP_RECORDING_OPER_MAX) {
+ ast_log(LOG_WARNING, "Invalid recording operation %d\n",
+ operation);
+ return -1;
+ }
+
+ cb = operations[recording->state][operation];
+
+ if (!cb) {
+ if (recording->state != STASIS_APP_RECORDING_STATE_RECORDING) {
+ /* So we can be specific in our error message. */
+ return STASIS_APP_RECORDING_OPER_NOT_RECORDING;
+ } else {
+ /* And, really, all operations should be valid during
+ * recording */
+ ast_log(LOG_ERROR,
+ "Unhandled operation during recording: %d\n",
+ operation);
+ return STASIS_APP_RECORDING_OPER_FAILED;
+ }
+ }
+
+ return cb(recording) ?
+ STASIS_APP_RECORDING_OPER_FAILED : STASIS_APP_RECORDING_OPER_OK;
}
static int load_module(void)
Modified: trunk/rest-api/api-docs/recordings.json
URL: http://svnview.digium.com/svn/asterisk/trunk/rest-api/api-docs/recordings.json?view=diff&rev=396331&r1=396330&r2=396331
==============================================================================
--- trunk/rest-api/api-docs/recordings.json (original)
+++ trunk/rest-api/api-docs/recordings.json Tue Aug 6 09:44:45 2013
@@ -87,6 +87,12 @@
"allowMultiple": false,
"dataType": "string"
}
+ ],
+ "errorResponses": [
+ {
+ "code": 404,
+ "reason": "Recording not found"
+ }
]
},
{
@@ -103,6 +109,12 @@
"allowMultiple": false,
"dataType": "string"
}
+ ],
+ "errorResponses": [
+ {
+ "code": 404,
+ "reason": "Recording not found"
+ }
]
}
]
@@ -124,6 +136,12 @@
"allowMultiple": false,
"dataType": "string"
}
+ ],
+ "errorResponses": [
+ {
+ "code": 404,
+ "reason": "Recording not found"
+ }
]
}
]
@@ -134,7 +152,7 @@
{
"httpMethod": "POST",
"summary": "Pause a live recording.",
- "notes": "Pausing a recording suspends silence detection, which will be restarted when the recording is unpaused.",
+ "notes": "Pausing a recording suspends silence detection, which will be restarted when the recording is unpaused. Paused time is not included in the accounting for maxDurationSeconds.",
"nickname": "pauseRecording",
"responseClass": "void",
"parameters": [
@@ -145,6 +163,16 @@
"required": true,
"allowMultiple": false,
"dataType": "string"
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 404,
+ "reason": "Recording not found"
+ },
+ {
+ "code": 409,
+ "reason": "Recording not in session"
}
]
}
@@ -166,6 +194,16 @@
"required": true,
"allowMultiple": false,
"dataType": "string"
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 404,
+ "reason": "Recording not found"
+ },
+ {
+ "code": 409,
+ "reason": "Recording not in session"
}
]
}
@@ -189,6 +227,16 @@
"allowMultiple": false,
"dataType": "string"
}
+ ],
+ "errorResponses": [
+ {
+ "code": 404,
+ "reason": "Recording not found"
+ },
+ {
+ "code": 409,
+ "reason": "Recording not in session"
+ }
]
}
]
@@ -209,6 +257,16 @@
"required": true,
"allowMultiple": false,
"dataType": "string"
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 404,
+ "reason": "Recording not found"
+ },
+ {
+ "code": 409,
+ "reason": "Recording not in session"
}
]
}
More information about the asterisk-commits
mailing list