diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c
index e8167d8ab5..bc799e64d9 100644
--- a/apps/app_chanspy.c
+++ b/apps/app_chanspy.c
@@ -245,6 +245,11 @@
 							</enum>
 						</enumlist>
 					</option>
+					<option name="D">
+						<para>Interleave the audio coming from the channel and the audio coming to the channel in
+						the output audio as a dual channel stream, rather than mix it. Does nothing if 'o'
+						is also set.</para>
+					</option>
 					<option name="e">
 						<argument name="ext" required="true" />
 						<para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
@@ -393,6 +398,7 @@ enum {
 	OPTION_EXITONHANGUP      = (1 << 18),   /* Hang up when the spied-on channel hangs up. */
 	OPTION_UNIQUEID          = (1 << 19),	/* The chanprefix is a channel uniqueid or fully specified channel name. */
 	OPTION_LONG_QUEUE        = (1 << 20),	/* Allow usage of a long queue to store audio frames. */
+	OPTION_INTERLEAVED       = (1 << 21),	/* Interleave the Read and Write frames in the output frame. */
 };
 
 enum {
@@ -411,6 +417,7 @@ AST_APP_OPTIONS(spy_opts, {
 	AST_APP_OPTION('B', OPTION_BARGE),
 	AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
 	AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
+	AST_APP_OPTION('D', OPTION_INTERLEAVED),
 	AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
 	AST_APP_OPTION('E', OPTION_EXITONHANGUP),
 	AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
@@ -471,6 +478,56 @@ static int spy_generate(struct ast_channel *chan, void *data, int len, int sampl
 	if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
 		/* Option 'o' was set, so don't mix channel audio */
 		f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin);
+	} else if (ast_test_flag(&csth->flags, OPTION_INTERLEAVED)) {
+		/* Option 'D' was set, so mix the spy frame as an interleaved dual channel frame. */
+		int i;
+		struct ast_frame *fr_read = NULL;
+		struct ast_frame *fr_write = NULL;
+		short read_buf[samples];
+		short write_buf[samples];
+		short stereo_buf[samples * 2];
+		struct ast_frame stereo_frame = {
+			.frametype = AST_FRAME_VOICE,
+			.datalen = sizeof(stereo_buf),
+			.samples = samples,
+		};
+
+		f = ast_audiohook_read_frame_all(&csth->spy_audiohook, samples, ast_format_slin, &fr_read, &fr_write);
+		if (f) {
+			ast_frame_free(f, 0);
+			f = NULL;
+		}
+
+		if (fr_read) {
+			memcpy(read_buf, fr_read->data.ptr, sizeof(read_buf));
+		} else {
+			/* silent out the output frame if we can't read the input */
+			memset(read_buf, 0, sizeof(read_buf));
+		}
+
+		if (fr_write) {
+			memcpy(write_buf, fr_write->data.ptr, sizeof(write_buf));
+		} else {
+			memset(write_buf, 0, sizeof(write_buf));
+		}
+
+		for (i = 0; i < samples; i++) {
+			stereo_buf[i*2] = read_buf[i];
+			stereo_buf[i*2+1] = write_buf[i];
+		}
+
+		stereo_frame.data.ptr = stereo_buf;
+		stereo_frame.subclass.format = ast_format_cache_get_slin_by_rate(samples);
+
+		f = ast_frdup(&stereo_frame);
+
+		if (fr_read) {
+			ast_frame_free(fr_read, 0);
+		}
+		if (fr_write) {
+			ast_frame_free(fr_write, 0);
+		}
+
 	} else {
 		f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
 	}