ARI: allow other operations to happen while bridged
[asterisk/asterisk.git] / res / res_stasis_playback.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@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 res_stasis playback support.
22  *
23  * \author David M. Lee, II <dlee@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 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include "asterisk/app.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/bridge.h"
38 #include "asterisk/bridge_internal.h"
39 #include "asterisk/file.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/module.h"
42 #include "asterisk/paths.h"
43 #include "asterisk/stasis_app_impl.h"
44 #include "asterisk/stasis_app_playback.h"
45 #include "asterisk/stasis_channels.h"
46 #include "asterisk/stringfields.h"
47 #include "asterisk/uuid.h"
48
49 /*! Number of hash buckets for playback container. Keep it prime! */
50 #define PLAYBACK_BUCKETS 127
51
52 /*! Default number of milliseconds of media to skip */
53 #define PLAYBACK_DEFAULT_SKIPMS 3000
54
55 #define SOUND_URI_SCHEME "sound:"
56 #define RECORDING_URI_SCHEME "recording:"
57
58 STASIS_MESSAGE_TYPE_DEFN(stasis_app_playback_snapshot_type);
59
60 /*! Container of all current playbacks */
61 static struct ao2_container *playbacks;
62
63 /*! Playback control object for res_stasis */
64 struct stasis_app_playback {
65         AST_DECLARE_STRING_FIELDS(
66                 AST_STRING_FIELD(id);   /*!< Playback unique id */
67                 AST_STRING_FIELD(media);        /*!< Playback media uri */
68                 AST_STRING_FIELD(language);     /*!< Preferred language */
69                 AST_STRING_FIELD(target);       /*!< Playback device uri */
70                 );
71         /*! Control object for the channel we're playing back to */
72         struct stasis_app_control *control;
73         /*! Number of milliseconds to skip before playing */
74         long offsetms;
75         /*! Number of milliseconds to skip for forward/reverse operations */
76         int skipms;
77
78         /*! Set when playback has been completed */
79         int done;
80         /*! Condition for waiting on done to be set */
81         ast_cond_t done_cond;
82         /*! Number of milliseconds of media that has been played */
83         long playedms;
84         /*! Current playback state */
85         enum stasis_app_playback_state state;
86 };
87
88 static void playback_dtor(void *obj)
89 {
90         struct stasis_app_playback *playback = obj;
91
92         ast_string_field_free_memory(playback);
93         ast_cond_destroy(&playback->done_cond);
94 }
95
96 static struct stasis_app_playback *playback_create(
97         struct stasis_app_control *control)
98 {
99         RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
100         char id[AST_UUID_STR_LEN];
101         int res;
102
103         if (!control) {
104                 return NULL;
105         }
106
107         playback = ao2_alloc(sizeof(*playback), playback_dtor);
108         if (!playback || ast_string_field_init(playback, 128)) {
109                 return NULL;
110         }
111
112         res = ast_cond_init(&playback->done_cond, NULL);
113         if (res != 0) {
114                 ast_log(LOG_ERROR, "Error creating done condition: %s\n",
115                         strerror(errno));
116                 return NULL;
117         }
118
119         ast_uuid_generate_str(id, sizeof(id));
120         ast_string_field_set(playback, id, id);
121
122         playback->control = control;
123
124         ao2_ref(playback, +1);
125         return playback;
126 }
127
128 static int playback_hash(const void *obj, int flags)
129 {
130         const struct stasis_app_playback *playback = obj;
131         const char *id = flags & OBJ_KEY ? obj : playback->id;
132         return ast_str_hash(id);
133 }
134
135 static int playback_cmp(void *obj, void *arg, int flags)
136 {
137         struct stasis_app_playback *lhs = obj;
138         struct stasis_app_playback *rhs = arg;
139         const char *rhs_id = flags & OBJ_KEY ? arg : rhs->id;
140
141         if (strcmp(lhs->id, rhs_id) == 0) {
142                 return CMP_MATCH | CMP_STOP;
143         } else {
144                 return 0;
145         }
146 }
147
148 static const char *state_to_string(enum stasis_app_playback_state state)
149 {
150         switch (state) {
151         case STASIS_PLAYBACK_STATE_QUEUED:
152                 return "queued";
153         case STASIS_PLAYBACK_STATE_PLAYING:
154                 return "playing";
155         case STASIS_PLAYBACK_STATE_PAUSED:
156                 return "paused";
157         case STASIS_PLAYBACK_STATE_STOPPED:
158         case STASIS_PLAYBACK_STATE_COMPLETE:
159         case STASIS_PLAYBACK_STATE_CANCELED:
160                 /* It doesn't really matter how we got here, but all of these
161                  * states really just mean 'done' */
162                 return "done";
163         case STASIS_PLAYBACK_STATE_MAX:
164                 break;
165         }
166
167         return "?";
168 }
169
170 static void playback_publish(struct stasis_app_playback *playback)
171 {
172         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
173         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
174         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
175
176         ast_assert(playback != NULL);
177
178         json = stasis_app_playback_to_json(playback);
179         if (json == NULL) {
180                 return;
181         }
182
183         message = ast_channel_blob_create_from_cache(
184                 stasis_app_control_get_channel_id(playback->control),
185                 stasis_app_playback_snapshot_type(), json);
186         if (message == NULL) {
187                 return;
188         }
189
190         stasis_app_control_publish(playback->control, message);
191 }
192
193 static int playback_first_update(struct stasis_app_playback *playback,
194         const char *uniqueid)
195 {
196         int res;
197         SCOPED_AO2LOCK(lock, playback);
198
199         if (playback->state == STASIS_PLAYBACK_STATE_CANCELED) {
200                 ast_log(LOG_NOTICE, "%s: Playback canceled for %s\n",
201                         uniqueid, playback->media);
202                 res = -1;
203         } else {
204                 res = 0;
205                 playback->state = STASIS_PLAYBACK_STATE_PLAYING;
206         }
207
208         playback_publish(playback);
209         return res;
210 }
211
212 static void playback_final_update(struct stasis_app_playback *playback,
213         long playedms, int res, const char *uniqueid)
214 {
215         SCOPED_AO2LOCK(lock, playback);
216
217         playback->playedms = playedms;
218         if (res == 0) {
219                 playback->state = STASIS_PLAYBACK_STATE_COMPLETE;
220         } else {
221                 if (playback->state == STASIS_PLAYBACK_STATE_STOPPED) {
222                         ast_log(LOG_NOTICE, "%s: Playback stopped for %s\n",
223                                 uniqueid, playback->media);
224                 } else {
225                         ast_log(LOG_WARNING, "%s: Playback failed for %s\n",
226                                 uniqueid, playback->media);
227                         playback->state = STASIS_PLAYBACK_STATE_STOPPED;
228                 }
229         }
230
231         playback_publish(playback);
232 }
233
234 /*!
235  * \brief RAII_VAR function to mark a playback as done when leaving scope.
236  */
237 static void mark_as_done(struct stasis_app_playback *playback)
238 {
239         SCOPED_AO2LOCK(lock, playback);
240         playback->done = 1;
241         ast_cond_broadcast(&playback->done_cond);
242 }
243
244 static void play_on_channel(struct stasis_app_playback *playback,
245         struct ast_channel *chan)
246 {
247         RAII_VAR(struct stasis_app_playback *, mark_when_done, playback,
248                 mark_as_done);
249         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
250         RAII_VAR(char *, file, NULL, ast_free);
251         int res;
252         long offsetms;
253
254         /* Even though these local variables look fairly pointless, the avoid
255          * having a bunch of NULL's passed directly into
256          * ast_control_streamfile() */
257         const char *fwd = NULL;
258         const char *rev = NULL;
259         const char *stop = NULL;
260         const char *pause = NULL;
261         const char *restart = NULL;
262
263         ast_assert(playback != NULL);
264
265         offsetms = playback->offsetms;
266
267         res = playback_first_update(playback, ast_channel_uniqueid(chan));
268
269         if (res != 0) {
270                 return;
271         }
272
273         if (ast_channel_state(chan) != AST_STATE_UP) {
274                 ast_answer(chan);
275         }
276
277         if (ast_begins_with(playback->media, SOUND_URI_SCHEME)) {
278                 /* Play sound */
279                 file = ast_strdup(playback->media + strlen(SOUND_URI_SCHEME));
280         } else if (ast_begins_with(playback->media, RECORDING_URI_SCHEME)) {
281                 /* Play recording */
282                 const char *relname =
283                         playback->media + strlen(RECORDING_URI_SCHEME);
284                 if (relname[0] == '/') {
285                         file = ast_strdup(relname);
286                 } else {
287                         ast_asprintf(&file, "%s/%s",
288                                 ast_config_AST_RECORDING_DIR, relname);
289                 }
290         } else {
291                 /* Play URL */
292                 ast_log(LOG_ERROR, "Unimplemented\n");
293                 return;
294         }
295
296         if (!file) {
297                 return;
298         }
299
300         res = ast_control_streamfile_lang(chan, file, fwd, rev, stop, pause,
301                 restart, playback->skipms, playback->language, &offsetms);
302
303         playback_final_update(playback, offsetms, res,
304                 ast_channel_uniqueid(chan));
305
306         return;
307 }
308
309 /*!
310  * \brief Special case code to play while a channel is in a bridge.
311  *
312  * \param bridge_channel The channel's bridge_channel.
313  * \param playback_id Id of the playback to start.
314  */
315 static void play_on_channel_in_bridge(struct ast_bridge_channel *bridge_channel,
316         const char *playback_id)
317 {
318         RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
319
320         playback = stasis_app_playback_find_by_id(playback_id);
321         if (!playback) {
322                 ast_log(LOG_ERROR, "Couldn't find playback %s\n",
323                         playback_id);
324                 return;
325         }
326
327         play_on_channel(playback, bridge_channel->chan);
328 }
329
330 /*!
331  * \brief \ref RAII_VAR function to remove a playback from the global list when
332  * leaving scope.
333  */
334 static void remove_from_playbacks(struct stasis_app_playback *playback)
335 {
336         ao2_unlink_flags(playbacks, playback,
337                 OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
338 }
339
340 static void *play_uri(struct stasis_app_control *control,
341         struct ast_channel *chan, void *data)
342 {
343         RAII_VAR(struct stasis_app_playback *, playback, NULL,
344                 remove_from_playbacks);
345         struct ast_bridge *bridge;
346         int res;
347
348         playback = data;
349
350         if (!control) {
351                 return NULL;
352         }
353
354         bridge = stasis_app_get_bridge(control);
355         if (bridge) {
356                 struct ast_bridge_channel *bridge_chan;
357
358                 /* Queue up playback on the bridge */
359                 ast_bridge_lock(bridge);
360                 bridge_chan = bridge_find_channel(bridge, chan);
361                 if (bridge_chan) {
362                         ast_bridge_channel_queue_playfile(
363                                 bridge_chan,
364                                 play_on_channel_in_bridge,
365                                 playback->id,
366                                 NULL); /* moh_class */
367                 }
368                 ast_bridge_unlock(bridge);
369
370                 /* Wait for playback to complete */
371                 ao2_lock(playback);
372                 while (!playback->done) {
373                         res = ast_cond_wait(&playback->done_cond,
374                                 ao2_object_get_lockaddr(playback));
375                         if (res != 0) {
376                                 ast_log(LOG_ERROR,
377                                         "Error waiting for playback to complete: %s\n",
378                                         strerror(errno));
379                         }
380                 }
381                 ao2_unlock(playback);
382         } else {
383                 play_on_channel(playback, chan);
384         }
385
386         return NULL;
387 }
388
389 static void set_target_uri(
390         struct stasis_app_playback *playback,
391         enum stasis_app_playback_target_type target_type,
392         const char *target_id)
393 {
394         const char *type = NULL;
395         switch (target_type) {
396         case STASIS_PLAYBACK_TARGET_CHANNEL:
397                 type = "channel";
398                 break;
399         case STASIS_PLAYBACK_TARGET_BRIDGE:
400                 type = "bridge";
401                 break;
402         }
403
404         ast_assert(type != NULL);
405
406         ast_string_field_build(playback, target, "%s:%s", type, target_id);
407 }
408
409 struct stasis_app_playback *stasis_app_control_play_uri(
410         struct stasis_app_control *control, const char *uri,
411         const char *language, const char *target_id,
412         enum stasis_app_playback_target_type target_type,
413         int skipms, long offsetms)
414 {
415         RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
416
417         if (skipms < 0 || offsetms < 0) {
418                 return NULL;
419         }
420
421         ast_debug(3, "%s: Sending play(%s) command\n",
422                 stasis_app_control_get_channel_id(control), uri);
423
424         playback = playback_create(control);
425
426         if (skipms == 0) {
427                 skipms = PLAYBACK_DEFAULT_SKIPMS;
428         }
429
430         ast_string_field_set(playback, media, uri);
431         ast_string_field_set(playback, language, language);
432         set_target_uri(playback, target_type, target_id);
433         playback->skipms = skipms;
434         playback->offsetms = offsetms;
435         ao2_link(playbacks, playback);
436
437         playback->state = STASIS_PLAYBACK_STATE_QUEUED;
438         playback_publish(playback);
439
440         /* A ref is kept in the playbacks container; no need to bump */
441         stasis_app_send_command_async(control, play_uri, playback);
442
443         /* Although this should be bumped for the caller */
444         ao2_ref(playback, +1);
445         return playback;
446 }
447
448 enum stasis_app_playback_state stasis_app_playback_get_state(
449         struct stasis_app_playback *control)
450 {
451         SCOPED_AO2LOCK(lock, control);
452         return control->state;
453 }
454
455 const char *stasis_app_playback_get_id(
456         struct stasis_app_playback *control)
457 {
458         /* id is immutable; no lock needed */
459         return control->id;
460 }
461
462 struct stasis_app_playback *stasis_app_playback_find_by_id(const char *id)
463 {
464         return ao2_find(playbacks, id, OBJ_KEY);
465 }
466
467 struct ast_json *stasis_app_playback_to_json(
468         const struct stasis_app_playback *playback)
469 {
470         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
471
472         if (playback == NULL) {
473                 return NULL;
474         }
475
476         json = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
477                 "id", playback->id,
478                 "media_uri", playback->media,
479                 "target_uri", playback->target,
480                 "language", playback->language,
481                 "state", state_to_string(playback->state));
482
483         return ast_json_ref(json);
484 }
485
486 typedef int (*playback_opreation_cb)(struct stasis_app_playback *playback);
487
488 static int playback_noop(struct stasis_app_playback *playback)
489 {
490         return 0;
491 }
492
493 static int playback_cancel(struct stasis_app_playback *playback)
494 {
495         SCOPED_AO2LOCK(lock, playback);
496         playback->state = STASIS_PLAYBACK_STATE_CANCELED;
497         return 0;
498 }
499
500 static int playback_stop(struct stasis_app_playback *playback)
501 {
502         SCOPED_AO2LOCK(lock, playback);
503         playback->state = STASIS_PLAYBACK_STATE_STOPPED;
504         return stasis_app_control_queue_control(playback->control,
505                 AST_CONTROL_STREAM_STOP);
506 }
507
508 static int playback_restart(struct stasis_app_playback *playback)
509 {
510         return stasis_app_control_queue_control(playback->control,
511                 AST_CONTROL_STREAM_RESTART);
512 }
513
514 static int playback_pause(struct stasis_app_playback *playback)
515 {
516         SCOPED_AO2LOCK(lock, playback);
517         playback->state = STASIS_PLAYBACK_STATE_PAUSED;
518         playback_publish(playback);
519         return stasis_app_control_queue_control(playback->control,
520                 AST_CONTROL_STREAM_SUSPEND);
521 }
522
523 static int playback_unpause(struct stasis_app_playback *playback)
524 {
525         SCOPED_AO2LOCK(lock, playback);
526         playback->state = STASIS_PLAYBACK_STATE_PLAYING;
527         playback_publish(playback);
528         return stasis_app_control_queue_control(playback->control,
529                 AST_CONTROL_STREAM_SUSPEND);
530 }
531
532 static int playback_reverse(struct stasis_app_playback *playback)
533 {
534         return stasis_app_control_queue_control(playback->control,
535                 AST_CONTROL_STREAM_REVERSE);
536 }
537
538 static int playback_forward(struct stasis_app_playback *playback)
539 {
540         return stasis_app_control_queue_control(playback->control,
541                 AST_CONTROL_STREAM_FORWARD);
542 }
543
544 /*!
545  * \brief A sparse array detailing how commands should be handled in the
546  * various playback states. Unset entries imply invalid operations.
547  */
548 playback_opreation_cb operations[STASIS_PLAYBACK_STATE_MAX][STASIS_PLAYBACK_MEDIA_OP_MAX] = {
549         [STASIS_PLAYBACK_STATE_QUEUED][STASIS_PLAYBACK_STOP] = playback_cancel,
550         [STASIS_PLAYBACK_STATE_QUEUED][STASIS_PLAYBACK_RESTART] = playback_noop,
551
552         [STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_STOP] = playback_stop,
553         [STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_RESTART] = playback_restart,
554         [STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_PAUSE] = playback_pause,
555         [STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_UNPAUSE] = playback_noop,
556         [STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_REVERSE] = playback_reverse,
557         [STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_FORWARD] = playback_forward,
558
559         [STASIS_PLAYBACK_STATE_PAUSED][STASIS_PLAYBACK_STOP] = playback_stop,
560         [STASIS_PLAYBACK_STATE_PAUSED][STASIS_PLAYBACK_PAUSE] = playback_noop,
561         [STASIS_PLAYBACK_STATE_PAUSED][STASIS_PLAYBACK_UNPAUSE] = playback_unpause,
562
563         [STASIS_PLAYBACK_STATE_COMPLETE][STASIS_PLAYBACK_STOP] = playback_noop,
564         [STASIS_PLAYBACK_STATE_CANCELED][STASIS_PLAYBACK_STOP] = playback_noop,
565         [STASIS_PLAYBACK_STATE_STOPPED][STASIS_PLAYBACK_STOP] = playback_noop,
566 };
567
568 enum stasis_playback_oper_results stasis_app_playback_operation(
569         struct stasis_app_playback *playback,
570         enum stasis_app_playback_media_operation operation)
571 {
572         playback_opreation_cb cb;
573         SCOPED_AO2LOCK(lock, playback);
574
575         ast_assert(playback->state >= 0 && playback->state < STASIS_PLAYBACK_STATE_MAX);
576
577         if (operation < 0 || operation >= STASIS_PLAYBACK_MEDIA_OP_MAX) {
578                 ast_log(LOG_ERROR, "Invalid playback operation %d\n", operation);
579                 return -1;
580         }
581
582         cb = operations[playback->state][operation];
583
584         if (!cb) {
585                 if (playback->state != STASIS_PLAYBACK_STATE_PLAYING) {
586                         /* So we can be specific in our error message. */
587                         return STASIS_PLAYBACK_OPER_NOT_PLAYING;
588                 } else {
589                         /* And, really, all operations should be valid during
590                          * playback */
591                         ast_log(LOG_ERROR,
592                                 "Unhandled operation during playback: %d\n",
593                                 operation);
594                         return STASIS_PLAYBACK_OPER_FAILED;
595                 }
596         }
597
598         return cb(playback) ?
599                 STASIS_PLAYBACK_OPER_FAILED : STASIS_PLAYBACK_OPER_OK;
600 }
601
602 static int load_module(void)
603 {
604         int r;
605
606         r = STASIS_MESSAGE_TYPE_INIT(stasis_app_playback_snapshot_type);
607         if (r != 0) {
608                 return AST_MODULE_LOAD_FAILURE;
609         }
610
611         playbacks = ao2_container_alloc(PLAYBACK_BUCKETS, playback_hash,
612                 playback_cmp);
613         if (!playbacks) {
614                 return AST_MODULE_LOAD_FAILURE;
615         }
616         return AST_MODULE_LOAD_SUCCESS;
617 }
618
619 static int unload_module(void)
620 {
621         ao2_cleanup(playbacks);
622         playbacks = NULL;
623         STASIS_MESSAGE_TYPE_CLEANUP(stasis_app_playback_snapshot_type);
624         return 0;
625 }
626
627 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis application playback support",
628         .load = load_module,
629         .unload = unload_module,
630         .nonoptreq = "res_stasis");