<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/19811">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_broadcast: Add Broadcast application<br><br>Adds a new application, Broadcast, 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. New kinds<br>of audio injection impossible with ChanSpy are also made<br>possible.<br><br>ASTERISK-30180 #close<br><br>Change-Id: I7ba72f765dbab9b58deeae028baca3f4f8377726<br>---<br>A apps/app_broadcast.c<br>A doc/CHANGES-staging/app_broadcast.txt<br>2 files changed, 643 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/11/19811/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_broadcast.c b/apps/app_broadcast.c</span><br><span>new file mode 100644</span><br><span>index 0000000..de4b81d</span><br><span>--- /dev/null</span><br><span>+++ b/apps/app_broadcast.c</span><br><span>@@ -0,0 +1,619 @@</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 broadcasting</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="Broadcast" 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 broadcasting to target channels, also</span><br><span style="color: hsl(120, 100%, 40%);">+                                              broadcast 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="r"></span><br><span style="color: hsl(120, 100%, 40%);">+                                             <para>Feed frames to barge channels in "reverse" by injecting them into the primary channel's read queue instead.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                            <para>This option is required for barge to work in a n-party bridge (but not for 2-party bridges). Alternately, you</span><br><span style="color: hsl(120, 100%, 40%);">+                                             can add an intermediate channel by using a non-optimized Local channel, so that the target channel is bridged with</span><br><span style="color: hsl(120, 100%, 40%);">+                                            a single channel that is connected to the bridge, but it is recommended this option be used instead.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                             <para>Note that this option will always feed injected audio to the other party, regardless of whether the target</span><br><span style="color: hsl(120, 100%, 40%);">+                                                channel is bridged or not.</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 broadcast 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>Broadcast 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 broadcast 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>Broadcasting will continue until the broadcasting 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 broadcast 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 broadcasting 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 Broadcast 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="Broadcast received audio to three channels and their bridged peers"></span><br><span style="color: hsl(120, 100%, 40%);">+                  same => n,Broadcast(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="Broadcast received audio to three channels, only"></span><br><span style="color: hsl(120, 100%, 40%);">+                    same => n,Broadcast(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,Broadcast(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,Broadcast(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,Broadcast(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%);">+                 Broadcast application. The two-way audio is only between the broadcasting 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_broadcast[] = "Broadcast";</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%);">+    OPTION_REVERSE_FEED      = (1 << 5),</span><br><span style="color: hsl(120, 100%, 40%);">+    OPTION_ANSWER_WARN       = (1 << 6),      /* Internal flag, not set by user */</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('r', OPTION_REVERSE_FEED),</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 broadcasting 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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Note that ast_channel_bridge_peer only returns non-NULL for 2-party bridges, not n-party bridges (e.g. ConfBridge) */</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%);">+               ast_debug(9, "Channel %s is not yet bridged, unable to setup barge\n", ast_channel_name(spyee_chan));</span><br><span style="color: hsl(120, 100%, 40%);">+               /* If we're bridged, but it's not a 2-party bridge, then we probably should have used OPTION_REVERSE_FEED. */</span><br><span style="color: hsl(120, 100%, 40%);">+         if (ast_test_flag(flags, OPTION_ANSWER_WARN) && ast_channel_is_bridged(spyee_chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_clear_flag(flags, OPTION_ANSWER_WARN); /* Don't warn more than once. */</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_log(LOG_WARNING, "Barge failed: channel is bridged, but not to a 2-party bridge. Use the 'r' option.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               }</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, "Broadcast", 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_broadcast(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%);">+        ast_set_flag(flags, OPTION_ANSWER_WARN); /* Initialize answer warn to 1 */</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 broadcast 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%);">+                   ast_log(LOG_WARNING, "Multi autochan allocation failure\n");</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%);">+             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%);">+                     continue;</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%);">+                   mac->connected = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_audiohook_init(&mac->whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Broadcast", 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, "Broadcast", 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%);">+           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, "Broadcasting 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, "Broadcasting: (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 broadcast 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, -1) > 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 broadcasting 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 broadcasting 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 broadcast 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%);">+                                 * The exception to this is if we are emulating barge by doing it "directly",</span><br><span style="color: hsl(120, 100%, 40%);">+                                * that is injecting the frames onto this channel's read queue, rather than</span><br><span style="color: hsl(120, 100%, 40%);">+                                * its bridged peer's write queue, then skip this. We only do one or the other. */</span><br><span style="color: hsl(120, 100%, 40%);">+                                if (!ast_test_flag(flags, OPTION_REVERSE_FEED) && !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%);">+                          } else if (ast_test_flag(flags, OPTION_REVERSE_FEED)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                       /* So, this is really clever...</span><br><span style="color: hsl(120, 100%, 40%);">+                                        * If we're connected to an n-party bridge instead of a 2-party bridge,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    * attach_barge will ALWAYS fail because we're connected to a bridge, not</span><br><span style="color: hsl(120, 100%, 40%);">+                                  * a single peer channel.</span><br><span style="color: hsl(120, 100%, 40%);">+                                      * Recall that the objective is for injected audio to be audible to both</span><br><span style="color: hsl(120, 100%, 40%);">+                                       * sides of the channel. So really, the typical way of doing this by</span><br><span style="color: hsl(120, 100%, 40%);">+                                   * directly injecting frames separately onto both channels is kind of</span><br><span style="color: hsl(120, 100%, 40%);">+                                  * bizarre to begin with, when you think about it.</span><br><span style="color: hsl(120, 100%, 40%);">+                                     *</span><br><span style="color: hsl(120, 100%, 40%);">+                                     * In other words, this is how ChanSpy and this module by default work:</span><br><span style="color: hsl(120, 100%, 40%);">+                                        * We have audio F to inject onto channels A and B, which are <= bridged =>:</span><br><span style="color: hsl(120, 100%, 40%);">+                                     * READ <- A -> WRITE <==> READ <- B -> WRITE</span><br><span style="color: hsl(120, 100%, 40%);">+                                        *            F --^                  F --^</span><br><span style="color: hsl(120, 100%, 40%);">+                                     *</span><br><span style="color: hsl(120, 100%, 40%);">+                                     * So that makes the same audio audible to both channels A and B, but</span><br><span style="color: hsl(120, 100%, 40%);">+                                  * in kind of a roundabout way. What if the bridged peer changes at</span><br><span style="color: hsl(120, 100%, 40%);">+                                    * some point, for example?</span><br><span style="color: hsl(120, 100%, 40%);">+                                    *</span><br><span style="color: hsl(120, 100%, 40%);">+                                     * While that method works for 2-party bridges, it doesn't work at all</span><br><span style="color: hsl(120, 100%, 40%);">+                                     * for an n-party bridge, so we do the thing that seems obvious to begin with:</span><br><span style="color: hsl(120, 100%, 40%);">+                                         * dump the frames onto THIS channel's read queue, and the channels will</span><br><span style="color: hsl(120, 100%, 40%);">+                                   * make their way into the bridge like any other audio from this channel,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      * and everything just works perfectly, no matter what kind of bridging</span><br><span style="color: hsl(120, 100%, 40%);">+                                        * scenario is being used. At that point, we don't even care if we're</span><br><span style="color: hsl(120, 100%, 40%);">+                                  * bridged or not, and really, why should we?</span><br><span style="color: hsl(120, 100%, 40%);">+                                  *</span><br><span style="color: hsl(120, 100%, 40%);">+                                     * In other words, we do this:</span><br><span style="color: hsl(120, 100%, 40%);">+                                         * READ <- A -> WRITE <==> READ <- B -> WRITE</span><br><span style="color: hsl(120, 100%, 40%);">+                                        *                       F --^       F --^</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%);">+                                   fres |= ast_audiohook_write_frame(&mac->whisper_audiohook, AST_AUDIOHOOK_DIRECTION_READ, 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 (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 broadcast\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 broadcast_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%);">+       struct ast_format *write_format;</span><br><span style="color: hsl(120, 100%, 40%);">+      int res = -1;</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 = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_WARNING, "Broadcast requires at least one channel\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%);">+   parse = ast_strdupa(data);</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 broadcast\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%);">+   write_format = ao2_bump(ast_channel_writeformat(chan));</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%);">+                goto cleanup;</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_broadcast(chan, &flags, args.channels);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Restore previous write format */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_set_write_format(chan, write_format)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", ast_channel_name(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%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(write_format, -1);</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%);">+   return ast_unregister_application(app_broadcast);</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%);">+       return ast_register_application_xml(app_broadcast, broadcast_exec);</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 Broadcasting");</span><br><span>diff --git a/doc/CHANGES-staging/app_broadcast.txt b/doc/CHANGES-staging/app_broadcast.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..03e6848</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/app_broadcast.txt</span><br><span>@@ -0,0 +1,4 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: app_broadcast</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+A Broadcast 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/+/19811">change 19811</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/+/19811"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 20 </div>
<div style="display:none"> Gerrit-Change-Id: I7ba72f765dbab9b58deeae028baca3f4f8377726 </div>
<div style="display:none"> Gerrit-Change-Number: 19811 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: N A <asterisk@phreaknet.org> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>