]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
app_record: Add RECORDING_INFO function.
authorNaveen Albert <asterisk@phreaknet.org>
Mon, 22 Jan 2024 12:23:47 +0000 (07:23 -0500)
committerGeorge Joseph <gjoseph@sangoma.com>
Thu, 26 Jun 2025 18:15:05 +0000 (12:15 -0600)
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 47250b716c7fc8b80a945e91dbc4b2889d659c1d)

apps/app_record.c

index b275957de74e6219bccc75d7c6be5ca328c8df1c..156be0fad319358665ee089c137c0deb99414e83 100644 (file)
                                </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");