[asterisk-commits] dlee: branch dlee/record r390021 - in /team/dlee/record: include/asterisk/ ma...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Wed May 29 09:51:06 CDT 2013
Author: dlee
Date: Wed May 29 09:51:02 2013
New Revision: 390021
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=390021
Log:
Recording work
Modified:
team/dlee/record/include/asterisk/channel.h
team/dlee/record/include/asterisk/stasis_app_recording.h
team/dlee/record/main/channel.c
team/dlee/record/res/res_stasis_http_channels.c
team/dlee/record/res/res_stasis_recording.c
team/dlee/record/res/stasis_http/resource_channels.c
team/dlee/record/res/stasis_http/resource_channels.h
team/dlee/record/rest-api/api-docs/channels.json
Modified: team/dlee/record/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/include/asterisk/channel.h?view=diff&rev=390021&r1=390020&r2=390021
==============================================================================
--- team/dlee/record/include/asterisk/channel.h (original)
+++ team/dlee/record/include/asterisk/channel.h Wed May 29 09:51:02 2013
@@ -1834,6 +1834,31 @@
/*! \brief Send empty audio to prime a channel driver */
int ast_prod(struct ast_channel *chan);
+
+/*! \brief An object which resets a channel's formats on cleanup */
+struct ast_format_janitor;
+
+/*!
+ * \brief Creates a janitor object which resets a channel's format specifiers
+ * when the janitor is cleaned up. Meant for use with RAII_VAR().
+ *
+ * \code
+ * RAII_VAR(struct ast_format_janitor *, janitor,
+ * ast_format_janitor_create(chan), ast_format_janitor_dtor);
+ * \endocde
+ *
+ * \param chan Channel to reset at destruction.
+ * \return Janitor object.
+ * \return \c NULL on error.
+ */
+struct ast_format_janitor *ast_format_janitor_create(struct ast_channel *chan);
+
+/*!
+ * \brief Destroy a janitor object, created with ast_format_janitor_create().
+ *
+ * \param janitor Janitor object to destroy. May be \c NULL.
+ */
+void ast_format_janitor_dtor(struct ast_format_janitor *janitor);
/*!
* \brief Sets read format on channel chan from capabilities
Modified: team/dlee/record/include/asterisk/stasis_app_recording.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/include/asterisk/stasis_app_recording.h?view=diff&rev=390021&r1=390020&r2=390021
==============================================================================
--- team/dlee/record/include/asterisk/stasis_app_recording.h (original)
+++ team/dlee/record/include/asterisk/stasis_app_recording.h Wed May 29 09:51:02 2013
@@ -51,6 +51,16 @@
enum stasis_app_recording_media_operation {
/*! Stop the recording operation. */
STASIS_APP_RECORDING_STOP,
+};
+
+/*! Possible actions to take if a recording already exists */
+enum stasis_app_recording_if_exists {
+ /*! Fail the recording. */
+ STASIS_APP_RECORDING_IF_EXISTS_FAIL,
+ /*! Overwrite the existing recording. */
+ STASIS_APP_RECORDING_IF_EXISTS_OVERWRITE,
+ /*! Append to the existing recording. */
+ STASIS_APP_RECORDING_IF_EXISTS_APPEND,
};
#define STASIS_APP_RECORDING_TERMINATE_INVALID 0
@@ -71,6 +81,7 @@
* \c STASIS_APP_RECORDING_TERMINATE_ANY to terminate on any DTMF
*/
char terminate_on;
+ enum stasis_app_recording_if_exists if_exists;
/*! If true, file is appended to instead of overwriting. */
int append:1;
/*! If true, a beep is played at the start of recording */
@@ -102,6 +113,16 @@
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 stasis_app_recording_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
Modified: team/dlee/record/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/main/channel.c?view=diff&rev=390021&r1=390020&r2=390021
==============================================================================
--- team/dlee/record/main/channel.c (original)
+++ team/dlee/record/main/channel.c Wed May 29 09:51:02 2013
@@ -5308,6 +5308,62 @@
}
ast_channel_unlock(chan);
return res;
+}
+
+struct ast_format_janitor {
+ struct ast_channel *channel;
+ struct ast_format rfmt;
+ struct ast_format wfmt;
+};
+
+struct ast_format_janitor *ast_format_janitor_create(struct ast_channel *chan)
+{
+ struct ast_format_janitor *janitor;
+
+ if (chan == NULL) {
+ return NULL;
+ }
+
+ janitor = ast_calloc(1, sizeof(*janitor));
+ if (janitor == NULL) {
+ return NULL;
+ }
+
+ janitor->channel = ast_channel_ref(chan);
+ ast_format_clear(&janitor->rfmt);
+ ast_format_copy(&janitor->rfmt, ast_channel_readformat(chan));
+ ast_format_clear(&janitor->wfmt);
+ ast_format_copy(&janitor->wfmt, ast_channel_writeformat(chan));
+
+ return janitor;
+}
+
+void ast_format_janitor_dtor(struct ast_format_janitor *janitor)
+{
+ int res;
+
+ if (janitor == NULL) {
+ return;
+ }
+
+ if (janitor->rfmt.id != ast_channel_readformat(janitor->channel)->id) {
+ res = ast_set_read_format(janitor->channel, &janitor->rfmt);
+ if (res != 0) {
+ ast_log(LOG_WARNING,
+ "Failed to reset channel read format");
+ }
+ }
+
+ if (janitor->wfmt.id != ast_channel_writeformat(janitor->channel)->id) {
+ res = ast_set_write_format(janitor->channel, &janitor->wfmt);
+ if (res != 0) {
+ ast_log(LOG_WARNING,
+ "Failed to reset channel write format");
+ }
+ }
+
+ janitor->channel = ast_channel_unref(janitor->channel);
+ ast_free(janitor);
}
struct set_format_trans_access {
Modified: team/dlee/record/res/res_stasis_http_channels.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/res/res_stasis_http_channels.c?view=diff&rev=390021&r1=390020&r2=390021
==============================================================================
--- team/dlee/record/res/res_stasis_http_channels.c (original)
+++ team/dlee/record/res/res_stasis_http_channels.c Wed May 29 09:51:02 2013
@@ -373,8 +373,8 @@
if (strcmp(i->name, "maxSilenceSeconds") == 0) {
args.max_silence_seconds = atoi(i->value);
} else
- if (strcmp(i->name, "append") == 0) {
- args.append = ast_true(i->value);
+ if (strcmp(i->name, "ifExists") == 0) {
+ args.if_exists = (i->value);
} else
if (strcmp(i->name, "beep") == 0) {
args.beep = ast_true(i->value);
Modified: team/dlee/record/res/res_stasis_recording.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/res/res_stasis_recording.c?view=diff&rev=390021&r1=390020&r2=390021
==============================================================================
--- team/dlee/record/res/res_stasis_recording.c (original)
+++ team/dlee/record/res/res_stasis_recording.c Wed May 29 09:51:02 2013
@@ -32,6 +32,7 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "asterisk/dsp.h"
#include "asterisk/file.h"
#include "asterisk/module.h"
#include "asterisk/stasis_app_impl.h"
@@ -40,6 +41,9 @@
/*! Number of hash buckets for recording container. Keep it prime! */
#define RECORDING_BUCKETS 127
+
+/*! Comment is ignored by most formats, so we will ignore it, too. */
+#define RECORDING_COMMENT NULL
STASIS_MESSAGE_TYPE_DEFN(stasis_app_recording_snapshot_type);
@@ -144,6 +148,29 @@
return STASIS_APP_RECORDING_TERMINATE_INVALID;
}
+enum stasis_app_recording_if_exists stasis_app_recording_if_exists_parse(
+ const char *str)
+{
+ if (ast_strlen_zero(str)) {
+ /* Default value */
+ return STASIS_APP_RECORDING_IF_EXISTS_FAIL;
+ }
+
+ if (strcmp(str, "fail") == 0) {
+ return STASIS_APP_RECORDING_IF_EXISTS_FAIL;
+ }
+
+ if (strcmp(str, "overwrite") == 0) {
+ return STASIS_APP_RECORDING_IF_EXISTS_OVERWRITE;
+ }
+
+ if (strcmp(str, "append") == 0) {
+ return STASIS_APP_RECORDING_IF_EXISTS_APPEND;
+ }
+
+ return -1;
+}
+
static void recording_publish(struct stasis_app_recording *recording)
{
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
@@ -206,13 +233,19 @@
RAII_VAR(struct stasis_app_recording *, recording,
NULL, recording_cleanup);
RAII_VAR(struct ast_filestream *, s, NULL, ast_closestream);
- int res;
- struct timeval start;
- int ms;
- long maxms;
- int ioflags;
- const char *comment = "Stasis recording";
- int check = 0;
+ RAII_VAR(struct ast_format_janitor *, janitor,
+ NULL, ast_format_janitor_dtor);
+ RAII_VAR(struct ast_dsp *, sildet, NULL, ast_dsp_free);
+
+ int check; /* Whether to check file existence before writing. */
+ int dtmf_term = 0; /* Flag to indicate termination via DTMF */
+ int ioflags; /* IO flags for writing output file */
+ int ms; /* Time remaining/waited, in milliseconds */
+ int res; /* Result variable */
+ int total_silence_ms = 0; /* Total silence currently recorded */
+ long max_silence_ms = 0; /* Max silence for termination */
+ long maxms; /* Max milliseconds to record */
+ struct timeval start; /* Start of recording */
recording = data;
ast_assert(recording != NULL);
@@ -222,15 +255,48 @@
recording_publish(recording);
ao2_unlock(recording);
+ check = 1;
ioflags = O_CREAT|O_WRONLY;
- if (recording->options->append) {
+
+ switch (recording->options->if_exists) {
+ case STASIS_APP_RECORDING_IF_EXISTS_FAIL:
+ break;
+ case STASIS_APP_RECORDING_IF_EXISTS_OVERWRITE:
+ check = 0;
ioflags |= O_APPEND;
- } else {
+ break;
+ case STASIS_APP_RECORDING_IF_EXISTS_APPEND:
+ check = 0;
ioflags |= O_TRUNC;
+ break;
+ }
+
+ if (recording->options->max_silence_seconds > 0) {
+ max_silence_ms = recording->options->max_silence_seconds * 1000;
+
+ janitor = ast_format_janitor_create(chan);
+ if (janitor == NULL) {
+ recording_fail(recording);
+ return NULL;
+ }
+
+ res = ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR);
+ if (res != 0) {
+ recording_fail(recording);
+ return NULL;
+ }
+
+ sildet = ast_dsp_new();
+ if (!sildet) {
+ recording_fail(recording);
+ return NULL;
+ }
+ ast_dsp_set_threshold(sildet,
+ ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
}
s = ast_writefile(recording->options->name,recording->options->format,
- comment, ioflags, check, AST_FILE_MODE);
+ RECORDING_COMMENT, ioflags, check, AST_FILE_MODE);
if (s == NULL) {
ast_log(LOG_WARNING, "Could not create file %s.%s\n",
recording->options->name, recording->options->format);
@@ -240,6 +306,8 @@
res = ast_auto_answer(chan);
if (res != 0) {
+ ast_debug(3, "%s: Failed to answer\n",
+ ast_channel_uniqueid(chan));
recording_fail(recording);
return NULL;
}
@@ -248,6 +316,8 @@
res = ast_play_sound(chan, "beep");
}
if (res != 0) {
+ ast_debug(3, "%s: Failed to play beep\n",
+ ast_channel_uniqueid(chan));
recording_fail(recording);
return NULL;
}
@@ -255,7 +325,6 @@
maxms = recording->options->max_duration_seconds * 1000;
start = ast_tvnow();
while ((ms = ast_remaining_ms(start, maxms))) {
- int terminated = 0;
RAII_VAR(struct ast_frame *, f, NULL, ast_frame_dtor);
ms = ast_waitfor(chan, ms);
@@ -281,12 +350,16 @@
switch (f->frametype) {
case AST_FRAME_VOICE:
res = ast_writestream(s, f);
+
+ if (res == 0 && sildet) {
+ ast_dsp_silence(sildet, f, &total_silence_ms);
+ }
break;
case AST_FRAME_VIDEO:
res = ast_writestream(s, f);
break;
case AST_FRAME_DTMF:
- terminated = recording_should_terminate(recording,
+ dtmf_term = recording_should_terminate(recording,
f->subclass.integer);
break;
default:
@@ -294,8 +367,20 @@
break;
}
- if (terminated) {
- ast_debug(3, "%s: Terminating recording\n",
+ if (dtmf_term) {
+ ast_debug(3,
+ "%s: DTMF detected. Terminating recording\n",
+ ast_channel_uniqueid(chan));
+ break;
+ }
+
+ if (total_silence_ms > max_silence_ms) {
+ /* Strip off the detected silence. Leave a second of it,
+ * for a smooth transition at the end. */
+ ast_stream_rewind(s, total_silence_ms - 1000);
+ ast_truncstream(s);
+ ast_debug(3,
+ "%s: Silence detected. Terminating recording\n",
ast_channel_uniqueid(chan));
break;
}
Modified: team/dlee/record/res/stasis_http/resource_channels.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/res/stasis_http/resource_channels.c?view=diff&rev=390021&r1=390020&r2=390021
==============================================================================
--- team/dlee/record/res/stasis_http/resource_channels.c (original)
+++ team/dlee/record/res/stasis_http/resource_channels.c Wed May 29 09:51:02 2013
@@ -256,13 +256,21 @@
options->max_duration_seconds = args->max_duration_seconds;
options->terminate_on =
stasis_app_recording_termination_parse(args->terminate_on);
- options->append = args->append;
+ options->if_exists =
+ stasis_app_recording_if_exists_parse(args->if_exists);
options->beep = args->beep;
if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) {
stasis_http_response_error(
response, 400, "Bad Request",
"terminateOn invalid");
+ return;
+ }
+
+ if (options->if_exists == -1) {
+ stasis_http_response_error(
+ response, 400, "Bad Request",
+ "ifExists invalid");
return;
}
Modified: team/dlee/record/res/stasis_http/resource_channels.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/res/stasis_http/resource_channels.h?view=diff&rev=390021&r1=390020&r2=390021
==============================================================================
--- team/dlee/record/res/stasis_http/resource_channels.h (original)
+++ team/dlee/record/res/stasis_http/resource_channels.h Wed May 29 09:51:02 2013
@@ -229,8 +229,8 @@
int max_duration_seconds;
/*! \brief Maximum duration of silence, in seconds. 0 for no limit */
int max_silence_seconds;
- /*! \brief If true, and recording already exists, append to recording */
- int append;
+ /*! \brief Action to take if a recording with the same name already exists. */
+ const char *if_exists;
/*! \brief Play beep when recording begins */
int beep;
/*! \brief DTMF input to terminate recording */
Modified: team/dlee/record/rest-api/api-docs/channels.json
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/rest-api/api-docs/channels.json?view=diff&rev=390021&r1=390020&r2=390021
==============================================================================
--- team/dlee/record/rest-api/api-docs/channels.json (original)
+++ team/dlee/record/rest-api/api-docs/channels.json Wed May 29 09:51:02 2013
@@ -507,13 +507,21 @@
}
},
{
- "name": "append",
- "description": "If true, and recording already exists, append to recording",
- "paramType": "query",
- "required": false,
- "allowMultiple": false,
- "dataType": "boolean",
- "defaultValue": false
+ "name": "ifExists",
+ "description": "Action to take if a recording with the same name already exists.",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string",
+ "defaultValue": "fail",
+ "allowableValues": {
+ "valueType": "LIST",
+ "values": [
+ "fail",
+ "overwrite",
+ "append"
+ ]
+ }
},
{
"name": "beep",
More information about the asterisk-commits
mailing list