From 6e36042f64f54785afa4815f262c1e0e08ce596f Mon Sep 17 00:00:00 2001
From: Jonathan Rose <jrose@digium.com>
Date: Fri, 11 Mar 2011 18:54:45 +0000
Subject: [PATCH] Mix Monitor:  Now with r and t options.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@310373 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 CHANGES                      |   7 +
 apps/app_mixmonitor.c        | 246 +++++++++++++++++++++++++++--------
 include/asterisk/audiohook.h |  11 ++
 main/audiohook.c             |  65 ++++++---
 4 files changed, 255 insertions(+), 74 deletions(-)

diff --git a/CHANGES b/CHANGES
index f6ad2ffe83..dde4ffcb31 100644
--- a/CHANGES
+++ b/CHANGES
@@ -70,6 +70,13 @@ Calendaring
  * Added setvar option to calendar.conf to allow setting channel variables on
    notification channels.
 
+MixMonitor
+--------------------------
+ * Added two new options, r and t with file name arguments to record 
+   single direction (unmixed) audio recording separate from the bidirectional
+   (mixed) recording.  The mixed file name argument is optional now as long
+   as at least one recording option is used.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 1.6.2 to Asterisk 1.8 ----------------
 ------------------------------------------------------------------------------
diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c
index 910e72cb1e..29463e2536 100644
--- a/apps/app_mixmonitor.c
+++ b/apps/app_mixmonitor.c
@@ -89,6 +89,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 						of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
 						<argument name="x" required="true" />
 					</option>
+					<option name="r">
+						<argument name="file" required="true" />
+						<para>Use the specified file to record the <emphasis>receive</emphasis> audio feed.
+						Like with the basic filename argument, if an absolute path isn't given, it will create
+						the file in the configured monitoring directory.</para>
+
+					</option>
+					<option name="t">
+						<argument name="file" required="true" />
+						<para>Use the specified file to record the <emphasis>transmit</emphasis> audio feed.
+						Like with the basic filename argument, if an absolute path isn't given, it will create
+						the file in the configured monitoring directory.</para>
+					</option>
 				</optionlist>
 			</parameter>
 			<parameter name="command">
