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