Asterisk - The Open Source Telephony Project  21.4.1
app_broadcast.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2022, Naveen Albert
5  *
6  * See http://www.asterisk.org for more information about
7  * the Asterisk project. Please do not directly contact
8  * any of the maintainers of this project for assistance;
9  * the project provides a web site, mailing lists and IRC
10  * channels for your use.
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License Version 2. See the LICENSE file
14  * at the top of the source tree.
15  */
16 
17 /*! \file
18  *
19  * \brief Channel audio broadcasting
20  *
21  * \author Naveen Albert <asterisk@phreaknet.org>
22  *
23  * \ingroup applications
24  */
25 
26 /*** MODULEINFO
27  <support_level>extended</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include <ctype.h>
33 #include <errno.h>
34 
35 #include "asterisk/channel.h"
36 #include "asterisk/audiohook.h"
37 #include "asterisk/app.h"
38 #include "asterisk/utils.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/module.h"
41 #include "asterisk/lock.h"
42 #include "asterisk/options.h"
43 #include "asterisk/autochan.h"
44 #include "asterisk/format_cache.h"
45 #include "asterisk/cli.h" /* use ESS macro */
46 
47 /*** DOCUMENTATION
48  <application name="Broadcast" language="en_US">
49  <synopsis>
50  Transmit or receive audio to or from multiple channels simultaneously
51  </synopsis>
52  <syntax>
53  <parameter name="options">
54  <optionlist>
55  <option name="b">
56  <para>In addition to broadcasting to target channels, also
57  broadcast to any channels to which target channels are bridged.</para>
58  </option>
59  <option name="l">
60  <para>Allow usage of a long queue to store audio frames.</para>
61  <note><para>This may introduce some delay in the received audio feed, but will improve the audio quality.</para></note>
62  </option>
63  <option name="o">
64  <para>Do not mix streams when combining audio from target channels (only applies with s option).</para>
65  </option>
66  <option name="r">
67  <para>Feed frames to barge channels in "reverse" by injecting them into the primary channel's read queue instead.</para>
68  <para>This option is required for barge to work in a n-party bridge (but not for 2-party bridges). Alternately, you
69  can add an intermediate channel by using a non-optimized Local channel, so that the target channel is bridged with
70  a single channel that is connected to the bridge, but it is recommended this option be used instead.</para>
71  <para>Note that this option will always feed injected audio to the other party, regardless of whether the target
72  channel is bridged or not.</para>
73  </option>
74  <option name="s">
75  <para>Rather than broadcast audio to a bunch of channels, receive the combined audio from the target channels.</para>
76  </option>
77  <option name="w">
78  <para>Broadcast audio received on this channel to other channels.</para>
79  </option>
80  </optionlist>
81  </parameter>
82  <parameter name="channels" required="true" argsep=",">
83  <para>List of channels for broadcast targets.</para>
84  <para>Channel names must be the full channel names, not merely device names.</para>
85  <para>Broadcasting will continue until the broadcasting channel hangs up or all target channels have hung up.</para>
86  </parameter>
87  </syntax>
88  <description>
89  <para>This application can be used to broadcast audio to multiple channels at once.
90  Any audio received on this channel will be transmitted to all of the specified channels and, optionally, their bridged peers.</para>
91  <para>It can also be used to aggregate audio from multiple channels at once.
92  Any audio on any of the specified channels, and optionally their bridged peers, will be transmitted to this channel.</para>
93  <para>Execution of the application continues until either the broadcasting channel hangs up
94  or all specified channels have hung up.</para>
95  <para>This application is used for one-to-many and many-to-one audio applications where
96  bridge mixing cannot be done synchronously on all the involved channels.
97  This is primarily useful for injecting the same audio stream into multiple channels at once,
98  or doing the reverse, combining the audio from multiple channels into a single stream.
99  This contrasts with using a separate injection channel for each target channel and/or
100  using a conference bridge.</para>
101  <para>The channel running the Broadcast application must do so synchronously. The specified channels,
102  however, may be doing other things.</para>
103  <example title="Broadcast received audio to three channels and their bridged peers">
104  same => n,Broadcast(wb,DAHDI/1,DAHDI/3,PJSIP/doorphone)
105  </example>
106  <example title="Broadcast received audio to three channels, only">
107  same => n,Broadcast(w,DAHDI/1,DAHDI/3,PJSIP/doorphone)
108  </example>
109  <example title="Combine audio from three channels and their bridged peers to us">
110  same => n,Broadcast(s,DAHDI/1,DAHDI/3,PJSIP/doorphone)
111  </example>
112  <example title="Combine audio from three channels to us">
113  same => n,Broadcast(so,DAHDI/1,DAHDI/3,PJSIP/doorphone)
114  </example>
115  <example title="Two-way audio with a bunch of channels">
116  same => n,Broadcast(wbso,DAHDI/1,DAHDI/3,PJSIP/doorphone)
117  </example>
118  <para>Note that in the last example above, this is NOT the same as a conference bridge.
119  The specified channels are not audible to each other, only to the channel running the
120  Broadcast application. The two-way audio is only between the broadcasting channel and
121  each of the specified channels, individually.</para>
122  </description>
123  <see-also>
124  <ref type="application">ChanSpy</ref>
125  </see-also>
126  </application>
127  ***/
128 
129 static const char app_broadcast[] = "Broadcast";
130 
131 enum {
132  OPTION_READONLY = (1 << 0), /* Don't mix the two channels */
133  OPTION_BARGE = (1 << 1), /* Barge mode (whisper to both channels) */
134  OPTION_LONG_QUEUE = (1 << 2), /* Allow usage of a long queue to store audio frames. */
135  OPTION_WHISPER = (1 << 3),
136  OPTION_SPY = (1 << 4),
137  OPTION_REVERSE_FEED = (1 << 5),
138  OPTION_ANSWER_WARN = (1 << 6), /* Internal flag, not set by user */
139 };
140 
141 AST_APP_OPTIONS(spy_opts, {
142  AST_APP_OPTION('b', OPTION_BARGE),
143  AST_APP_OPTION('l', OPTION_LONG_QUEUE),
144  AST_APP_OPTION('o', OPTION_READONLY),
145  AST_APP_OPTION('r', OPTION_REVERSE_FEED),
146  AST_APP_OPTION('s', OPTION_SPY),
147  AST_APP_OPTION('w', OPTION_WHISPER),
148 });
149 
151  char *name;
152  struct ast_autochan *autochan;
153  struct ast_autochan *bridge_autochan;
154  struct ast_audiohook whisper_audiohook;
155  struct ast_audiohook bridge_whisper_audiohook;
156  struct ast_audiohook spy_audiohook;
157  unsigned int connected:1;
158  unsigned int bridge_connected:1;
159  unsigned int spying:1;
160  AST_LIST_ENTRY(multi_autochan) entry; /*!< Next record */
161 };
162 
164 
165 struct multi_spy {
167  unsigned int readonly:1;
168 };
169 
170 static void *spy_alloc(struct ast_channel *chan, void *data)
171 {
172  return data; /* just store the data pointer in the channel structure */
173 }
174 
175 static void spy_release(struct ast_channel *chan, void *data)
176 {
177  return; /* nothing to do */
178 }
179 
180 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
181 {
182  struct multi_spy *multispy = data;
183  struct multi_autochan_list *chanlist = multispy->chanlist;
184  struct multi_autochan *mac;
185  struct ast_frame *f;
186  short *data1, *data2;
187  int res, i;
188 
189  /* All the frames we get are slin, so they will all have the same number of samples. */
190  static const int num_samples = 160;
191  short combine_buf[num_samples];
192  struct ast_frame wf = {
194  .offset = 0,
195  .subclass.format = ast_format_slin,
196  .datalen = num_samples * 2,
197  .samples = num_samples,
198  .src = __FUNCTION__,
199  };
200 
201  memset(&combine_buf, 0, sizeof(combine_buf));
202  wf.data.ptr = combine_buf;
203 
204  AST_RWLIST_WRLOCK(chanlist);
205  AST_RWLIST_TRAVERSE_SAFE_BEGIN(chanlist, mac, entry) {
206  ast_audiohook_lock(&mac->spy_audiohook);
207  if (mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
208  ast_audiohook_unlock(&mac->spy_audiohook); /* Channel is already gone more than likely, the broadcasting channel will clean this up. */
209  continue;
210  }
211 
212  if (multispy->readonly) { /* Option 'o' was set, so don't mix channel audio */
213  f = ast_audiohook_read_frame(&mac->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin);
214  } else {
215  f = ast_audiohook_read_frame(&mac->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
216  }
217  ast_audiohook_unlock(&mac->spy_audiohook);
218 
219  if (!f) {
220  continue; /* No frame? No problem. */
221  }
222 
223  /* Mix the samples. */
224  for (i = 0, data1 = combine_buf, data2 = f->data.ptr; i < num_samples; i++, data1++, data2++) {
225  ast_slinear_saturated_add(data1, data2);
226  }
227  ast_frfree(f);
228  }
229  AST_RWLIST_TRAVERSE_SAFE_END;
230  AST_RWLIST_UNLOCK(chanlist);
231 
232  res = ast_write(chan, &wf);
233  ast_frfree(&wf);
234 
235  return res;
236 }
237 
238 static struct ast_generator spygen = {
239  .alloc = spy_alloc,
240  .release = spy_release,
241  .generate = spy_generate,
242 };
243 
244 static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook, struct ast_flags *flags)
245 {
246  int res;
247 
248  ast_autochan_channel_lock(autochan);
249  ast_debug(1, "Attaching spy channel %s to %s\n", spychan_name, ast_channel_name(autochan->chan));
250 
251  if (ast_test_flag(flags, OPTION_READONLY)) {
252  ast_set_flag(audiohook, AST_AUDIOHOOK_MUTE_WRITE);
253  } else {
254  ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
255  }
256  if (ast_test_flag(flags, OPTION_LONG_QUEUE)) {
257  ast_debug(2, "Using a long queue to store audio frames in spy audiohook\n");
258  } else {
259  ast_set_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE);
260  }
261  res = ast_audiohook_attach(autochan->chan, audiohook);
262  ast_autochan_channel_unlock(autochan);
263  return res;
264 }
265 
266 static int attach_barge(struct ast_autochan *spyee_autochan, struct ast_autochan **spyee_bridge_autochan,
267  struct ast_audiohook *bridge_whisper_audiohook, const char *spyer_name, const char *name, struct ast_flags *flags)
268 {
269  int retval = 0;
270  struct ast_autochan *internal_bridge_autochan;
271  struct ast_channel *spyee_chan;
272  RAII_VAR(struct ast_channel *, bridged, NULL, ast_channel_cleanup);
273 
274  ast_autochan_channel_lock(spyee_autochan);
275  spyee_chan = ast_channel_ref(spyee_autochan->chan);
276  ast_autochan_channel_unlock(spyee_autochan);
277 
278  /* Note that ast_channel_bridge_peer only returns non-NULL for 2-party bridges, not n-party bridges (e.g. ConfBridge) */
279  bridged = ast_channel_bridge_peer(spyee_chan);
280  ast_channel_unref(spyee_chan);
281  if (!bridged) {
282  ast_debug(9, "Channel %s is not yet bridged, unable to setup barge\n", ast_channel_name(spyee_chan));
283  /* If we're bridged, but it's not a 2-party bridge, then we probably should have used OPTION_REVERSE_FEED. */
284  if (ast_test_flag(flags, OPTION_ANSWER_WARN) && ast_channel_is_bridged(spyee_chan)) {
285  ast_clear_flag(flags, OPTION_ANSWER_WARN); /* Don't warn more than once. */
286  ast_log(LOG_WARNING, "Barge failed: channel is bridged, but not to a 2-party bridge. Use the 'r' option.\n");
287  }
288  return -1;
289  }
290 
291  ast_audiohook_init(bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Broadcast", 0);
292  internal_bridge_autochan = ast_autochan_setup(bridged);
293  if (!internal_bridge_autochan) {
294  return -1;
295  }
296 
297  if (start_spying(internal_bridge_autochan, spyer_name, bridge_whisper_audiohook, flags)) {
298  ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee '%s'. Barge mode disabled.\n", name);
299  retval = -1;
300  }
301 
302  *spyee_bridge_autochan = internal_bridge_autochan;
303  return retval;
304 }
305 
306 static void multi_autochan_free(struct multi_autochan *mac)
307 {
308  if (mac->connected) {
309  if (mac->whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
310  ast_debug(2, "Whisper audiohook no longer running\n");
311  }
312  ast_audiohook_lock(&mac->whisper_audiohook);
313  ast_audiohook_detach(&mac->whisper_audiohook);
314  ast_audiohook_unlock(&mac->whisper_audiohook);
315  ast_audiohook_destroy(&mac->whisper_audiohook);
316  }
317  if (mac->bridge_connected) {
318  if (mac->bridge_whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
319  ast_debug(2, "Whisper (bridged) audiohook no longer running\n");
320  }
321  ast_audiohook_lock(&mac->bridge_whisper_audiohook);
322  ast_audiohook_detach(&mac->bridge_whisper_audiohook);
323  ast_audiohook_unlock(&mac->bridge_whisper_audiohook);
324  ast_audiohook_destroy(&mac->bridge_whisper_audiohook);
325  }
326  if (mac->spying) {
327  if (mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
328  ast_debug(2, "Spy audiohook no longer running\n");
329  }
330  ast_audiohook_lock(&mac->spy_audiohook);
331  ast_audiohook_detach(&mac->spy_audiohook);
332  ast_audiohook_unlock(&mac->spy_audiohook);
333  ast_audiohook_destroy(&mac->spy_audiohook);
334  }
335  if (mac->name) {
336  int total = mac->connected + mac->bridge_connected + mac->spying;
337  ast_debug(1, "Removing channel %s from target list (%d hook%s)\n", mac->name, total, ESS(total));
338  ast_free(mac->name);
339  }
340  if (mac->autochan) {
341  ast_autochan_destroy(mac->autochan);
342  }
343  if (mac->bridge_autochan) {
344  ast_autochan_destroy(mac->bridge_autochan);
345  }
346  ast_free(mac);
347 }
348 
349 static int do_broadcast(struct ast_channel *chan, struct ast_flags *flags, const char *channels)
350 {
351  int res = 0;
352  struct ast_frame *f;
353  struct ast_silence_generator *silgen = NULL;
354  struct multi_spy multispy;
355  struct multi_autochan_list chanlist;
356  struct multi_autochan *mac;
357  int numchans = 0;
358  int readonly = ast_test_flag(flags, OPTION_READONLY) ? 1 : 0;
359  char *next, *chansdup = ast_strdupa(channels);
360 
361  AST_RWLIST_HEAD_INIT(&chanlist);
363 
364  ast_set_flag(flags, OPTION_ANSWER_WARN); /* Initialize answer warn to 1 */
365 
366  /* Hey, look ma, no list lock needed! Sometimes, it's nice to not have to share... */
367 
368  /* Build a list of targets */
369  while ((next = strsep(&chansdup, ","))) {
370  struct ast_channel *ochan;
371  if (ast_strlen_zero(next)) {
372  continue;
373  }
374  if (!strcmp(next, ast_channel_name(chan))) {
375  ast_log(LOG_WARNING, "Refusing to broadcast to ourself: %s\n", next);
376  continue;
377  }
378  ochan = ast_channel_get_by_name(next);
379  if (!ochan) {
380  ast_log(LOG_WARNING, "No such channel: %s\n", next);
381  continue;
382  }
383  /* Append to end of list. */
384  if (!(mac = ast_calloc(1, sizeof(*mac)))) {
385  ast_log(LOG_WARNING, "Multi autochan allocation failure\n");
386  continue;
387  }
388  mac->name = ast_strdup(next);
389  mac->autochan = ast_autochan_setup(ochan);
390  if (!mac->name || !mac->autochan) {
391  multi_autochan_free(mac);
392  continue;
393  }
394  if (ast_test_flag(flags, OPTION_WHISPER)) {
395  mac->connected = 1;
396  ast_audiohook_init(&mac->whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Broadcast", 0);
397  /* Inject audio from our channel to this target. */
398  if (start_spying(mac->autochan, next, &mac->whisper_audiohook, flags)) {
399  ast_log(LOG_WARNING, "Unable to attach whisper audiohook to %s\n", next);
400  multi_autochan_free(mac);
401  continue;
402  }
403  }
404  if (ast_test_flag(flags, OPTION_SPY)) {
405  mac->spying = 1;
406  ast_audiohook_init(&mac->spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "Broadcast", 0);
407  if (start_spying(mac->autochan, next, &mac->spy_audiohook, flags)) {
408  ast_log(LOG_WARNING, "Unable to attach spy audiohook to %s\n", next);
409  multi_autochan_free(mac);
410  continue;
411  }
412  }
413  AST_RWLIST_INSERT_TAIL(&chanlist, mac, entry);
414  numchans++;
415  ochan = ast_channel_unref(ochan);
416  }
417 
418  ast_verb(4, "Broadcasting to %d channel%s on %s\n", numchans, ESS(numchans), ast_channel_name(chan));
419  ast_debug(1, "Broadcasting: (TX->1) whisper=%d, (TX->2) barge=%d, (RX<-%d) spy=%d (%s)\n",
420  ast_test_flag(flags, OPTION_WHISPER) ? 1 : 0,
421  ast_test_flag(flags, OPTION_BARGE) ? 1 : 0,
422  readonly ? 1 : 2,
423  ast_test_flag(flags, OPTION_SPY) ? 1 : 0,
424  readonly ? "single" : "both");
425 
426  if (ast_test_flag(flags, OPTION_SPY)) {
427  multispy.chanlist = &chanlist;
428  multispy.readonly = readonly;
429  ast_activate_generator(chan, &spygen, &multispy);
430  } else {
431  /* We're not expecting to read any audio, just broadcast audio to a bunch of other channels. */
433  }
434 
435  while (numchans && ast_waitfor(chan, -1) > 0) {
436  int fres = 0;
437  f = ast_read(chan);
438  if (!f) {
439  ast_debug(1, "Channel %s must have hung up\n", ast_channel_name(chan));
440  res = -1;
441  break;
442  }
443  if (f->frametype != AST_FRAME_VOICE) { /* Ignore any non-voice frames */
444  ast_frfree(f);
445  continue;
446  }
447  /* Write the frame to all our targets. */
448  AST_RWLIST_WRLOCK(&chanlist);
449  AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chanlist, mac, entry) {
450  /* Note that if no media is received, execution is suspended, but assuming continuous or
451  * or frequent audio on the broadcasting channel, we'll quickly enough detect hung up targets.
452  * This isn't really an issue, just something that might be confusing at first, but this is
453  * due to the limitation with audiohooks of using the channel for timing. */
454  if ((ast_test_flag(flags, OPTION_WHISPER) && mac->whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
455  || (ast_test_flag(flags, OPTION_SPY) && mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
456  || (mac->bridge_connected && ast_test_flag(flags, OPTION_BARGE) && mac->bridge_whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)) {
457  /* Even if we're spying only and not actually broadcasting audio, we need to detect channel hangup. */
458  AST_RWLIST_REMOVE_CURRENT(entry);
459  ast_debug(2, "Looks like %s has hung up\n", mac->name);
460  multi_autochan_free(mac);
461  numchans--;
462  ast_debug(2, "%d channel%s remaining in broadcast on %s\n", numchans, ESS(numchans), ast_channel_name(chan));
463  continue;
464  }
465 
466  if (ast_test_flag(flags, OPTION_WHISPER)) {
467  ast_audiohook_lock(&mac->whisper_audiohook);
468  fres |= ast_audiohook_write_frame(&mac->whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
469  ast_audiohook_unlock(&mac->whisper_audiohook);
470  }
471 
472  if (ast_test_flag(flags, OPTION_BARGE)) {
473  /* This hook lets us inject audio into the channel that the spyee is currently
474  * bridged with. If the spyee isn't bridged with anything yet, nothing will
475  * be attached and we'll need to continue attempting to attach the barge
476  * audio hook.
477  * The exception to this is if we are emulating barge by doing it "directly",
478  * that is injecting the frames onto this channel's read queue, rather than
479  * its bridged peer's write queue, then skip this. We only do one or the other. */
480  if (!ast_test_flag(flags, OPTION_REVERSE_FEED) && !mac->bridge_connected && !attach_barge(mac->autochan, &mac->bridge_autochan,
481  &mac->bridge_whisper_audiohook, ast_channel_name(chan), mac->name, flags)) {
482  ast_debug(2, "Attached barge channel for %s\n", mac->name);
483  mac->bridge_connected = 1;
484  }
485 
486  if (mac->bridge_connected) {
487  ast_audiohook_lock(&mac->bridge_whisper_audiohook);
488  fres |= ast_audiohook_write_frame(&mac->bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
489  ast_audiohook_unlock(&mac->bridge_whisper_audiohook);
490  } else if (ast_test_flag(flags, OPTION_REVERSE_FEED)) {
491  /* So, this is really clever...
492  * If we're connected to an n-party bridge instead of a 2-party bridge,
493  * attach_barge will ALWAYS fail because we're connected to a bridge, not
494  * a single peer channel.
495  * Recall that the objective is for injected audio to be audible to both
496  * sides of the channel. So really, the typical way of doing this by
497  * directly injecting frames separately onto both channels is kind of
498  * bizarre to begin with, when you think about it.
499  *
500  * In other words, this is how ChanSpy and this module by default work:
501  * We have audio F to inject onto channels A and B, which are <= bridged =>:
502  * READ <- A -> WRITE <==> READ <- B -> WRITE
503  * F --^ F --^
504  *
505  * So that makes the same audio audible to both channels A and B, but
506  * in kind of a roundabout way. What if the bridged peer changes at
507  * some point, for example?
508  *
509  * While that method works for 2-party bridges, it doesn't work at all
510  * for an n-party bridge, so we do the thing that seems obvious to begin with:
511  * dump the frames onto THIS channel's read queue, and the channels will
512  * make their way into the bridge like any other audio from this channel,
513  * and everything just works perfectly, no matter what kind of bridging
514  * scenario is being used. At that point, we don't even care if we're
515  * bridged or not, and really, why should we?
516  *
517  * In other words, we do this:
518  * READ <- A -> WRITE <==> READ <- B -> WRITE
519  * F --^ F --^
520  */
521  ast_audiohook_lock(&mac->whisper_audiohook);
522  fres |= ast_audiohook_write_frame(&mac->whisper_audiohook, AST_AUDIOHOOK_DIRECTION_READ, f);
523  ast_audiohook_unlock(&mac->whisper_audiohook);
524  }
525  }
526  if (fres) {
527  ast_log(LOG_WARNING, "Failed to write to audiohook for %s\n", mac->name);
528  fres = 0;
529  }
530  }
531  AST_RWLIST_TRAVERSE_SAFE_END;
532  AST_RWLIST_UNLOCK(&chanlist);
533  ast_frfree(f);
534  }
535 
536  if (!numchans) {
537  ast_debug(1, "Exiting due to all target channels having left the broadcast\n");
538  }
539 
540  if (ast_test_flag(flags, OPTION_SPY)) {
542  } else {
544  }
545 
546  /* Cleanup any remaining targets */
547  AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chanlist, mac, entry) {
548  AST_RWLIST_REMOVE_CURRENT(entry);
549  multi_autochan_free(mac);
550  }
551  AST_RWLIST_TRAVERSE_SAFE_END;
552 
554  return res;
555 }
556 
557 static int broadcast_exec(struct ast_channel *chan, const char *data)
558 {
559  struct ast_flags flags;
560  struct ast_format *write_format;
561  int res = -1;
563  AST_APP_ARG(options);
564  AST_APP_ARG(channels); /* Channel list last, so we can have multiple */
565  );
566  char *parse = NULL;
567 
568  if (ast_strlen_zero(data)) {
569  ast_log(LOG_WARNING, "Broadcast requires at least one channel\n");
570  return -1;
571  }
572 
573  parse = ast_strdupa(data);
574  AST_STANDARD_APP_ARGS(args, parse);
575 
576  if (ast_strlen_zero(args.channels)) {
577  ast_log(LOG_WARNING, "Must specify at least one channel for broadcast\n");
578  return -1;
579  }
580  if (args.options) {
581  ast_app_parse_options(spy_opts, &flags, NULL, args.options);
582  } else {
583  ast_clear_flag(&flags, AST_FLAGS_ALL);
584  }
585 
586  if (!ast_test_flag(&flags, OPTION_BARGE) && !ast_test_flag(&flags, OPTION_SPY) && !ast_test_flag(&flags, OPTION_WHISPER)) {
587  ast_log(LOG_WARNING, "At least one of the b, s, or w option must be specified (provided options have no effect)\n");
588  return -1;
589  }
590 
591  write_format = ao2_bump(ast_channel_writeformat(chan));
592  if (ast_set_write_format(chan, ast_format_slin) < 0) {
593  ast_log(LOG_ERROR, "Failed to set write format to slin.\n");
594  goto cleanup;
595  }
596 
597  res = do_broadcast(chan, &flags, args.channels);
598 
599  /* Restore previous write format */
600  if (ast_set_write_format(chan, write_format)) {
601  ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", ast_channel_name(chan));
602  }
603 
604 cleanup:
605  ao2_ref(write_format, -1);
606  return res;
607 }
608 
609 static int unload_module(void)
610 {
611  return ast_unregister_application(app_broadcast);
612 }
613 
614 static int load_module(void)
615 {
616  return ast_register_application_xml(app_broadcast, broadcast_exec);
617 }
618 
619 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Audio Broadcasting");
Main Channel structure associated with a channel.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohook_direction direction, struct ast_frame *frame)
Writes a frame into the audiohook structure.
Definition: audiohook.c:167
#define ast_autochan_channel_lock(autochan)
Lock the autochan's channel lock.
Definition: autochan.h:75
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition: channel.c:2951
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
Audiohooks Architecture.
void ast_channel_clear_flag(struct ast_channel *chan, unsigned int flag)
Clear a flag on a channel.
Definition: channel.c:11034
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
Definition of a media format.
Definition: format.c:43
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition: audiohook.c:484
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define AST_RWLIST_HEAD_INIT(head)
Initializes an rwlist head structure.
Definition: linkedlists.h:639
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition: audiohook.c:124
Utility functions.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags flags)
Initialize an audiohook structure.
Definition: audiohook.c:100
#define ast_audiohook_unlock(ah)
Unlock an audiohook.
Definition: audiohook.h:318
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition: res_stasis.c:327
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2969
List of channel drivers.
Definition: app_dial.c:797
#define ast_debug(level,...)
Log a DEBUG message.
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5803
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
Core PBX routines and definitions.
struct ast_silence_generator * ast_channel_start_silence_generator(struct ast_channel *chan)
Starts a silence generator on the given channel.
Definition: channel.c:8164
"smart" channels that update automatically if a channel is masqueraded
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
Definition: audiohook.c:550
void ast_channel_set_flag(struct ast_channel *chan, unsigned int flag)
Set a flag on a channel.
Definition: channel.c:11027
int ast_channel_is_bridged(const struct ast_channel *chan)
Determine if a channel is in a bridge.
Definition: channel.c:10545
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_silence_generator *state)
Stops a previously-started silence generator on the given channel.
Definition: channel.c:8210
#define ESS(x)
Definition: cli.h:59
union ast_frame::@224 data
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
Definition: channel.c:5144
Structure used to handle boolean flags.
Definition: utils.h:199
#define AST_RWLIST_HEAD(name, type)
Defines a structure to be used to hold a read/write list of specified type.
Definition: linkedlists.h:199
void ast_autochan_destroy(struct ast_autochan *autochan)
destroy an ast_autochan structure
Definition: autochan.c:64
struct ast_autochan * ast_autochan_setup(struct ast_channel *chan)
set up a new ast_autochan structure
Definition: autochan.c:38
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:2893
struct multi_autochan::@9 entry
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2947
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3162
Standard Command Line Interface.
struct ast_channel * ast_channel_bridge_peer(struct ast_channel *chan)
Get the channel's bridge peer only if the bridge is two-party.
Definition: channel.c:10564
Data structure associated with a single frame of data.
Options provided by main asterisk program.
Definition: search.h:40
enum ast_audiohook_status status
Definition: audiohook.h:108
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
enum ast_frame_type frametype
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
#define ast_audiohook_lock(ah)
Lock an audiohook.
Definition: audiohook.h:313
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct ast_frame * ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, struct ast_format *format)
Reads a frame in from the audiohook structure.
Definition: audiohook.c:446
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Media Format Cache API.
#define AST_APP_ARG(name)
Define an application argument.