Asterisk - The Open Source Telephony Project  21.4.1
res_stasis_snoop.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Stasis application snoop control support.
22  *
23  * \author Joshua Colp <jcolp@digium.com>
24  */
25 
26 /*** MODULEINFO
27  <depend type="module">res_stasis</depend>
28  <support_level>core</support_level>
29  ***/
30 
31 #include "asterisk.h"
32 
33 #include "asterisk/module.h"
36 #include "asterisk/audiohook.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/timing.h"
39 #include "asterisk/stasis_channels.h"
40 #include "asterisk/json.h"
41 #include "asterisk/format_cache.h"
42 
43 /*! \brief The interval (in milliseconds) that the Snoop timer is triggered, also controls length of audio within frames */
44 #define SNOOP_INTERVAL 20
45 
46 /*! \brief Index used to keep Snoop channel names unique */
47 static unsigned int chan_idx;
48 
49 /*! \brief Structure which contains all of the snoop information */
51  /*! \brief Timer used for waking up Stasis thread */
52  struct ast_timer *timer;
53  /*! \brief Audiohook used to spy on the channel */
55  /*! \brief Direction for spying */
57  /*! \brief Number of samples to be read in when spying */
58  unsigned int spy_samples;
59  /*! \brief Format in use by the spy audiohook */
61  /*! \brief Audiohook used to whisper on the channel */
63  /*! \brief Direction for whispering */
65  /*! \brief Stasis application and arguments */
66  struct ast_str *app;
67  /*! \brief Snoop channel */
68  struct ast_channel *chan;
69  /*! \brief The channel that the Snoop channel is snooping on */
71  /*! \brief Whether the spy capability is active or not */
72  unsigned int spy_active:1;
73  /*! \brief Whether the whisper capability is active or not */
74  unsigned int whisper_active:1;
75  /*! \brief A frame of silence to use when the audiohook returns null */
77 };
78 
79 /*! \brief Destructor for snoop structure */
80 static void snoop_destroy(void *obj)
81 {
82  struct stasis_app_snoop *snoop = obj;
83 
84  if (snoop->timer) {
85  ast_timer_close(snoop->timer);
86  }
87 
88  if (snoop->spy_active) {
89  ast_audiohook_destroy(&snoop->spy);
90  }
91 
92  if (snoop->whisper_active) {
94  }
95 
96  if (snoop->silence.data.ptr) {
97  ast_free(snoop->silence.data.ptr);
98  snoop->silence.data.ptr = NULL;
99  }
100 
101  ast_free(snoop->app);
102 
104  ast_channel_cleanup(snoop->chan);
105 }
106 
107 /*! \internal
108  * \brief Publish the chanspy message over Stasis-Core
109  * \param snoop The snoop structure
110  * \param start If non-zero, the spying is starting. Otherwise, the spyer is
111  * finishing
112  */
113 static void publish_chanspy_message(struct stasis_app_snoop *snoop, int start)
114 {
115  RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
116  RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
117  RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
118  RAII_VAR(struct ast_channel_snapshot *, snoop_snapshot, NULL, ao2_cleanup);
119  RAII_VAR(struct ast_channel_snapshot *, spyee_snapshot, NULL, ao2_cleanup);
121 
122  blob = ast_json_null();
123  if (!blob || !type) {
124  return;
125  }
126 
127  payload = ast_multi_channel_blob_create(blob);
128  if (!payload) {
129  return;
130  }
131 
132  snoop_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(snoop->chan));
133  if (!snoop_snapshot) {
134  return;
135  }
136  ast_multi_channel_blob_add_channel(payload, "spyer_channel", snoop_snapshot);
137 
138  if (snoop->spyee_chan) {
139  ast_channel_lock(snoop->spyee_chan);
140  spyee_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(snoop->spyee_chan));
141  ast_channel_unlock(snoop->spyee_chan);
142  if (spyee_snapshot) {
143  ast_multi_channel_blob_add_channel(payload, "spyee_channel", spyee_snapshot);
144  }
145  }
146 
147  message = stasis_message_create(type, payload);
148  if (!message) {
149  return;
150  }
151 
153 }
154 
155 /*! \brief Callback function for writing to a Snoop whisper audiohook */
156 static int snoop_write(struct ast_channel *chan, struct ast_frame *frame)
157 {
158  struct stasis_app_snoop *snoop = ast_channel_tech_pvt(chan);
159 
160  if (!snoop->whisper_active) {
161  return 0;
162  }
163 
164  ast_audiohook_lock(&snoop->whisper);
168  } else {
169  ast_audiohook_write_frame(&snoop->whisper, snoop->whisper_direction, frame);
170  }
171  ast_audiohook_unlock(&snoop->whisper);
172 
173  return 0;
174 }
175 
176 /*! \brief Callback function for reading from a Snoop channel */
177 static struct ast_frame *snoop_read(struct ast_channel *chan)
178 {
179  struct stasis_app_snoop *snoop = ast_channel_tech_pvt(chan);
180  struct ast_frame *frame = NULL;
181 
182  /* If we fail to ack the timer OR if any active audiohooks are done hangup */
183  if ((ast_timer_ack(snoop->timer, 1) < 0) ||
184  (snoop->spy_active && snoop->spy.status != AST_AUDIOHOOK_STATUS_RUNNING) ||
186  return NULL;
187  }
188 
189  /* Only get audio from the spy audiohook if it is active */
190  if (!snoop->spy_active) {
191  return &ast_null_frame;
192  }
193 
194  ast_audiohook_lock(&snoop->spy);
195 
196  frame = ast_audiohook_read_frame(&snoop->spy, snoop->spy_samples, snoop->spy_direction, snoop->spy_format);
197 
198  ast_audiohook_unlock(&snoop->spy);
199 
200  return frame ? frame : &snoop->silence;
201 }
202 
203 /*! \brief Callback function for hanging up a Snoop channel */
204 static int snoop_hangup(struct ast_channel *chan)
205 {
206  struct stasis_app_snoop *snoop = ast_channel_tech_pvt(chan);
207 
208  if (snoop->spy_active) {
209  ast_audiohook_lock(&snoop->spy);
210  ast_audiohook_detach(&snoop->spy);
211  ast_audiohook_unlock(&snoop->spy);
212  }
213 
214  if (snoop->whisper_active) {
215  ast_audiohook_lock(&snoop->whisper);
216  ast_audiohook_detach(&snoop->whisper);
217  ast_audiohook_unlock(&snoop->whisper);
218  }
219 
220  publish_chanspy_message(snoop, 0);
221 
222  ao2_cleanup(snoop);
223 
224  ast_channel_tech_pvt_set(chan, NULL);
225 
226  return 0;
227 }
228 
229 static int snoop_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
230 {
231  struct stasis_app_snoop *snoop = ast_channel_tech_pvt(oldchan);
232 
233  if (snoop->chan != oldchan) {
234  return -1;
235  }
236 
237  ast_channel_unref(snoop->chan);
238  ast_channel_ref(newchan);
239  snoop->chan = newchan;
240 
241  return 0;
242 }
243 
244 /*! \brief Channel interface declaration */
245 static struct ast_channel_tech snoop_tech = {
246  .type = "Snoop",
247  .description = "Snoop Channel Driver",
248  .write = snoop_write,
249  .read = snoop_read,
250  .hangup = snoop_hangup,
251  .fixup = snoop_fixup,
252 };
253 
254 /*! \brief Thread used for running the Stasis application */
255 static void *snoop_stasis_thread(void *obj)
256 {
257  RAII_VAR(struct stasis_app_snoop *, snoop, obj, ao2_cleanup);
258 
259  ast_pbx_exec_application(snoop->chan, "Stasis", ast_str_buffer(snoop->app));
260 
261  ast_hangup(snoop->chan);
262 
263  return NULL;
264 }
265 
266 /*! \brief Internal helper function which sets up and attaches a snoop audiohook */
267 static int snoop_setup_audiohook(struct ast_channel *chan, enum ast_audiohook_type type, enum stasis_app_snoop_direction requested_direction,
268  enum ast_audiohook_direction *direction, struct ast_audiohook *audiohook)
269 {
270  ast_audiohook_init(audiohook, type, "Snoop", 0);
271 
272  if (requested_direction == STASIS_SNOOP_DIRECTION_OUT) {
273  *direction = AST_AUDIOHOOK_DIRECTION_WRITE;
274  } else if (requested_direction == STASIS_SNOOP_DIRECTION_IN) {
275  *direction = AST_AUDIOHOOK_DIRECTION_READ;
276  } else if (requested_direction == STASIS_SNOOP_DIRECTION_BOTH) {
277  *direction = AST_AUDIOHOOK_DIRECTION_BOTH;
278  } else {
279  return -1;
280  }
281 
282  /* Set the audiohook direction so we don't write unnecessary frames */
283  if (ast_audiohook_set_frame_feed_direction(audiohook, *direction)) {
284  /* If we are unable to set direction, the audiohook either failed to init
285  or someone else started using it already. If we don't bail here, we risk
286  feeding frames that will never be read */
287  return -1;
288  }
289 
290  return ast_audiohook_attach(chan, audiohook);
291 }
292 
293 /*! \brief Helper function which gets the format for a Snoop channel based on the channel being snooped on */
294 static void snoop_determine_format(struct ast_channel *chan, struct stasis_app_snoop *snoop)
295 {
296  SCOPED_CHANNELLOCK(lock, chan);
297  unsigned int rate = MAX(ast_format_get_sample_rate(ast_channel_rawwriteformat(chan)),
298  ast_format_get_sample_rate(ast_channel_rawreadformat(chan)));
299 
301 }
302 
305  const char *app, const char *app_args, const char *snoop_id)
306 {
307  RAII_VAR(struct stasis_app_snoop *, snoop, NULL, ao2_cleanup);
308  struct ast_format_cap *caps;
309  pthread_t thread;
310  struct ast_assigned_ids assignedids = {
311  .uniqueid = snoop_id,
312  };
313 
314  if (spy == STASIS_SNOOP_DIRECTION_NONE &&
315  whisper == STASIS_SNOOP_DIRECTION_NONE) {
316  return NULL;
317  }
318 
319  snoop = ao2_alloc_options(sizeof(*snoop), snoop_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
320  if (!snoop) {
321  return NULL;
322  }
323 
324  /* Allocate a buffer to store the Stasis application and arguments in */
325  snoop->app = ast_str_create(64);
326  if (!snoop->app) {
327  return NULL;
328  }
329 
330  ast_str_set(&snoop->app, 0, "%s", app);
331  if (!ast_strlen_zero(app_args)) {
332  ast_str_append(&snoop->app, 0, ",%s", app_args);
333  }
334 
335  /* Set up a timer for the Snoop channel so it wakes up at a specific interval */
336  snoop->timer = ast_timer_open();
337  if (!snoop->timer) {
338  return NULL;
339  }
340  ast_timer_set_rate(snoop->timer, 1000 / SNOOP_INTERVAL);
341 
342  /* Determine which signed linear format should be used */
343  snoop_determine_format(chan, snoop);
344 
345  /* Allocate a Snoop channel and set up various parameters */
346  snoop->chan = ast_channel_alloc(1, AST_STATE_UP, "", "", "", "", "", &assignedids, NULL, 0, "Snoop/%s-%08x", ast_channel_uniqueid(chan),
347  (unsigned)ast_atomic_fetchadd_int((int *)&chan_idx, +1));
348  if (!snoop->chan) {
349  return NULL;
350  }
351 
352  /* To keep the channel valid on the Snoop structure until it is destroyed we bump the ref up here */
353  ast_channel_ref(snoop->chan);
354 
355  ast_channel_tech_set(snoop->chan, &snoop_tech);
356  ao2_ref(snoop, +1);
357  ast_channel_tech_pvt_set(snoop->chan, snoop);
358  ast_channel_set_fd(snoop->chan, 0, ast_timer_fd(snoop->timer));
359 
360  /* The format on the Snoop channel will be this signed linear format, and it will never change */
362  if (!caps) {
363  ast_channel_unlock(snoop->chan);
364  ast_hangup(snoop->chan);
365  return NULL;
366  }
367  ast_format_cap_append(caps, snoop->spy_format, 0);
368  ast_channel_nativeformats_set(snoop->chan, caps);
369  ao2_ref(caps, -1);
370 
371  ast_channel_set_writeformat(snoop->chan, snoop->spy_format);
372  ast_channel_set_rawwriteformat(snoop->chan, snoop->spy_format);
373  ast_channel_set_readformat(snoop->chan, snoop->spy_format);
374  ast_channel_set_rawreadformat(snoop->chan, snoop->spy_format);
375 
376  ast_channel_unlock(snoop->chan);
377 
378  if (spy != STASIS_SNOOP_DIRECTION_NONE) {
379  if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_SPY, spy, &snoop->spy_direction, &snoop->spy)) {
380  ast_hangup(snoop->chan);
381  return NULL;
382  }
383 
385  snoop->spy_active = 1;
386 
388  snoop->silence.datalen = snoop->spy_samples * sizeof(uint16_t),
389  snoop->silence.samples = snoop->spy_samples,
390  snoop->silence.mallocd = 0,
391  snoop->silence.offset = 0,
392  snoop->silence.src = __PRETTY_FUNCTION__,
393  snoop->silence.subclass.format = snoop->spy_format,
394  snoop->silence.data.ptr = ast_calloc(snoop->spy_samples, sizeof(uint16_t));
395  if (!snoop->silence.data.ptr) {
396  ast_hangup(snoop->chan);
397  return NULL;
398  }
399  }
400 
401  /* If whispering is enabled set up the audiohook */
402  if (whisper != STASIS_SNOOP_DIRECTION_NONE) {
403  if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_WHISPER, whisper, &snoop->whisper_direction, &snoop->whisper)) {
404  ast_hangup(snoop->chan);
405  return NULL;
406  }
407 
408  snoop->whisper_active = 1;
409  }
410 
411  /* Create the thread which services the Snoop channel */
412  ao2_ref(snoop, +1);
413  if (ast_pthread_create_detached_background(&thread, NULL, snoop_stasis_thread, snoop)) {
414  ao2_cleanup(snoop);
415 
416  /* No other thread is servicing this channel so we can immediately hang it up */
417  ast_hangup(snoop->chan);
418  return NULL;
419  }
420 
421  /* Keep a reference to the channel we are spying on */
422  snoop->spyee_chan = ast_channel_ref(chan);
423 
424  publish_chanspy_message(snoop, 1);
425 
426  /* The caller of this has a reference as well */
427  return ast_channel_ref(snoop->chan);
428 }
429 
430 static int load_module(void)
431 {
433 }
434 
435 static int unload_module(void)
436 {
437  return 0;
438 }
439 
440 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis application snoop support",
441  .support_level = AST_MODULE_SUPPORT_CORE,
442  .load = load_module,
443  .unload = unload_module,
444  .requires = "res_stasis",
445 );
int ast_audiohook_set_frame_feed_direction(struct ast_audiohook *audiohook, enum ast_audiohook_direction direction)
Sets direction on audiohook.
Definition: audiohook.c:150
Main Channel structure associated with a channel.
stasis_app_snoop_direction
Directions for audio stream flow.
Asterisk main include file. File version handling, generic pbx functions.
struct ast_audiohook spy
Audiohook used to spy on the channel.
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
struct ast_str * app
Stasis application and arguments.
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
Audio stream in from the channel.
Audiohooks Architecture.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
pthread_t thread
Definition: app_sla.c:329
struct ast_audiohook whisper
Audiohook used to whisper on the channel.
Structure representing a snapshot of channel state.
void ast_timer_close(struct ast_timer *handle)
Close an opened timing handle.
Definition: timing.c:154
Structure to pass both assignedid values to channel drivers.
Definition: channel.h:604
struct ast_timer * ast_timer_open(void)
Open a timer.
Definition: timing.c:122
static int snoop_hangup(struct ast_channel *chan)
Callback function for hanging up a Snoop channel.
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
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
Audio stream to AND from the channel.
struct stasis_message_type * ast_channel_chanspy_start_type(void)
Message type for when a channel starts spying on another channel.
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition: audiohook.c:124
struct ast_channel * spyee_chan
The channel that the Snoop channel is snooping on.
static struct ast_frame * snoop_read(struct ast_channel *chan)
Callback function for reading from a Snoop channel.
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:757
struct ast_frame_subclass subclass
static int snoop_write(struct ast_channel *chan, struct ast_frame *frame)
Callback function for writing to a Snoop whisper audiohook.
struct ast_json * ast_json_null(void)
Get the JSON null value.
Definition: json.c:248
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
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
unsigned int spy_samples
Number of samples to be read in when spying.
Structure which contains all of the snoop information.
enum ast_audiohook_direction spy_direction
Direction for spying.
#define ast_audiohook_unlock(ah)
Unlock an audiohook.
Definition: audiohook.h:318
static unsigned int chan_idx
Index used to keep Snoop channel names unique.
ast_mutex_t lock
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition: lock.h:619
Asterisk JSON abstraction layer.
struct ast_channel * chan
Snoop channel.
const char * src
ast_audiohook_type
Definition: audiohook.h:35
void ast_multi_channel_blob_add_channel(struct ast_multi_channel_blob *obj, const char *role, struct ast_channel_snapshot *snapshot)
Add a ast_channel_snapshot to a ast_multi_channel_blob object.
#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
Audio stream out to the channel.
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
Structure to describe a channel "technology", ie a channel driver See for examples: ...
Definition: channel.h:628
Core PBX routines and definitions.
static void * snoop_stasis_thread(void *obj)
Thread used for running the Stasis application.
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
Acknowledge a timer event.
Definition: timing.c:171
int ast_pbx_exec_application(struct ast_channel *chan, const char *app_name, const char *app_args)
Execute an application.
Definition: pbx_app.c:501
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
Definition: audiohook.c:550
Backend API for implementing components of res_stasis.
Support for dynamic strings.
Definition: strings.h:623
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
static struct ast_channel_tech snoop_tech
Channel interface declaration.
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1511
union ast_frame::@224 data
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
static void snoop_determine_format(struct ast_channel *chan, struct stasis_app_snoop *snoop)
Helper function which gets the format for a Snoop channel based on the channel being snooped on...
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2541
int ast_timer_fd(const struct ast_timer *handle)
Get a poll()-able file descriptor for a timer.
Definition: timing.c:161
int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate)
Set the timing tick rate.
Definition: timing.c:166
#define SNOOP_INTERVAL
The interval (in milliseconds) that the Snoop timer is triggered, also controls length of audio withi...
struct ast_channel * stasis_app_control_snoop(struct ast_channel *chan, enum stasis_app_snoop_direction spy, enum stasis_app_snoop_direction whisper, const char *app, const char *app_args, const char *snoop_id)
Create a snoop on the provided channel.
void ast_channel_set_fd(struct ast_channel *chan, int which, int fd)
Definition: channel.c:2426
struct ast_timer * timer
Timer used for waking up Stasis thread.
ast_audiohook_direction
Definition: audiohook.h:48
unsigned int whisper_active
Whether the whisper capability is active or not.
struct ast_frame ast_null_frame
Definition: main/frame.c:79
enum ast_audiohook_direction whisper_direction
Direction for whispering.
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2947
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object...
Stasis Application Snoop API. See StasisApplication API" for detailed documentation.
A multi channel blob data structure for multi_channel_blob stasis messages.
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition: format.c:379
unsigned int spy_active
Whether the spy capability is active or not.
Data structure associated with a single frame of data.
Abstract JSON element (object, array, string, int, ...).
enum ast_audiohook_status status
Definition: audiohook.h:108
enum ast_frame_type frametype
struct ast_format * format
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
#define ast_audiohook_lock(ah)
Lock an audiohook.
Definition: audiohook.h:313
#define ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, assignedids, requestor, amaflag,...)
Create a channel structure.
Definition: channel.h:1258
Asterisk module definitions.
struct ast_format * spy_format
Format in use by the spy audiohook.
static int snoop_setup_audiohook(struct ast_channel *chan, enum ast_audiohook_type type, enum stasis_app_snoop_direction requested_direction, enum ast_audiohook_direction *direction, struct ast_audiohook *audiohook)
Internal helper function which sets up and attaches a snoop audiohook.
#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
struct ast_frame silence
A frame of silence to use when the audiohook returns null.
static void snoop_destroy(void *obj)
Destructor for snoop structure.
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
Timing source management.
struct ast_multi_channel_blob * ast_multi_channel_blob_create(struct ast_json *blob)
Create a ast_multi_channel_blob suitable for a stasis_message.
struct stasis_message_type * ast_channel_chanspy_stop_type(void)
Message type for when a channel stops spying on another channel.
struct ast_format * ast_format_cache_get_slin_by_rate(unsigned int rate)
Retrieve the best signed linear format given a sample rate.
Definition: format_cache.c:512
Media Format Cache API.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659