Merge "res_pjsip: New endpoint option "refer_blind_progress""
[asterisk/asterisk.git] / res / res_stasis_snoop.c
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"
34 #include "asterisk/stasis_app_impl.h"
35 #include "asterisk/stasis_app_snoop.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 */
50 struct stasis_app_snoop {
51         /*! \brief Timer used for waking up Stasis thread */
52         struct ast_timer *timer;
53         /*! \brief Audiohook used to spy on the channel */
54         struct ast_audiohook spy;
55         /*! \brief Direction for spying */
56         enum ast_audiohook_direction spy_direction;
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 */
60         struct ast_format *spy_format;
61         /*! \brief Audiohook used to whisper on the channel */
62         struct ast_audiohook whisper;
63         /*! \brief Direction for whispering */
64         enum ast_audiohook_direction whisper_direction;
65         /*! \brief Stasis application and arguments */
66         struct ast_str *app;
67         /*! \brief Snoop channel */
68         struct ast_channel *chan;
69         /*! \brief Whether the spy capability is active or not */
70         unsigned int spy_active:1;
71         /*! \brief Whether the whisper capability is active or not */
72         unsigned int whisper_active:1;
73         /*! \brief Uniqueid of the channel this snoop is snooping on */
74         char uniqueid[AST_MAX_UNIQUEID];
75 };
76
77 /*! \brief Destructor for snoop structure */
78 static void snoop_destroy(void *obj)
79 {
80         struct stasis_app_snoop *snoop = obj;
81
82         if (snoop->timer) {
83                 ast_timer_close(snoop->timer);
84         }
85
86         if (snoop->spy_active) {
87                 ast_audiohook_destroy(&snoop->spy);
88         }
89
90         if (snoop->whisper_active) {
91                 ast_audiohook_destroy(&snoop->whisper);
92         }
93
94         ast_free(snoop->app);
95
96         ast_channel_cleanup(snoop->chan);
97 }
98
99 /*! \internal
100  * \brief Publish the chanspy message over Stasis-Core
101  * \param snoop The snoop structure
102  * \start start If non-zero, the spying is starting. Otherwise, the spyer is
103  * finishing
104  */
105 static void publish_chanspy_message(struct stasis_app_snoop *snoop, int start)
106 {
107         RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
108         RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
109         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
110         RAII_VAR(struct ast_channel_snapshot *, snoop_snapshot, NULL, ao2_cleanup);
111         RAII_VAR(struct ast_channel_snapshot *, spyee_snapshot, NULL, ao2_cleanup);
112         struct stasis_message_type *type = start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type();
113
114         blob = ast_json_null();
115         if (!blob || !type) {
116                 return;
117         }
118
119         payload = ast_multi_channel_blob_create(blob);
120         if (!payload) {
121                 return;
122         }
123
124         snoop_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(snoop->chan));
125         if (!snoop_snapshot) {
126                 return;
127         }
128         ast_multi_channel_blob_add_channel(payload, "spyer_channel", snoop_snapshot);
129
130         spyee_snapshot = ast_channel_snapshot_get_latest(snoop->uniqueid);
131         if (spyee_snapshot) {
132                 ast_multi_channel_blob_add_channel(payload, "spyee_channel", spyee_snapshot);
133         }
134
135         message = stasis_message_create(type, payload);
136         if (!message) {
137                 return;
138         }
139
140         stasis_publish(ast_channel_topic(snoop->chan), message);
141 }
142
143 /*! \brief Callback function for writing to a Snoop whisper audiohook */
144 static int snoop_write(struct ast_channel *chan, struct ast_frame *frame)
145 {
146         struct stasis_app_snoop *snoop = ast_channel_tech_pvt(chan);
147
148         if (!snoop->whisper_active) {
149                 return 0;
150         }
151
152         ast_audiohook_lock(&snoop->whisper);
153         if (snoop->whisper_direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
154                 ast_audiohook_write_frame(&snoop->whisper, AST_AUDIOHOOK_DIRECTION_READ, frame);
155                 ast_audiohook_write_frame(&snoop->whisper, AST_AUDIOHOOK_DIRECTION_WRITE, frame);
156         } else {
157                 ast_audiohook_write_frame(&snoop->whisper, snoop->whisper_direction, frame);
158         }
159         ast_audiohook_unlock(&snoop->whisper);
160
161         return 0;
162 }
163
164 /*! \brief Callback function for reading from a Snoop channel */
165 static struct ast_frame *snoop_read(struct ast_channel *chan)
166 {
167         struct stasis_app_snoop *snoop = ast_channel_tech_pvt(chan);
168         struct ast_frame *frame = NULL;
169
170         /* If we fail to ack the timer OR if any active audiohooks are done hangup */
171         if ((ast_timer_ack(snoop->timer, 1) < 0) ||
172                 (snoop->spy_active && snoop->spy.status != AST_AUDIOHOOK_STATUS_RUNNING) ||
173                 (snoop->whisper_active && snoop->whisper.status != AST_AUDIOHOOK_STATUS_RUNNING)) {
174                 return NULL;
175         }
176
177         /* Only get audio from the spy audiohook if it is active */
178         if (!snoop->spy_active) {
179                 return &ast_null_frame;
180         }
181
182         ast_audiohook_lock(&snoop->spy);
183         if (snoop->spy_direction != AST_AUDIOHOOK_DIRECTION_BOTH) {
184                 /*
185                  * When a singular direction is chosen frames are still written to the
186                  * opposing direction's queue. Those frames must be read so the queue
187                  * does not continue to grow, however since they are not needed for the
188                  * selected direction they can be dropped.
189                  */
190                 enum ast_audiohook_direction opposing_direction =
191                         snoop->spy_direction == AST_AUDIOHOOK_DIRECTION_READ ?
192                         AST_AUDIOHOOK_DIRECTION_WRITE : AST_AUDIOHOOK_DIRECTION_READ;
193                 ast_frame_dtor(ast_audiohook_read_frame(&snoop->spy, snoop->spy_samples,
194                                                         opposing_direction, snoop->spy_format));
195         }
196
197         frame = ast_audiohook_read_frame(&snoop->spy, snoop->spy_samples, snoop->spy_direction, snoop->spy_format);
198         ast_audiohook_unlock(&snoop->spy);
199
200         return frame ? frame : &ast_null_frame;
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         struct ast_app *stasis = pbx_findapp("Stasis");
259
260         if (!stasis) {
261                 ast_hangup(snoop->chan);
262                 return NULL;
263         }
264
265         pbx_exec(snoop->chan, stasis, ast_str_buffer(snoop->app));
266
267         ast_hangup(snoop->chan);
268
269         return NULL;
270 }
271
272 /*! \brief Internal helper function which sets up and attaches a snoop audiohook */
273 static int snoop_setup_audiohook(struct ast_channel *chan, enum ast_audiohook_type type, enum stasis_app_snoop_direction requested_direction,
274         enum ast_audiohook_direction *direction, struct ast_audiohook *audiohook)
275 {
276         ast_audiohook_init(audiohook, type, "Snoop", 0);
277
278         if (requested_direction == STASIS_SNOOP_DIRECTION_OUT) {
279                 *direction = AST_AUDIOHOOK_DIRECTION_WRITE;
280         } else if (requested_direction == STASIS_SNOOP_DIRECTION_IN) {
281                 *direction = AST_AUDIOHOOK_DIRECTION_READ;
282         } else if (requested_direction == STASIS_SNOOP_DIRECTION_BOTH) {
283                 *direction = AST_AUDIOHOOK_DIRECTION_BOTH;
284         } else {
285                 return -1;
286         }
287
288         return ast_audiohook_attach(chan, audiohook);
289 }
290
291 /*! \brief Helper function which gets the format for a Snoop channel based on the channel being snooped on */
292 static void snoop_determine_format(struct ast_channel *chan, struct stasis_app_snoop *snoop)
293 {
294         SCOPED_CHANNELLOCK(lock, chan);
295         unsigned int rate = MAX(ast_format_get_sample_rate(ast_channel_rawwriteformat(chan)),
296                 ast_format_get_sample_rate(ast_channel_rawreadformat(chan)));
297
298         snoop->spy_format = ast_format_cache_get_slin_by_rate(rate);
299 }
300
301 struct ast_channel *stasis_app_control_snoop(struct ast_channel *chan,
302         enum stasis_app_snoop_direction spy, enum stasis_app_snoop_direction whisper,
303         const char *app, const char *app_args, const char *snoop_id)
304 {
305         RAII_VAR(struct stasis_app_snoop *, snoop, NULL, ao2_cleanup);
306         struct ast_format_cap *caps;
307         pthread_t thread;
308         struct ast_assigned_ids assignedids = {
309                 .uniqueid = snoop_id,
310         };
311
312         if (spy == STASIS_SNOOP_DIRECTION_NONE &&
313                 whisper == STASIS_SNOOP_DIRECTION_NONE) {
314                 return NULL;
315         }
316
317         snoop = ao2_alloc_options(sizeof(*snoop), snoop_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
318         if (!snoop) {
319                 return NULL;
320         }
321
322         /* Allocate a buffer to store the Stasis application and arguments in */
323         snoop->app = ast_str_create(64);
324         if (!snoop->app) {
325                 return NULL;
326         }
327
328         ast_str_set(&snoop->app, 0, "%s", app);
329         if (!ast_strlen_zero(app_args)) {
330                 ast_str_append(&snoop->app, 0, ",%s", app_args);
331         }
332
333         /* Set up a timer for the Snoop channel so it wakes up at a specific interval */
334         snoop->timer = ast_timer_open();
335         if (!snoop->timer) {
336                 return NULL;
337         }
338         ast_timer_set_rate(snoop->timer, 1000 / SNOOP_INTERVAL);
339
340         /* Determine which signed linear format should be used */
341         snoop_determine_format(chan, snoop);
342
343         /* Allocate a Snoop channel and set up various parameters */
344         snoop->chan = ast_channel_alloc(1, AST_STATE_UP, "", "", "", "", "", &assignedids, NULL, 0, "Snoop/%s-%08x", ast_channel_uniqueid(chan),
345                 (unsigned)ast_atomic_fetchadd_int((int *)&chan_idx, +1));
346         if (!snoop->chan) {
347                 return NULL;
348         }
349
350         ast_copy_string(snoop->uniqueid, ast_channel_uniqueid(chan), sizeof(snoop->uniqueid));
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 */
361         caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
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
384                 snoop->spy_samples = ast_format_get_sample_rate(snoop->spy_format) / (1000 / SNOOP_INTERVAL);
385                 snoop->spy_active = 1;
386         }
387
388         /* If whispering is enabled set up the audiohook */
389         if (whisper != STASIS_SNOOP_DIRECTION_NONE) {
390                 if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_WHISPER, whisper, &snoop->whisper_direction, &snoop->whisper)) {
391                         ast_hangup(snoop->chan);
392                         return NULL;
393                 }
394
395                 snoop->whisper_active = 1;
396         }
397
398         /* Create the thread which services the Snoop channel */
399         ao2_ref(snoop, +1);
400         if (ast_pthread_create_detached_background(&thread, NULL, snoop_stasis_thread, snoop)) {
401                 ao2_cleanup(snoop);
402
403                 /* No other thread is servicing this channel so we can immediately hang it up */
404                 ast_hangup(snoop->chan);
405                 return NULL;
406         }
407
408         publish_chanspy_message(snoop, 1);
409
410         /* The caller of this has a reference as well */
411         return ast_channel_ref(snoop->chan);
412 }
413
414 static int load_module(void)
415 {
416         return AST_MODULE_LOAD_SUCCESS;
417 }
418
419 static int unload_module(void)
420 {
421         return 0;
422 }
423
424 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis application snoop support",
425         .support_level = AST_MODULE_SUPPORT_CORE,
426         .load = load_module,
427         .unload = unload_module,
428         .nonoptreq = "res_stasis"
429 );