<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/18975">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_multicast: Add Multicast application.<br><br>Adds a new application, Multicast, which can be used for<br>one-to-many transmission and many-to-one reception of<br>channel audio in Asterisk. This is similar to ChanSpy,<br>except it is designed for multiple channel targets instead<br>of a single one. This can make certain kinds of audio<br>manipulation more efficient and streamlined.<br><br>ASTERISK-30180 #close<br><br>Change-Id: I7ba72f765dbab9b58deeae028baca3f4f8377726<br>---<br>A apps/app_multicast.c<br>A doc/CHANGES-staging/app_multicast.txt<br>2 files changed, 558 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/75/18975/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_multicast.c b/apps/app_multicast.c</span><br><span>new file mode 100644</span><br><span>index 0000000..9eb0111</span><br><span>--- /dev/null</span><br><span>+++ b/apps/app_multicast.c</span><br><span>@@ -0,0 +1,554 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2022, Naveen Albert</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Channel audio multicasting</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \ingroup applications</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>extended</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <ctype.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/channel.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/audiohook.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/utils.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/pbx.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/lock.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/options.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/autochan.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/format_cache.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/cli.h" /* use ESS macro */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** DOCUMENTATION</span><br><span style="color: hsl(120, 100%, 40%);">+ <application name="Multicast" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Transmit or receive audio to or from multiple channels simultaneously</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="options"></span><br><span style="color: hsl(120, 100%, 40%);">+ <optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="b"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>In addition to multicasting to target channels, also</span><br><span style="color: hsl(120, 100%, 40%);">+ multicast to any channels to which target channels are bridged.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="l"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Allow usage of a long queue to store audio frames.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <note><para>This may introduce some delay in the received audio feed, but will improve the audio quality.</para></note></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="o"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Do not mix streams when combining audio from target channels (only applies with s option).</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="s"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Rather than multicast audio to a bunch of channels, receive the combined audio from the target channels.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="w"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Multicast audio received on this channel to other channels.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ </optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="channels" required="true" argsep=","></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>List of channels for multicast targets.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Channel names must be the full channel names, not merely device names.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Multicasting will continue until the multicasting channel hangs up or all target channels have hung up.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This application can be used to multicast audio to multiple channels at once.</span><br><span style="color: hsl(120, 100%, 40%);">+ Any audio received on this channel will be transmitted to all of the specified channels and, optionally, their bridged peers.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>It can also be used to aggregate audio from multiple channels at once.</span><br><span style="color: hsl(120, 100%, 40%);">+ Any audio on any of the specified channels, and optionally their bridged peers, will be transmitted to this channel.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Execution of the application continues until either the multicasting channel hangs up</span><br><span style="color: hsl(120, 100%, 40%);">+ or all specified channels have hung up.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This application is used for one-to-many and many-to-one audio applications where</span><br><span style="color: hsl(120, 100%, 40%);">+ bridge mixing cannot be done synchronously on all the involved channels.</span><br><span style="color: hsl(120, 100%, 40%);">+ This is primarily useful for injecting the same audio stream into multiple channels at once,</span><br><span style="color: hsl(120, 100%, 40%);">+ or doing the reverse, combining the audio from multiple channels into a single stream.</span><br><span style="color: hsl(120, 100%, 40%);">+ This contrasts with using a separate injection channel for each target channel and/or</span><br><span style="color: hsl(120, 100%, 40%);">+ using a conference bridge.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The channel running the Multicast application must do so synchronously. The specified channels,</span><br><span style="color: hsl(120, 100%, 40%);">+ however, may be doing other things.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <example title="Multicast received audio to three channels and their bridged peers"></span><br><span style="color: hsl(120, 100%, 40%);">+ same => n,Multicast(wb,DAHDI/1,DAHDI/3,PJSIP/doorphone)</span><br><span style="color: hsl(120, 100%, 40%);">+ </example></span><br><span style="color: hsl(120, 100%, 40%);">+ <example title="Multicast received audio to three channels, only"></span><br><span style="color: hsl(120, 100%, 40%);">+ same => n,Multicast(w,DAHDI/1,DAHDI/3,PJSIP/doorphone)</span><br><span style="color: hsl(120, 100%, 40%);">+ </example></span><br><span style="color: hsl(120, 100%, 40%);">+ <example title="Combine audio from three channels and their bridged peers to us"></span><br><span style="color: hsl(120, 100%, 40%);">+ same => n,Multicast(s,DAHDI/1,DAHDI/3,PJSIP/doorphone)</span><br><span style="color: hsl(120, 100%, 40%);">+ </example></span><br><span style="color: hsl(120, 100%, 40%);">+ <example title="Combine audio from three channels to us"></span><br><span style="color: hsl(120, 100%, 40%);">+ same => n,Multicast(so,DAHDI/1,DAHDI/3,PJSIP/doorphone)</span><br><span style="color: hsl(120, 100%, 40%);">+ </example></span><br><span style="color: hsl(120, 100%, 40%);">+ <example title="Two-way audio with a bunch of channels"></span><br><span style="color: hsl(120, 100%, 40%);">+ same => n,Multicast(wbso,DAHDI/1,DAHDI/3,PJSIP/doorphone)</span><br><span style="color: hsl(120, 100%, 40%);">+ </example></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Note that in the last example above, this is NOT the same as a conference bridge.</span><br><span style="color: hsl(120, 100%, 40%);">+ The specified channels are not audible to each other, only to the channel running the</span><br><span style="color: hsl(120, 100%, 40%);">+ Multicast application. The two-way audio is only between the multicasting channel and</span><br><span style="color: hsl(120, 100%, 40%);">+ each of the specified channels, individually.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">ChanSpy</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ </see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ </application></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const char app_multicast[] = "Multicast";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+ OPTION_READONLY = (1 << 0), /* Don't mix the two channels */</span><br><span style="color: hsl(120, 100%, 40%);">+ OPTION_BARGE = (1 << 1), /* Barge mode (whisper to both channels) */</span><br><span style="color: hsl(120, 100%, 40%);">+ OPTION_LONG_QUEUE = (1 << 2), /* Allow usage of a long queue to store audio frames. */</span><br><span style="color: hsl(120, 100%, 40%);">+ OPTION_WHISPER = (1 << 3),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPTION_SPY = (1 << 4),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_APP_OPTIONS(spy_opts, {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('b', OPTION_BARGE),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('l', OPTION_LONG_QUEUE),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('o', OPTION_READONLY),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('s', OPTION_SPY),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('w', OPTION_WHISPER),</span><br><span style="color: hsl(120, 100%, 40%);">+});</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct multi_autochan {</span><br><span style="color: hsl(120, 100%, 40%);">+ char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_autochan *autochan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_autochan *bridge_autochan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_audiohook whisper_audiohook;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_audiohook bridge_whisper_audiohook;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_audiohook spy_audiohook;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int connected:1;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int bridge_connected:1;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int spying:1;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_ENTRY(multi_autochan) entry; /*!< Next record */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_RWLIST_HEAD(multi_autochan_list, multi_autochan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct multi_spy {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct multi_autochan_list *chanlist;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int readonly:1;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void *spy_alloc(struct ast_channel *chan, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return data; /* just store the data pointer in the channel structure */</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void spy_release(struct ast_channel *chan, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return; /* nothing to do */</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct multi_spy *multispy = data;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct multi_autochan_list *chanlist = multispy->chanlist;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct multi_autochan *mac;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame *f;</span><br><span style="color: hsl(120, 100%, 40%);">+ short *data1, *data2;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res, i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* All the frames we get are slin, so they will all have the same number of samples. */</span><br><span style="color: hsl(120, 100%, 40%);">+ static const int num_samples = 160;</span><br><span style="color: hsl(120, 100%, 40%);">+ short combine_buf[num_samples];</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame wf = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .frametype = AST_FRAME_VOICE,</span><br><span style="color: hsl(120, 100%, 40%);">+ .offset = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ .subclass.format = ast_format_slin,</span><br><span style="color: hsl(120, 100%, 40%);">+ .datalen = num_samples * 2,</span><br><span style="color: hsl(120, 100%, 40%);">+ .samples = num_samples,</span><br><span style="color: hsl(120, 100%, 40%);">+ .src = __FUNCTION__,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ memset(&combine_buf, 0, sizeof(combine_buf));</span><br><span style="color: hsl(120, 100%, 40%);">+ wf.data.ptr = combine_buf;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(chanlist);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(chanlist, mac, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_lock(&mac->spy_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_unlock(&mac->spy_audiohook); /* Channel is already gone more than likely, the multicasting channel will clean this up. */</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (multispy->readonly) { /* Option 'o' was set, so don't mix channel audio */</span><br><span style="color: hsl(120, 100%, 40%);">+ f = ast_audiohook_read_frame(&mac->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ f = ast_audiohook_read_frame(&mac->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_unlock(&mac->spy_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!f) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue; /* No frame? No problem. */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Mix the samples. */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0, data1 = combine_buf, data2 = f->data.ptr; i < num_samples; i++, data1++, data2++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_slinear_saturated_add(data1, data2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_frfree(f);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(chanlist);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_write(chan, &wf);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_frfree(&wf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_generator spygen = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .alloc = spy_alloc,</span><br><span style="color: hsl(120, 100%, 40%);">+ .release = spy_release,</span><br><span style="color: hsl(120, 100%, 40%);">+ .generate = spy_generate,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook, struct ast_flags *flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autochan_channel_lock(autochan);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Attaching spy channel %s to %s\n", spychan_name, ast_channel_name(autochan->chan));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(flags, OPTION_READONLY)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_set_flag(audiohook, AST_AUDIOHOOK_MUTE_WRITE);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(flags, OPTION_LONG_QUEUE)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(2, "Using a long queue to store audio frames in spy audiohook\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_set_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_audiohook_attach(autochan->chan, audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autochan_channel_unlock(autochan);</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int attach_barge(struct ast_autochan *spyee_autochan, struct ast_autochan **spyee_bridge_autochan,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_audiohook *bridge_whisper_audiohook, const char *spyer_name, const char *name, struct ast_flags *flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int retval = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_autochan *internal_bridge_autochan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *spyee_chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_channel *, bridged, NULL, ast_channel_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autochan_channel_lock(spyee_autochan);</span><br><span style="color: hsl(120, 100%, 40%);">+ spyee_chan = ast_channel_ref(spyee_autochan->chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autochan_channel_unlock(spyee_autochan);</span><br><span style="color: hsl(120, 100%, 40%);">+ bridged = ast_channel_bridge_peer(spyee_chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unref(spyee_chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!bridged) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_init(bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Multicast", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ internal_bridge_autochan = ast_autochan_setup(bridged);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!internal_bridge_autochan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (start_spying(internal_bridge_autochan, spyer_name, bridge_whisper_audiohook, flags)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee '%s'. Barge mode disabled.\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+ retval = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ *spyee_bridge_autochan = internal_bridge_autochan;</span><br><span style="color: hsl(120, 100%, 40%);">+ return retval;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void multi_autochan_free(struct multi_autochan *mac)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->connected) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(2, "Whisper audiohook no longer running\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_lock(&mac->whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_detach(&mac->whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_unlock(&mac->whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_destroy(&mac->whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->bridge_connected) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->bridge_whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(2, "Whisper (bridged) audiohook no longer running\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_lock(&mac->bridge_whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_detach(&mac->bridge_whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_unlock(&mac->bridge_whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_destroy(&mac->bridge_whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->spying) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(2, "Spy audiohook no longer running\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_lock(&mac->spy_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_detach(&mac->spy_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_unlock(&mac->spy_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_destroy(&mac->spy_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->name) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int total = mac->connected + mac->bridge_connected + mac->spying;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Removing channel %s from target list (%d hook%s)\n", mac->name, total, ESS(total));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(mac->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->autochan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autochan_destroy(mac->autochan);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->bridge_autochan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autochan_destroy(mac->bridge_autochan);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(mac);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int do_multicast(struct ast_channel *chan, struct ast_flags *flags, const char *channels)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame *f;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_silence_generator *silgen = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct multi_spy multispy;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct multi_autochan_list chanlist;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct multi_autochan *mac;</span><br><span style="color: hsl(120, 100%, 40%);">+ int numchans = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int readonly = ast_test_flag(flags, OPTION_READONLY) ? 1 : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *next, *chansdup = ast_strdupa(channels);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_HEAD_INIT(&chanlist);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_set_flag(chan, AST_FLAG_SPYING);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Hey, look ma, no list lock needed! Sometimes, it's nice to not have to share... */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Build a list of targets */</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((next = strsep(&chansdup, ","))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *ochan;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(next)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcmp(next, ast_channel_name(chan))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Refusing to multicast to ourself: %s\n", next);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ochan = ast_channel_get_by_name(next);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ochan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "No such channel: %s\n", next);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Append to end of list. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((mac = ast_calloc(1, sizeof(*mac)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ mac->name = ast_strdup(next);</span><br><span style="color: hsl(120, 100%, 40%);">+ mac->autochan = ast_autochan_setup(ochan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!mac->name || !mac->autochan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ multi_autochan_free(mac);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(flags, OPTION_WHISPER)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ mac->connected = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_init(&mac->whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Multicast", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Inject audio from our channel to this target. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (start_spying(mac->autochan, next, &mac->whisper_audiohook, flags)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to attach whisper audiohook to %s\n", next);</span><br><span style="color: hsl(120, 100%, 40%);">+ multi_autochan_free(mac);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(flags, OPTION_SPY)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ mac->spying = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_init(&mac->spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (start_spying(mac->autochan, next, &mac->spy_audiohook, flags)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to attach spy audiohook to %s\n", next);</span><br><span style="color: hsl(120, 100%, 40%);">+ multi_autochan_free(mac);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_INSERT_TAIL(&chanlist, mac, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ numchans++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Multi autochan allocation failure\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ochan = ast_channel_unref(ochan);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(4, "Multicasting to %d channel%s on %s\n", numchans, ESS(numchans), ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Multicasting: (TX->1) whisper=%d, (TX->2) barge=%d, (RX<-%d) spy=%d (%s)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_flag(flags, OPTION_WHISPER) ? 1 : 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_flag(flags, OPTION_BARGE) ? 1 : 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ readonly ? 1 : 2,</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_flag(flags, OPTION_SPY) ? 1 : 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ readonly ? "single" : "both");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(flags, OPTION_SPY)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ multispy.chanlist = &chanlist;</span><br><span style="color: hsl(120, 100%, 40%);">+ multispy.readonly = readonly;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_activate_generator(chan, &spygen, &multispy);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* We're not expecting to read any audio, just multicast audio to a bunch of other channels. */</span><br><span style="color: hsl(120, 100%, 40%);">+ silgen = ast_channel_start_silence_generator(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while (numchans && ast_waitfor(chan, 1000) > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int fres = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ f = ast_read(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!f) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Channel %s must have hung up\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (f->frametype != AST_FRAME_VOICE) { /* Ignore any non-voice frames */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_frfree(f);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Write the frame to all our targets. */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(&chanlist);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chanlist, mac, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Note that if no media is received, execution is suspended, but assuming continuous or</span><br><span style="color: hsl(120, 100%, 40%);">+ * or frequent audio on the multicasting channel, we'll quickly enough detect hung up targets.</span><br><span style="color: hsl(120, 100%, 40%);">+ * This isn't really an issue, just something that might be confusing at first, but this is</span><br><span style="color: hsl(120, 100%, 40%);">+ * due to the limitation with audiohooks of using the channel for timing. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((ast_test_flag(flags, OPTION_WHISPER) && mac->whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)</span><br><span style="color: hsl(120, 100%, 40%);">+ || (ast_test_flag(flags, OPTION_SPY) && mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)</span><br><span style="color: hsl(120, 100%, 40%);">+ || (mac->bridge_connected && ast_test_flag(flags, OPTION_BARGE) && mac->bridge_whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Even if we're spying only and not actually multicasting audio, we need to detect channel hangup. */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_REMOVE_CURRENT(entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(2, "Looks like %s has hung up\n", mac->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ multi_autochan_free(mac);</span><br><span style="color: hsl(120, 100%, 40%);">+ numchans--;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(2, "%d channel%s remaining in multicast on %s\n", numchans, ESS(numchans), ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(flags, OPTION_WHISPER)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_lock(&mac->whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ fres |= ast_audiohook_write_frame(&mac->whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_unlock(&mac->whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(flags, OPTION_BARGE)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* This hook lets us inject audio into the channel that the spyee is currently</span><br><span style="color: hsl(120, 100%, 40%);">+ * bridged with. If the spyee isn't bridged with anything yet, nothing will</span><br><span style="color: hsl(120, 100%, 40%);">+ * be attached and we'll need to continue attempting to attach the barge</span><br><span style="color: hsl(120, 100%, 40%);">+ * audio hook. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!mac->bridge_connected && !attach_barge(mac->autochan, &mac->bridge_autochan,</span><br><span style="color: hsl(120, 100%, 40%);">+ &mac->bridge_whisper_audiohook, ast_channel_name(chan), mac->name, flags)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(2, "Attached barge channel for %s\n", mac->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ mac->bridge_connected = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mac->bridge_connected) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_lock(&mac->bridge_whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ fres |= ast_audiohook_write_frame(&mac->bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_unlock(&mac->bridge_whisper_audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (fres) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to write to audiohook for %s\n", mac->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ fres = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&chanlist);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_frfree(f);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!numchans) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Exiting due to all target channels having left the multicast\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(flags, OPTION_SPY)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_deactivate_generator(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_stop_silence_generator(chan, silgen);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Cleanup any remaining targets */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chanlist, mac, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_REMOVE_CURRENT(entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ multi_autochan_free(mac);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_clear_flag(chan, AST_FLAG_SPYING);</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int multicast_exec(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags flags;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(options);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(channels); /* Channel list last, so we can have multiple */</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+ char *parse = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, parse);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.channels)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Must specify at least one channel for multicast\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (args.options) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_parse_options(spy_opts, &flags, NULL, args.options);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_clear_flag(&flags, AST_FLAGS_ALL);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_test_flag(&flags, OPTION_BARGE) && !ast_test_flag(&flags, OPTION_SPY) && !ast_test_flag(&flags, OPTION_WHISPER)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "At least one of the b, s, or w option must be specified (provided options have no effect)\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_write_format(chan, ast_format_slin) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to set write format to slin.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res = do_multicast(chan, &flags, args.channels);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_unregister_application(app_multicast);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_register_application_xml(app_multicast, multicast_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Audio Multicasting");</span><br><span>diff --git a/doc/CHANGES-staging/app_multicast.txt b/doc/CHANGES-staging/app_multicast.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..b0e68a3</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/app_multicast.txt</span><br><span>@@ -0,0 +1,4 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: app_multicast</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+A Multicast application is now available which allows</span><br><span style="color: hsl(120, 100%, 40%);">+for asynchronous one-to-many and many-to-one channel audio.</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/18975">change 18975</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/18975"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I7ba72f765dbab9b58deeae028baca3f4f8377726 </div>
<div style="display:none"> Gerrit-Change-Number: 18975 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>