mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-02 11:06:31 +00:00
app_record: Add RECORDING_INFO function.
Add a function that can be used to retrieve info
about a previous recording, such as its duration.
This is being added as a function to avoid possibly
trampling on dialplan variables, and could be extended
to provide other information in the future.
Resolves: #548
UserNote: The RECORDING_INFO function can now be used
to retrieve the duration of a recording.
(cherry picked from commit b5a04ed7ac
)
This commit is contained in:
committed by
George Joseph
parent
c7582d6536
commit
beac79f092
@@ -119,8 +119,32 @@
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">RECORDING_INFO</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
|
||||
<function name="RECORDING_INFO" language="en_US">
|
||||
<synopsis>
|
||||
Retrieve information about a recording previously created using the Record application
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="property" required="true">
|
||||
<para>The property about the recording to retrieve.</para>
|
||||
<enumlist>
|
||||
<enum name="duration">
|
||||
<para>The duration, in milliseconds, of the recording.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Returns information about the previous recording created by <literal>Record</literal>.
|
||||
This function cannot be used if no recordings have yet been completed.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Record</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
***/
|
||||
|
||||
#define OPERATOR_KEY '0'
|
||||
@@ -221,13 +245,65 @@ static int create_destination_directory(const char *path)
|
||||
return ast_mkdir(directory, 0777);
|
||||
}
|
||||
|
||||
struct recording_data {
|
||||
unsigned long duration; /* Duration, in ms */
|
||||
};
|
||||
|
||||
static void recording_data_free(void *data)
|
||||
{
|
||||
ast_free(data);
|
||||
}
|
||||
|
||||
static const struct ast_datastore_info recording_data_info = {
|
||||
.type = "RECORDING_INFO",
|
||||
.destroy = recording_data_free,
|
||||
};
|
||||
|
||||
static int recording_info_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
struct ast_datastore *ds;
|
||||
struct recording_data *recdata;
|
||||
|
||||
*buf = '\0';
|
||||
|
||||
if (!chan) {
|
||||
ast_log(LOG_ERROR, "%s() can only be executed on a channel\n", cmd);
|
||||
return -1;
|
||||
} else if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_ERROR, "%s() requires an argument\n", cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_channel_lock(chan);
|
||||
ds = ast_channel_datastore_find(chan, &recording_data_info, NULL);
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
if (!ds) {
|
||||
ast_log(LOG_ERROR, "No recordings have completed on channel %s\n", ast_channel_name(chan));
|
||||
return -1;
|
||||
}
|
||||
|
||||
recdata = ds->data;
|
||||
|
||||
if (!strcasecmp(data, "duration")) {
|
||||
snprintf(buf, len, "%ld", recdata->duration);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Invalid property type: %s\n", data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int record_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
struct ast_datastore *ds;
|
||||
int res = 0;
|
||||
char *ext = NULL, *opts[0];
|
||||
char *parse;
|
||||
int i = 0;
|
||||
char tmp[PATH_MAX];
|
||||
struct recording_data *recdata;
|
||||
|
||||
struct ast_filestream *s = NULL;
|
||||
struct ast_frame *f = NULL;
|
||||
@@ -255,6 +331,31 @@ static int record_exec(struct ast_channel *chan, const char *data)
|
||||
struct timeval start;
|
||||
const char *status_response = "ERROR";
|
||||
|
||||
/* Retrieve or create the datastore */
|
||||
ast_channel_lock(chan);
|
||||
if (!(ds = ast_channel_datastore_find(chan, &recording_data_info, NULL))) {
|
||||
if (!(ds = ast_datastore_alloc(&recording_data_info, NULL))) {
|
||||
ast_log(LOG_ERROR, "Unable to allocate new datastore.\n");
|
||||
ast_channel_unlock(chan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(recdata = ast_calloc(1, sizeof(*recdata)))) {
|
||||
ast_datastore_free(ds);
|
||||
ast_channel_unlock(chan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ds->data = recdata;
|
||||
ast_channel_datastore_add(chan, ds);
|
||||
} else {
|
||||
recdata = ds->data;
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
/* Reset, in case already set */
|
||||
recdata->duration = 0;
|
||||
|
||||
/* The next few lines of code parse out the filename and header from the input string */
|
||||
if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
|
||||
ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
|
||||
@@ -517,6 +618,8 @@ static int record_exec(struct ast_channel *chan, const char *data)
|
||||
ast_channel_stop_silence_generator(chan, silgen);
|
||||
|
||||
out:
|
||||
recdata->duration = ast_tvdiff_ms(ast_tvnow(), start);
|
||||
|
||||
if ((silence > 0) && rfmt) {
|
||||
res = ast_set_read_format(chan, rfmt);
|
||||
if (res) {
|
||||
@@ -533,14 +636,25 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct ast_custom_function acf_recording_info = {
|
||||
.name = "RECORDING_INFO",
|
||||
.read = recording_info_read,
|
||||
};
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app);
|
||||
int res;
|
||||
res = ast_custom_function_unregister(&acf_recording_info);
|
||||
res |= ast_unregister_application(app);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application_xml(app, record_exec);
|
||||
int res;
|
||||
res = ast_register_application_xml(app, record_exec);
|
||||
res |= ast_custom_function_register(&acf_recording_info);
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial Record Application");
|
||||
|
Reference in New Issue
Block a user