mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 18:55:19 +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 47250b716c)
			
			
This commit is contained in:
		
				
					committed by
					
						 George Joseph
						George Joseph
					
				
			
			
				
	
			
			
			
						parent
						
							6d2e05da56
						
					
				
				
					commit
					5863873d10
				
			| @@ -119,8 +119,32 @@ | |||||||
| 				</variable> | 				</variable> | ||||||
| 			</variablelist> | 			</variablelist> | ||||||
| 		</description> | 		</description> | ||||||
|  | 		<see-also> | ||||||
|  | 			<ref type="function">RECORDING_INFO</ref> | ||||||
|  | 		</see-also> | ||||||
| 	</application> | 	</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' | #define OPERATOR_KEY '0' | ||||||
| @@ -221,13 +245,65 @@ static int create_destination_directory(const char *path) | |||||||
| 	return ast_mkdir(directory, 0777); | 	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) | static int record_exec(struct ast_channel *chan, const char *data) | ||||||
| { | { | ||||||
|  | 	struct ast_datastore *ds; | ||||||
| 	int res = 0; | 	int res = 0; | ||||||
| 	char *ext = NULL, *opts[0]; | 	char *ext = NULL, *opts[0]; | ||||||
| 	char *parse; | 	char *parse; | ||||||
| 	int i = 0; | 	int i = 0; | ||||||
| 	char tmp[PATH_MAX]; | 	char tmp[PATH_MAX]; | ||||||
|  | 	struct recording_data *recdata; | ||||||
|  |  | ||||||
| 	struct ast_filestream *s = NULL; | 	struct ast_filestream *s = NULL; | ||||||
| 	struct ast_frame *f = NULL; | 	struct ast_frame *f = NULL; | ||||||
| @@ -255,6 +331,31 @@ static int record_exec(struct ast_channel *chan, const char *data) | |||||||
| 	struct timeval start; | 	struct timeval start; | ||||||
| 	const char *status_response = "ERROR"; | 	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 */ | 	/* 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 */ | 	if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */ | ||||||
| 		ast_log(LOG_WARNING, "Record requires an argument (filename)\n"); | 		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); | 		ast_channel_stop_silence_generator(chan, silgen); | ||||||
|  |  | ||||||
| out: | out: | ||||||
|  | 	recdata->duration = ast_tvdiff_ms(ast_tvnow(), start); | ||||||
|  |  | ||||||
| 	if ((silence > 0) && rfmt) { | 	if ((silence > 0) && rfmt) { | ||||||
| 		res = ast_set_read_format(chan, rfmt); | 		res = ast_set_read_format(chan, rfmt); | ||||||
| 		if (res) { | 		if (res) { | ||||||
| @@ -533,14 +636,25 @@ out: | |||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static struct ast_custom_function acf_recording_info = { | ||||||
|  | 	.name = "RECORDING_INFO", | ||||||
|  | 	.read = recording_info_read, | ||||||
|  | }; | ||||||
|  |  | ||||||
| static int unload_module(void) | 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) | 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"); | AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial Record Application"); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user