]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
app_chanspy: Add 'D' option for dual-channel audio
authorMike Bradeen <mbradeen@sangoma.com>
Wed, 31 Jan 2024 15:55:04 +0000 (08:55 -0700)
committerAsterisk Development Team <asteriskteam@digium.com>
Thu, 7 Mar 2024 14:18:40 +0000 (14:18 +0000)
Adds the 'D' option to app chanspy that causes the input and output
frames of the spied channel to be interleaved in the spy output frame.
This allows the input and output of the spied channel to be decoded
separately by the receiver.

If the 'o' option is also set, the 'D' option is ignored as the
audio being spied is inherently one direction.

Fixes: #569
UserNote: The ChanSpy application now accepts the 'D' option which
will interleave the spied audio within the outgoing frames. The
purpose of this is to allow the audio to be read as a Dual channel
stream with separate incoming and outgoing audio. Setting both the
'o' option and the 'D' option and results in the 'D' option being
ignored.

(cherry picked from commit 69fe81481369ba4db6919e77656f287d0011640c)

apps/app_chanspy.c

index e8167d8ab54e54520364aaf242d9702c5cefb320..bc799e64d97589e94443b2d4253085bb9689399e 100644 (file)
                                                        </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);
        }