@@ -159,6 +172,8 @@ static const char * const mixmonitor_spy_type = "MixMonitor";
 struct mixmonitor {
 	struct ast_audiohook audiohook;
 	char *filename;
+	char *filename_read;
+	char *filename_write;
 	char *post_process;
 	char *name;
 	unsigned int flags;
@@ -172,13 +187,18 @@ enum mixmonitor_flags {
 	MUXFLAG_VOLUME = (1 << 3),
 	MUXFLAG_READVOLUME = (1 << 4),
 	MUXFLAG_WRITEVOLUME = (1 << 5),
+	MUXFLAG_READ = (1 << 6),
+	MUXFLAG_WRITE = (1 << 7),
+	MUXFLAG_COMBINED = (1 << 8),
 };
 
 enum mixmonitor_args {
 	OPT_ARG_READVOLUME = 0,
 	OPT_ARG_WRITEVOLUME,
 	OPT_ARG_VOLUME,
-	OPT_ARG_ARRAY_SIZE,
+	OPT_ARG_WRITENAME,
+	OPT_ARG_READNAME,
+	OPT_ARG_ARRAY_SIZE,	/* Always last element of the enum */
 };
 
 AST_APP_OPTIONS(mixmonitor_opts, {
@@ -187,6 +207,8 @@ AST_APP_OPTIONS(mixmonitor_opts, {
 	AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
 	AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
 	AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
+	AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
+	AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
 });
 
 struct mixmonitor_ds {
@@ -197,7 +219,11 @@ struct mixmonitor_ds {
 	/* The filestream is held in the datastore so it can be stopped
 	 * immediately during stop_mixmonitor or channel destruction. */
 	int fs_quit;
+
 	struct ast_filestream *fs;
+	struct ast_filestream *fs_read;
+	struct ast_filestream *fs_write;
+
 	struct ast_audiohook *audiohook;
 };
 
@@ -207,11 +233,31 @@ struct mixmonitor_ds {
  */
 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
 {
+	unsigned char quitting = 0;
+
 	if (mixmonitor_ds->fs) {
+		quitting = 1;
 		ast_closestream(mixmonitor_ds->fs);
 		mixmonitor_ds->fs = NULL;
+		ast_verb(2, "MixMonitor close filestream (mixed)\n");
+	}
+
+	if (mixmonitor_ds->fs_read) {
+		quitting = 1;
+		ast_closestream(mixmonitor_ds->fs_read);
+		mixmonitor_ds->fs_read = NULL;
+		ast_verb(2, "MixMonitor close filestream (read)\n");
+	}
+
+	if (mixmonitor_ds->fs_write) {
+		quitting = 1;
+		ast_closestream(mixmonitor_ds->fs_write);
+		mixmonitor_ds->fs_write = NULL;
+		ast_verb(2, "MixMonitor close filestream (write)\n");
+	}
+
+	if (quitting) {
 		mixmonitor_ds->fs_quit = 1;
-		ast_verb(2, "MixMonitor close filestream\n");
 	}
 }
 
@@ -269,18 +315,51 @@ static void mixmonitor_free(struct mixmonitor *mixmonitor)
 		if (mixmonitor->mixmonitor_ds) {
 			ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
 			ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
+			ast_free(mixmonitor->filename_write);
+			ast_free(mixmonitor->filename_read);
 			ast_free(mixmonitor->mixmonitor_ds);
+			ast_free(mixmonitor->name);
+			ast_free(mixmonitor->post_process);
 		}
 		ast_free(mixmonitor);
 	}
 }
+
+static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag)
+{
+	/* Initialize the file if not already done so */
+	char *ext = NULL;
+	char *last_slash = NULL;
+	if (!ast_strlen_zero(filename)) {
+		if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
+			*oflags = O_CREAT | O_WRONLY;
+			*oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
+			
+			last_slash = strrchr(filename, '/');
+
+			if ((ext = strrchr(filename, '.')) && (ext > last_slash)) {
+				*(ext++) = '\0';
+			} else {
+				ext = "raw";
+			}
+
+			if (!(*fs = ast_writefile(filename, ext, NULL, *oflags, 0, 0666))) {
+				ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, ext);
+				*errflag = 1;
+			}
+		}
+	}
+}
+
 static void *mixmonitor_thread(void *obj) 
 {
 	struct mixmonitor *mixmonitor = obj;
+
 	struct ast_filestream **fs = NULL;
+	struct ast_filestream **fs_read = NULL;
+	struct ast_filestream **fs_write = NULL;
+
 	unsigned int oflags;
-	char *ext;
-	char *last_slash;
 	int errflag = 0;
 	struct ast_format format_slin;
 
@@ -288,13 +367,18 @@ static void *mixmonitor_thread(void *obj)
 	ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
 
 	fs = &mixmonitor->mixmonitor_ds->fs;
+	fs_read = &mixmonitor->mixmonitor_ds->fs_read;
+	fs_write = &mixmonitor->mixmonitor_ds->fs_write;
 
 	/* The audiohook must enter and exit the loop locked */
 	ast_audiohook_lock(&mixmonitor->audiohook);
 	while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
 		struct ast_frame *fr = NULL;
+		struct ast_frame *fr_read = NULL;
+		struct ast_frame *fr_write = NULL;
 
-		if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, &format_slin))) {
+		if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, &format_slin,
+						&fr_read, &fr_write))) {
 			ast_audiohook_trigger_wait(&mixmonitor->audiohook);
 
 			if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
@@ -309,25 +393,28 @@ static void *mixmonitor_thread(void *obj)
 
 		if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
 			ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
-			/* Initialize the file if not already done so */
-			if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
-				oflags = O_CREAT | O_WRONLY;
-				oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
-
-				last_slash = strrchr(mixmonitor->filename, '/');
-				if ((ext = strrchr(mixmonitor->filename, '.')) && (ext > last_slash))
-					*(ext++) = '\0';
-				else
-					ext = "raw";
-
-				if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) {
-					ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
-					errflag = 1;
+			mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag);
+			mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag);
+			mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag);
+
+			/* Write out the frame(s) */
+			if ((*fs_read) && (fr_read)) {
+				struct ast_frame *cur;
+
+				for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
+					ast_writestream(*fs_read, cur);
 				}
 			}
 
