From a75fd32212c35b41143442bd757387fad636177a Mon Sep 17 00:00:00 2001 From: "David M. Lee" Date: Wed, 3 Jul 2013 17:58:45 +0000 Subject: ARI - channel recording support This patch is the first step in adding recording support to the Asterisk REST Interface. Recordings are stored in /var/spool/recording. Since recordings may be destructive (overwriting existing files), the API rejects attempts to escape the recording directory (avoiding issues if someone attempts to record to ../../lib/sounds/greeting, for example). (closes issue ASTERISK-21594) (closes issue ASTERISK-21581) Review: https://reviewboard.asterisk.org/r/2612/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393550 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- include/asterisk/app.h | 20 +++- include/asterisk/channel.h | 12 ++ include/asterisk/file.h | 10 +- include/asterisk/paths.h | 1 + include/asterisk/stasis_app_recording.h | 203 ++++++++++++++++++++++++++++++++ include/asterisk/utils.h | 13 ++ 6 files changed, 252 insertions(+), 7 deletions(-) create mode 100644 include/asterisk/stasis_app_recording.h (limited to 'include') diff --git a/include/asterisk/app.h b/include/asterisk/app.h index 7ddacfc4e..91438a2d0 100644 --- a/include/asterisk/app.h +++ b/include/asterisk/app.h @@ -690,9 +690,23 @@ int ast_control_streamfile_w_cb(struct ast_channel *chan, /*! \brief Play a stream and wait for a digit, returning the digit that was pressed */ int ast_play_and_wait(struct ast_channel *chan, const char *fn); +/*! + * Possible actions to take if a recording already exists + * \since 12 + */ +enum ast_record_if_exists { + /*! Fail the recording. */ + AST_RECORD_IF_EXISTS_FAIL, + /*! Overwrite the existing recording. */ + AST_RECORD_IF_EXISTS_OVERWRITE, + /*! Append to the existing recording. */ + AST_RECORD_IF_EXISTS_APPEND, +}; + /*! * \brief Record a file based on input from a channel - * This function will play "auth-thankyou" upon successful recording. + * This function will play "auth-thankyou" upon successful recording if + * skip_confirmation_sound is false. * * \param chan the channel being recorded * \param playfile Filename of sound to play before recording begins @@ -706,13 +720,15 @@ int ast_play_and_wait(struct ast_channel *chan, const char *fn); * \param path Optional filesystem path to unlock * \param acceptdtmf Character of DTMF to end and accept the recording * \param canceldtmf Character of DTMF to end and cancel the recording + * \param skip_confirmation_sound If true, don't play auth-thankyou at end. Nice for custom recording prompts in apps. + * \param if_exists Action to take if recording already exists. * * \retval -1 failure or hangup * \retval 'S' Recording ended from silence timeout * \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 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); /*! * \brief Record a file based on input from a channel. Use default accept and cancel DTMF. diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index fae43d423..d61494141 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -1603,6 +1603,18 @@ void ast_channel_setwhentohangup_tv(struct ast_channel *chan, struct timeval off */ int ast_answer(struct ast_channel *chan); +/*! + * \brief Answer a channel, if it's not already answered. + * + * \param chan channel to answer + * + * \details See ast_answer() + * + * \retval 0 on success + * \retval non-zero on failure + */ +int ast_auto_answer(struct ast_channel *chan); + /*! * \brief Answer a channel * diff --git a/include/asterisk/file.h b/include/asterisk/file.h index 844b43429..372c0f7ed 100644 --- a/include/asterisk/file.h +++ b/include/asterisk/file.h @@ -64,8 +64,8 @@ enum ast_waitstream_fr_cb_values { */ typedef void (ast_waitstream_fr_cb)(struct ast_channel *chan, long ms, enum ast_waitstream_fr_cb_values val); -/*! - * \brief Streams a file +/*! + * \brief Streams a file * \param c channel to stream the file to * \param filename the name of the file you wish to stream, minus the extension * \param preflang the preferred language you wish to have the file streamed to you in @@ -86,12 +86,12 @@ int ast_streamfile(struct ast_channel *c, const char *filename, const char *pref */ int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits); -/*! - * \brief Stops a stream +/*! + * \brief Stops a stream * * \param c The channel you wish to stop playback on * - * Stop playback of a stream + * Stop playback of a stream * * \retval 0 always * diff --git a/include/asterisk/paths.h b/include/asterisk/paths.h index 14da7aaf9..ea0c56123 100644 --- a/include/asterisk/paths.h +++ b/include/asterisk/paths.h @@ -23,6 +23,7 @@ extern const char *ast_config_AST_CONFIG_FILE; extern const char *ast_config_AST_MODULE_DIR; extern const char *ast_config_AST_SPOOL_DIR; extern const char *ast_config_AST_MONITOR_DIR; +extern const char *ast_config_AST_RECORDING_DIR; extern const char *ast_config_AST_VAR_DIR; extern const char *ast_config_AST_DATA_DIR; extern const char *ast_config_AST_LOG_DIR; diff --git a/include/asterisk/stasis_app_recording.h b/include/asterisk/stasis_app_recording.h new file mode 100644 index 000000000..9c9930406 --- /dev/null +++ b/include/asterisk/stasis_app_recording.h @@ -0,0 +1,203 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * David M. Lee, II + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#ifndef _ASTERISK_STASIS_APP_RECORDING_H +#define _ASTERISK_STASIS_APP_RECORDING_H + +/*! \file + * + * \brief Stasis Application Recording API. See \ref res_stasis "Stasis + * Application API" for detailed documentation. + * + * \author David M. Lee, II + * \since 12 + */ + +#include "asterisk/app.h" +#include "asterisk/stasis_app.h" + +/*! Opaque struct for handling the recording of media to a file. */ +struct stasis_app_recording; + +/*! State of a recording operation */ +enum stasis_app_recording_state { + /*! The recording has not started yet */ + STASIS_APP_RECORDING_STATE_QUEUED, + /*! The media is currently recording */ + STASIS_APP_RECORDING_STATE_RECORDING, + /*! The media is currently paused */ + STASIS_APP_RECORDING_STATE_PAUSED, + /*! The media has stopped recording */ + STASIS_APP_RECORDING_STATE_COMPLETE, + /*! The media has stopped playing */ + STASIS_APP_RECORDING_STATE_FAILED, +}; + +/*! Valid operation for controlling a recording. */ +enum stasis_app_recording_media_operation { + /*! Stop the recording operation. */ + STASIS_APP_RECORDING_STOP, +}; + +#define STASIS_APP_RECORDING_TERMINATE_INVALID 0 +#define STASIS_APP_RECORDING_TERMINATE_NONE -1 +#define STASIS_APP_RECORDING_TERMINATE_ANY -2 + +struct stasis_app_recording_options { + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(name); /*!< name Name of the recording. */ + AST_STRING_FIELD(format); /*!< Format to be recorded (wav, gsm, etc.) */ + ); + /*! Number of seconds of silence before ending the recording. */ + int max_silence_seconds; + /*! Maximum recording duration. 0 for no maximum. */ + int max_duration_seconds; + /*! Which DTMF to use to terminate the recording + * \c STASIS_APP_RECORDING_TERMINATE_NONE to terminate only on hangup + * \c STASIS_APP_RECORDING_TERMINATE_ANY to terminate on any DTMF + */ + char terminate_on; + /*! How to handle recording when a file already exists */ + enum ast_record_if_exists if_exists; + /*! If true, a beep is played at the start of recording */ + int beep:1; +}; + +/*! + * \brief Allocate a recording options object. + * + * Clean up with ao2_cleanup(). + * + * \param name Name of the recording. + * \param format Format to record in. + * \return Newly allocated options object. + * \return \c NULL on error. + */ +struct stasis_app_recording_options *stasis_app_recording_options_create( + const char *name, const char *format); + +/*! + * \brief Parse a string into the recording termination enum. + * + * \param str String to parse. + * \return DTMF value to terminate on. + * \return \c STASIS_APP_RECORDING_TERMINATE_NONE to not terminate on DTMF. + * \return \c STASIS_APP_RECORDING_TERMINATE_ANY to terminate on any DTMF. + * \return \c STASIS_APP_RECORDING_TERMINATE_INVALID if input was invalid. + */ +char stasis_app_recording_termination_parse(const char *str); + +/*! + * \brief Parse a string into the if_exists enum. + * + * \param str String to parse. + * \return How to handle an existing file. + * \return -1 on error. + */ +enum ast_record_if_exists stasis_app_recording_if_exists_parse( + const char *str); + +/*! + * \brief Record media from a channel. + * + * A reference to the \a options object may be kept, so it MUST NOT be modified + * after calling this function. + * + * On error, \c errno is set to indicate the failure reason. + * - \c EINVAL: Invalid input. + * - \c EEXIST: A recording with that name is in session. + * - \c ENOMEM: Out of memory. + * + * \param control Control for \c res_stasis. + * \param options Recording options. + * \return Recording control object. + * \return \c NULL on error. + */ +struct stasis_app_recording *stasis_app_control_record( + struct stasis_app_control *control, + struct stasis_app_recording_options *options); + +/*! + * \brief Gets the current state of a recording operation. + * + * \param recording Recording control object. + * \return The state of the \a recording object. + */ +enum stasis_app_recording_state stasis_app_recording_get_state( + struct stasis_app_recording *recording); + +/*! + * \brief Gets the unique name of a recording object. + * + * \param recording Recording control object. + * \return \a recording's name. + * \return \c NULL if \a recording ic \c NULL + */ +const char *stasis_app_recording_get_name( + struct stasis_app_recording *recording); + +/*! + * \brief Finds the recording object with the given name. + * + * \param name Name of the recording object to find. + * \return Associated \ref stasis_app_recording object. + * \return \c NULL if \a name not found. + */ +struct stasis_app_recording *stasis_app_recording_find_by_name(const char *name); + +/*! + * \brief Construct a JSON model of a recording. + * + * \param recording Recording to conver. + * \return JSON model. + * \return \c NULL on error. + */ +struct ast_json *stasis_app_recording_to_json( + const struct stasis_app_recording *recording); + +/*! + * \brief Possible results from a recording operation. + */ +enum stasis_app_recording_oper_results { + /*! Operation completed successfully. */ + STASIS_APP_RECORDING_OPER_OK, + /*! Operation failed. */ + STASIS_APP_RECORDING_OPER_FAILED, + /*! Operation failed b/c recording is not in session. */ + STASIS_APP_RECORDING_OPER_NOT_RECORDING, +}; + +/*! + * \brief Controls the media for a given recording operation. + * + * \param recording Recording control object. + * \param control Media control operation. + * \return \c STASIS_APP_RECORDING_OPER_OK on success. + * \return \ref stasis_app_recording_oper_results indicating failure. + */ +enum stasis_app_recording_oper_results stasis_app_recording_operation( + struct stasis_app_recording *recording, + enum stasis_app_recording_media_operation operation); + +/*! + * \brief Message type for recording updates. The data is an + * \ref ast_channel_blob. + */ +struct stasis_message_type *stasis_app_recording_snapshot_type(void); + +#endif /* _ASTERISK_STASIS_APP_RECORDING_H */ diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h index ce6db0965..184850905 100644 --- a/include/asterisk/utils.h +++ b/include/asterisk/utils.h @@ -718,6 +718,19 @@ void ast_enable_packet_fragmentation(int sock); */ int ast_mkdir(const char *path, int mode); +/*! + * \brief Recursively create directory path, but only if it resolves within + * the given \a base_path. + * + * If \a base_path does not exist, it will not be created and this function + * returns \c EPERM. + * + * \param path The directory path to create + * \param mode The permissions with which to try to create the directory + * \return 0 on success or an error code otherwise + */ +int ast_safe_mkdir(const char *base_path, const char *path, int mode); + #define ARRAY_LEN(a) (size_t) (sizeof(a) / sizeof(0[a])) -- cgit v1.2.3