mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-12 15:45:18 +00:00
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
This commit is contained in:
@@ -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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user