-			/* Write out the frame(s) */
-			if (*fs) {
+			if ((*fs_write) && (fr_write)) {
+				struct ast_frame *cur;
+
+				for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
+					ast_writestream(*fs_write, cur);
+				}
+			}
+
+			if ((*fs) && (fr)) {
 				struct ast_frame *cur;
 
 				for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
@@ -337,7 +424,19 @@ static void *mixmonitor_thread(void *obj)
 			ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
 		}
 		/* All done! free it. */
-		ast_frame_free(fr, 0);
+		if (fr) {
+			ast_frame_free(fr, 0);
+		}
+		if (fr_read) {
+			ast_frame_free(fr_read, 0);
+		}
+		if (fr_write) {
+			ast_frame_free(fr_write, 0);
+		}
+
+		fr = NULL;
+		fr_write = NULL;
+		fr_read = NULL;
 
 		ast_audiohook_lock(&mixmonitor->audiohook);
 	}
@@ -396,15 +495,14 @@ static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel
 	return 0;
 }
 
-static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
-				  int readvol, int writevol, const char *post_process) 
+static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
+				  unsigned int flags, int readvol, int writevol,
+				  const char *post_process, const char *filename_write,
+				  const char *filename_read) 
 {
 	pthread_t thread;
 	struct mixmonitor *mixmonitor;
 	char postprocess2[1024] = "";
-	size_t len;
-
-	len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
 
 	postprocess2[0] = 0;
 	/* If a post process system command is given attach it to the structure */
@@ -412,18 +510,16 @@ static void launch_monitor_thread(struct ast_channel *chan, const char *filename
 		char *p1, *p2;
 
 		p1 = ast_strdupa(post_process);
-		for (p2 = p1; *p2 ; p2++) {
+		for (p2 = p1; *p2; p2++) {
 			if (*p2 == '^' && *(p2+1) == '{') {
 				*p2 = '$';
 			}
 		}
 		pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
-		if (!ast_strlen_zero(postprocess2))
-			len += strlen(postprocess2) + 1;
 	}
 
 	/* Pre-allocate mixmonitor structure and spy */
-	if (!(mixmonitor = ast_calloc(1, len))) {
+	if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
 		return;
 	}
 
@@ -445,15 +541,24 @@ static void launch_monitor_thread(struct ast_channel *chan, const char *filename
 		mixmonitor_free(mixmonitor);
 		return;
 	}
-	mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
-	strcpy(mixmonitor->name, chan->name);
+
+	mixmonitor->name = ast_strdup(chan->name);
+
 	if (!ast_strlen_zero(postprocess2)) {
-		mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
-		strcpy(mixmonitor->post_process, postprocess2);
+		mixmonitor->post_process = ast_strdup(postprocess2);
+	}
+
+	if (!ast_strlen_zero(filename)) {
+		mixmonitor->filename = ast_strdup(filename);
+	}
+
+	if (!ast_strlen_zero(filename_write)) {
+		mixmonitor->filename_write = ast_strdup(filename_write);
 	}
 
-	mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
-	strcpy(mixmonitor->filename, filename);
+	if (!ast_strlen_zero(filename_read)) {
+		mixmonitor->filename_read = ast_strdup(filename_read);
+	}
 
 	ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
 
@@ -473,11 +578,39 @@ static void launch_monitor_thread(struct ast_channel *chan, const char *filename
 	ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
 }
 
+/* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
+/* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
+static char *filename_parse(char *filename, char *buffer, size_t len)
+{
+	char *slash;
+	if (ast_strlen_zero(filename)) {
+		ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
+	} else if (filename[0] != '/') {
+		char *build;
+		build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
+		sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
+		filename = build;
+	}
+
+	ast_copy_string(buffer, filename, len);
+
+	if ((slash = strrchr(filename, '/'))) {
+		*slash = '\0';
+	}
+	ast_mkdir(filename, 0777);
+
+	return buffer;
+}
+
 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
 {
 	int x, readvol = 0, writevol = 0;
-	struct ast_flags flags = {0};
-	char *parse, *tmp, *slash;
+	char *filename_read = NULL;
+	char *filename_write = NULL;
+	char filename_buffer[1024] = "";
+
+	struct ast_flags flags = { 0 };
+	char *parse;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(filename);
 		AST_APP_ARG(options);
@@ -485,18 +618,13 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data)
 	);
 	
 	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
+		ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
 		return -1;
 	}
 
 	parse = ast_strdupa(data);
 
 	AST_STANDARD_APP_ARGS(args, parse);
-	
-	if (ast_strlen_zero(args.filename)) {
-		ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
-		return -1;
-	}
 
 	if (args.options) {
 		char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
@@ -532,24 +660,30 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data)
 				readvol = writevol = get_volfactor(x);
 			}
 		}
+
+		if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
+			filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
+		}
+
+		if (ast_test_flag(&flags, MUXFLAG_READ)) {
+			filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
+		}
 	}
 
-	/* if not provided an absolute path, use the system-configured monitoring directory */
-	if (args.filename[0] != '/') {
-		char *build;
+	/* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
 
-		build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
-		sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
-		args.filename = build;
+	if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
+		ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
+		return -1;
 	}
 
-	tmp = ast_strdupa(args.filename);
-	if ((slash = strrchr(tmp, '/')))
-		*slash = '\0';
-	ast_mkdir(tmp, 0777);
+	/* If filename exists, try to create directories for it */
+	if (!(ast_strlen_zero(args.filename))) {
+		args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
+	}
 
 	pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
-	launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
+	launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process, filename_write, filename_read);
 
 	return 0;
 }
diff --git a/include/asterisk/audiohook.h b/include/asterisk/audiohook.h
index 798a6d6e00..fd1da91dd3 100644
--- a/include/asterisk/audiohook.h
+++ b/include/asterisk/audiohook.h
@@ -151,6 +151,17 @@ int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohoo
  */
 struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, struct ast_format *format);
 
