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); }