diff options
author | David M. Lee <dlee@digium.com> | 2013-08-06 14:44:45 +0000 |
---|---|---|
committer | David M. Lee <dlee@digium.com> | 2013-08-06 14:44:45 +0000 |
commit | c79084879427750dc848834a8390bb0c8468f24b (patch) | |
tree | 2e6c4371cda2c73172642b267aa54dea469d600d /res | |
parent | b97d318b7bc31b47fbd4b74421e351095f05139d (diff) |
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/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396331 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res')
-rw-r--r-- | res/ari/resource_recordings.c | 78 | ||||
-rw-r--r-- | res/ari/resource_recordings.h | 2 | ||||
-rw-r--r-- | res/res_ari_recordings.c | 11 | ||||
-rw-r--r-- | res/res_stasis_recording.c | 130 |
4 files changed, 204 insertions, 17 deletions
diff --git a/res/ari/resource_recordings.c b/res/ari/resource_recordings.c index 46439ff0b..f87ff0a0a 100644 --- a/res/ari/resource_recordings.c +++ b/res/ari/resource_recordings.c @@ -71,27 +71,81 @@ void ast_ari_get_live_recording(struct ast_variable *headers, 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) +{ + control_recording(args->recording_name, STASIS_APP_RECORDING_STOP, + 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_pause_recording\n"); + control_recording(args->recording_name, STASIS_APP_RECORDING_PAUSE, + response); } -void ast_ari_unpause_recording(struct ast_variable *headers, struct ast_unpause_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_unpause_recording\n"); + control_recording(args->recording_name, STASIS_APP_RECORDING_UNPAUSE, + response); } -void ast_ari_mute_recording(struct ast_variable *headers, struct ast_mute_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_mute_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) + +void ast_ari_unmute_recording(struct ast_variable *headers, + struct ast_unmute_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_UNMUTE, + response); } diff --git a/res/ari/resource_recordings.h b/res/ari/resource_recordings.h index e3ee88be8..2529766e7 100644 --- a/res/ari/resource_recordings.h +++ b/res/ari/resource_recordings.h @@ -134,7 +134,7 @@ struct ast_pause_recording_args { /*! * \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 diff --git a/res/res_ari_recordings.c b/res/res_ari_recordings.c index 01ad49a65..77fc830cf 100644 --- a/res/res_ari_recordings.c +++ b/res/res_ari_recordings.c @@ -295,6 +295,7 @@ static void ast_ari_get_live_recording_cb( break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ + case 404: /* Recording not found */ is_valid = 1; break; default: @@ -351,6 +352,7 @@ static void ast_ari_cancel_recording_cb( break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ + case 404: /* Recording not found */ is_valid = 1; break; default: @@ -407,6 +409,7 @@ static void ast_ari_stop_recording_cb( break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ + case 404: /* Recording not found */ is_valid = 1; break; default: @@ -463,6 +466,8 @@ static void ast_ari_pause_recording_cb( 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 @@ static void ast_ari_unpause_recording_cb( 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 @@ static void ast_ari_mute_recording_cb( 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 @@ static void ast_ari_unmute_recording_cb( 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: diff --git a/res/res_stasis_recording.c b/res/res_stasis_recording.c index f94f42895..575ccae1e 100644 --- a/res/res_stasis_recording.c +++ b/res/res_stasis_recording.c @@ -59,11 +59,13 @@ struct stasis_app_recording { 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 @@ static const char *state_to_string(enum stasis_app_recording_state state) 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 @@ static void *record_file(struct stasis_app_control *control, } 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 @@ struct ast_json *stasis_app_recording_to_json( 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) |