+/*! \brief Reads a frame in from the audiohook structure in mixed audio mode and copies read and write frame data to provided arguments.
+ * \param audiohook Audiohook structure
+ * \param samples Number of samples wanted
+ * \param direction Direction the audio frame came from
+ * \param format Format of frame remote side wants back
+ * \param ast_frame read_frame - if available, we'll copy the read buffer to this.
+ * \param ast_frame write_frame - if available, we'll copy the write buffer to this.
+ * \return Returns frame on success, NULL on failure
+ */
+struct ast_frame *ast_audiohook_read_frame_all(struct ast_audiohook *audiohook, size_t samples, struct ast_format *format, struct ast_frame **read_frame, struct ast_frame **write_frame);
+
 /*! \brief Attach audiohook to channel
  * \param chan Channel
  * \param audiohook Audiohook structure
diff --git a/main/audiohook.c b/main/audiohook.c
index 9fd2ca957c..bb510e333f 100644
--- a/main/audiohook.c
+++ b/main/audiohook.c
@@ -238,7 +238,7 @@ static struct ast_frame *audiohook_read_frame_single(struct ast_audiohook *audio
 	return ast_frdup(&frame);
 }
 
-static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audiohook, size_t samples)
+static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audiohook, size_t samples, struct ast_frame **read_reference, struct ast_frame **write_reference)
 {
 	int i = 0, usable_read, usable_write;
 	short buf1[samples], buf2[samples], *read_buf = NULL, *write_buf = NULL, *final_buf = NULL, *data1 = NULL, *data2 = NULL;
@@ -313,14 +313,24 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho
 	/* Basically we figure out which buffer to use... and if mixing can be done here */
 	if (!read_buf && !write_buf)
 		return NULL;
