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