-	else if (read_buf && write_buf) {
+
+	if (read_buf) {
+		final_buf = buf1;
+		frame.data.ptr = final_buf;
+		*read_reference = ast_frdup(&frame);
+	}
+
+	if (write_buf) {
+		final_buf = buf2;
+		frame.data.ptr = final_buf;
+		*write_reference = ast_frdup(&frame);
+	}
+
+	if (read_buf && write_buf) {
 		for (i = 0, data1 = read_buf, data2 = write_buf; i < samples; i++, data1++, data2++)
 			ast_slinear_saturated_add(data1, data2);
 		final_buf = buf1;
-	} else if (read_buf)
-		final_buf = buf1;
-	else if (write_buf)
-		final_buf = buf2;
+	}
 
 	/* Make the final buffer part of the frame, so it gets duplicated fine */
 	frame.data.ptr = final_buf;
@@ -329,14 +339,7 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho
 	return ast_frdup(&frame);
 }
 
-/*! \brief Reads a frame in from the audiohook structure
- * \param audiohook Audiohook structure
- * \param samples Number of samples wanted in requested output format
- * \param direction Direction the audio frame came from
- * \param format Format of frame remote side wants back
- * \return Returns frame on success, NULL on failure
- */
-struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, struct ast_format *format)
+static struct ast_frame *audiohook_read_frame_helper(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, struct ast_format *format, struct ast_frame **read_reference, struct ast_frame **write_reference)
 {
 	struct ast_frame *read_frame = NULL, *final_frame = NULL;
 	struct ast_format tmp_fmt;
@@ -352,10 +355,10 @@ struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size
 		samples_converted = samples * (ast_format_rate(format) / (float) audiohook->hook_internal_samp_rate);
 	}
 
-	if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ?
-		audiohook_read_frame_both(audiohook, samples_converted) :
-		audiohook_read_frame_single(audiohook, samples_converted, direction)))) {
-		return NULL;
+	if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ? 
+		audiohook_read_frame_both(audiohook, samples_converted, read_reference, write_reference) : 
+		audiohook_read_frame_single(audiohook, samples_converted, direction)))) { 
+		return NULL; 
 	}
 
 	/* If they don't want signed linear back out, we'll have to send it through the translation path */
@@ -383,6 +386,32 @@ struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size
 	return final_frame;
 }
 
+/*! \brief Reads a frame in from the audiohook structure
+ * \param audiohook Audiohook structure
+ * \param samples Number of samples wanted in requested output format
+ * \param direction Direction the audio frame came from
+ * \param format Format of frame remote side wants back
+ * \return Returns frame on success, NULL on failure
+ */
+struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, struct ast_format *format)
+{
+	return audiohook_read_frame_helper(audiohook, samples, direction, format, NULL, NULL);
+}
+
+/*! \brief Reads a frame in from the audiohook structure
+ * \param audiohook Audiohook structure
+ * \param samples Number of samples wanted
+ * \param direction Direction the audio frame came from
+ * \param format Format of frame remote side wants back
+ * \param read_frame frame pointer for copying read frame data
+ * \param write_frame frame pointer for copying write frame data
+ * \return Returns frame on success, NULL on failure
+ */
+struct ast_frame *ast_audiohook_read_frame_all(struct ast_audiohook *audiohook, size_t samples, struct ast_format *format, struct ast_frame **read_frame, struct ast_frame **write_frame)
+{
+	return audiohook_read_frame_helper(audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, format, read_frame, write_frame);
+}
+
 static void audiohook_list_set_samplerate_compatibility(struct ast_audiohook_list *audiohook_list)
 {
 	struct ast_audiohook *ah = NULL;
-- 
GitLab