Asterisk - The Open Source Telephony Project  GIT-master-44aef04
res_stasis_playback.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  * 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 #include "asterisk/app.h"
35 #include "asterisk/astobj2.h"
36 #include "asterisk/bridge.h"
38 #include "asterisk/file.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/module.h"
41 #include "asterisk/paths.h"
46 #include "asterisk/stringfields.h"
47 #include "asterisk/uuid.h"
48 #include "asterisk/say.h"
49 #include "asterisk/indications.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 #define NUMBER_URI_SCHEME "number:"
60 #define DIGITS_URI_SCHEME "digits:"
61 #define CHARACTERS_URI_SCHEME "characters:"
62 #define TONE_URI_SCHEME "tone:"
63 
64 /*! Container of all current playbacks */
65 static struct ao2_container *playbacks;
66 
67 /*! Playback control object for res_stasis */
70  AST_STRING_FIELD(id); /*!< Playback unique id */
71  AST_STRING_FIELD(media); /*!< The current media playing */
72  AST_STRING_FIELD(language); /*!< Preferred language */
73  AST_STRING_FIELD(target); /*!< Playback device uri */
74  );
75  /*! The list of medias to play back */
76  AST_VECTOR(, char *) medias;
77 
78  /*! The current index in \c medias we're playing */
79  size_t media_index;
80 
81  /*! Control object for the channel we're playing back to */
82  struct stasis_app_control *control;
83  /*! Number of milliseconds to skip before playing */
84  long offsetms;
85  /*! Number of milliseconds to skip for forward/reverse operations */
86  int skipms;
87  /*! Number of milliseconds of media that has been played */
88  long playedms;
89  /*! Current playback state */
91  /*! Set when the playback can be controlled */
92  unsigned int controllable:1;
93 };
94 
96  const struct stasis_message_sanitizer *sanitize)
97 {
98  struct ast_channel_blob *channel_blob = stasis_message_data(message);
99  struct ast_json *blob = channel_blob->blob;
100  const char *state =
102  const char *type;
103 
104  if (!strcmp(state, "playing")) {
105  type = "PlaybackStarted";
106  } else if (!strcmp(state, "continuing")) {
107  type = "PlaybackContinuing";
108  } else if (!strcmp(state, "done")) {
109  type = "PlaybackFinished";
110  } else {
111  return NULL;
112  }
113 
114  return ast_json_pack("{s: s, s: o?, s: O}",
115  "type", type,
116  "timestamp", ast_json_timeval(*stasis_message_timestamp(message), NULL),
117  "playback", blob);
118 }
119 
121  .to_json = playback_to_json,
122 );
123 
124 static void playback_dtor(void *obj)
125 {
126  struct stasis_app_playback *playback = obj;
127  int i;
128 
129  for (i = 0; i < AST_VECTOR_SIZE(&playback->medias); i++) {
130  char *media = AST_VECTOR_GET(&playback->medias, i);
131 
132  ast_free(media);
133  }
134  AST_VECTOR_FREE(&playback->medias);
135 
136  ao2_cleanup(playback->control);
138 }
139 
141  struct stasis_app_control *control, const char *id)
142 {
143  RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
144  char uuid[AST_UUID_STR_LEN];
145 
146  if (!control) {
147  return NULL;
148  }
149 
150  playback = ao2_alloc(sizeof(*playback), playback_dtor);
151  if (!playback || ast_string_field_init(playback, 128)) {
152  return NULL;
153  }
154 
155  if (AST_VECTOR_INIT(&playback->medias, 8)) {
156  ao2_ref(playback, -1);
157  return NULL;
158  }
159 
160  if (!ast_strlen_zero(id)) {
161  ast_string_field_set(playback, id, id);
162  } else {
163  ast_uuid_generate_str(uuid, sizeof(uuid));
164  ast_string_field_set(playback, id, uuid);
165  }
166 
167  ao2_ref(control, +1);
168  playback->control = control;
169 
170  ao2_ref(playback, +1);
171  return playback;
172 }
173 
174 static int playback_hash(const void *obj, int flags)
175 {
176  const struct stasis_app_playback *playback = obj;
177  const char *id = flags & OBJ_KEY ? obj : playback->id;
178  return ast_str_hash(id);
179 }
180 
181 static int playback_cmp(void *obj, void *arg, int flags)
182 {
183  struct stasis_app_playback *lhs = obj;
184  struct stasis_app_playback *rhs = arg;
185  const char *rhs_id = flags & OBJ_KEY ? arg : rhs->id;
186 
187  if (strcmp(lhs->id, rhs_id) == 0) {
188  return CMP_MATCH | CMP_STOP;
189  } else {
190  return 0;
191  }
192 }
193 
195 {
196  switch (state) {
198  return "queued";
200  return "playing";
202  return "paused";
204  return "continuing";
208  /* It doesn't really matter how we got here, but all of these
209  * states really just mean 'done' */
210  return "done";
212  break;
213  }
214 
215  return "?";
216 }
217 
218 static void playback_publish(struct stasis_app_playback *playback)
219 {
220  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
222 
223  ast_assert(playback != NULL);
224 
225  json = stasis_app_playback_to_json(playback);
226  if (json == NULL) {
227  return;
228  }
229 
231  stasis_app_control_get_channel_id(playback->control),
233  if (message == NULL) {
234  return;
235  }
236 
237  stasis_app_control_publish(playback->control, message);
238 }
239 
240 static int playback_first_update(struct stasis_app_playback *playback,
241  const char *uniqueid)
242 {
243  int res;
244  SCOPED_AO2LOCK(lock, playback);
245 
246  if (playback->state == STASIS_PLAYBACK_STATE_CANCELED) {
247  ast_log(LOG_NOTICE, "%s: Playback canceled for %s\n",
248  uniqueid, playback->media);
249  res = -1;
250  } else {
251  res = 0;
252  playback->state = STASIS_PLAYBACK_STATE_PLAYING;
253  }
254 
255  playback_publish(playback);
256  return res;
257 }
258 
259 static void playback_final_update(struct stasis_app_playback *playback,
260  long playedms, int res, const char *uniqueid)
261 {
262  SCOPED_AO2LOCK(lock, playback);
263 
264  playback->playedms = playedms;
265  if (res == 0) {
266  if (playback->media_index == AST_VECTOR_SIZE(&playback->medias) - 1) {
267  playback->state = STASIS_PLAYBACK_STATE_COMPLETE;
268  } else {
269  playback->state = STASIS_PLAYBACK_STATE_CONTINUING;
270  }
271  } else {
272  if (playback->state == STASIS_PLAYBACK_STATE_STOPPED) {
273  ast_log(LOG_NOTICE, "%s: Playback stopped for %s\n",
274  uniqueid, playback->media);
275  } else {
276  ast_log(LOG_WARNING, "%s: Playback failed for %s\n",
277  uniqueid, playback->media);
278  playback->state = STASIS_PLAYBACK_STATE_STOPPED;
279  }
280  }
281 
282  playback_publish(playback);
283 }
284 
285 static void play_on_channel(struct stasis_app_playback *playback,
286  struct ast_channel *chan)
287 {
288  int res;
289  long offsetms;
290  size_t index;
291 
292  /* Even though these local variables look fairly pointless, they avoid
293  * having a bunch of NULL's passed directly into
294  * ast_control_streamfile() */
295  const char *fwd = NULL;
296  const char *rev = NULL;
297  const char *stop = NULL;
298  const char *pause = NULL;
299  const char *restart = NULL;
300 
301  ast_assert(playback != NULL);
302 
303  if (ast_channel_state(chan) != AST_STATE_UP) {
305  }
306 
307  offsetms = playback->offsetms;
308 
309  for (index = 0; index < AST_VECTOR_SIZE(&playback->medias); index++) {
310  playback->media_index = index;
311 
312  /* Set the current media to play */
313  ast_string_field_set(playback, media, AST_VECTOR_GET(&playback->medias, playback->media_index));
314 
315  res = playback_first_update(playback, ast_channel_uniqueid(chan));
316  if (res != 0) {
317  return;
318  }
319 
320  if (ast_begins_with(playback->media, SOUND_URI_SCHEME)) {
321  playback->controllable = 1;
322 
323  /* Play sound */
324  res = ast_control_streamfile_lang(chan, playback->media + strlen(SOUND_URI_SCHEME),
325  fwd, rev, stop, pause, restart, playback->skipms, playback->language,
326  &offsetms);
327  } else if (ast_begins_with(playback->media, RECORDING_URI_SCHEME)) {
328  /* Play recording */
329  RAII_VAR(struct stasis_app_stored_recording *, recording, NULL,
330  ao2_cleanup);
331  const char *relname =
332  playback->media + strlen(RECORDING_URI_SCHEME);
333  recording = stasis_app_stored_recording_find_by_name(relname);
334 
335  if (!recording) {
336  ast_log(LOG_ERROR, "Attempted to play recording '%s' on channel '%s' but recording does not exist",
337  relname, ast_channel_name(chan));
338  continue;
339  }
340 
341  playback->controllable = 1;
342 
343  res = ast_control_streamfile_lang(chan,
344  stasis_app_stored_recording_get_file(recording), fwd, rev, stop, pause,
345  restart, playback->skipms, playback->language, &offsetms);
346  } else if (ast_begins_with(playback->media, NUMBER_URI_SCHEME)) {
347  int number;
348 
349  if (sscanf(playback->media + strlen(NUMBER_URI_SCHEME), "%30d", &number) != 1) {
350  ast_log(LOG_ERROR, "Attempted to play number '%s' on channel '%s' but number is invalid",
351  playback->media + strlen(NUMBER_URI_SCHEME), ast_channel_name(chan));
352  continue;
353  }
354 
355  res = ast_say_number(chan, number, stop, playback->language, NULL);
356  } else if (ast_begins_with(playback->media, DIGITS_URI_SCHEME)) {
357  res = ast_say_digit_str(chan, playback->media + strlen(DIGITS_URI_SCHEME),
358  stop, playback->language);
359  } else if (ast_begins_with(playback->media, CHARACTERS_URI_SCHEME)) {
360  res = ast_say_character_str(chan, playback->media + strlen(CHARACTERS_URI_SCHEME),
361  stop, playback->language, AST_SAY_CASE_NONE);
362  } else if (ast_begins_with(playback->media, TONE_URI_SCHEME)) {
363  playback->controllable = 1;
364  res = ast_control_tone(chan, playback->media + strlen(TONE_URI_SCHEME));
365  } else {
366  /* Play URL */
367  ast_log(LOG_ERROR, "Attempted to play URI '%s' on channel '%s' but scheme is unsupported\n",
368  playback->media, ast_channel_name(chan));
369  continue;
370  }
371 
372  playback_final_update(playback, offsetms, res,
373  ast_channel_uniqueid(chan));
374  if (res == AST_CONTROL_STREAM_STOP) {
375  break;
376  }
377 
378  /* Reset offset for any subsequent media */
379  offsetms = 0;
380  }
381  return;
382 }
383 
384 /*!
385  * \brief Special case code to play while a channel is in a bridge.
386  *
387  * \param bridge_channel The channel's bridge_channel.
388  * \param playback_id Id of the playback to start.
389  */
390 static void play_on_channel_in_bridge(struct ast_bridge_channel *bridge_channel,
391  const char *playback_id)
392 {
393  RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
394 
395  playback = stasis_app_playback_find_by_id(playback_id);
396  if (!playback) {
397  ast_log(LOG_ERROR, "Couldn't find playback %s\n",
398  playback_id);
399  return;
400  }
401 
402  play_on_channel(playback, bridge_channel->chan);
403 }
404 
405 /*!
406  * \brief \ref RAII_VAR function to remove a playback from the global list when
407  * leaving scope.
408  */
409 static void remove_from_playbacks(void *data)
410 {
411  struct stasis_app_playback *playback = data;
412 
413  ao2_unlink_flags(playbacks, playback,
415  ao2_ref(playback, -1);
416 }
417 
418 static int play_uri(struct stasis_app_control *control,
419  struct ast_channel *chan, void *data)
420 {
421  struct stasis_app_playback *playback = data;
422  struct ast_bridge *bridge;
423 
424  if (!control) {
425  return -1;
426  }
427 
428  bridge = stasis_app_get_bridge(control);
429  if (bridge) {
430  struct ast_bridge_channel *bridge_chan;
431 
432  /* Queue up playback on the bridge */
433  ast_bridge_lock(bridge);
434  bridge_chan = ao2_bump(bridge_find_channel(bridge, chan));
435  ast_bridge_unlock(bridge);
436  if (bridge_chan) {
438  bridge_chan,
440  playback->id,
441  NULL); /* moh_class */
442  }
443  ao2_cleanup(bridge_chan);
444  } else {
445  play_on_channel(playback, chan);
446  }
447 
448  return 0;
449 }
450 
451 static void set_target_uri(
452  struct stasis_app_playback *playback,
453  enum stasis_app_playback_target_type target_type,
454  const char *target_id)
455 {
456  const char *type = NULL;
457  switch (target_type) {
459  type = "channel";
460  break;
462  type = "bridge";
463  break;
464  }
465 
466  ast_assert(type != NULL);
467 
468  ast_string_field_build(playback, target, "%s:%s", type, target_id);
469 }
470 
472  struct stasis_app_control *control, const char **media,
473  size_t media_count, const char *language, const char *target_id,
474  enum stasis_app_playback_target_type target_type,
475  int skipms, long offsetms, const char *id)
476 {
477  struct stasis_app_playback *playback;
478  size_t i;
479 
480  if (skipms < 0 || offsetms < 0 || media_count == 0) {
481  return NULL;
482  }
483 
484  playback = playback_create(control, id);
485  if (!playback) {
486  return NULL;
487  }
488 
489  for (i = 0; i < media_count; i++) {
490  char *media_uri;
491 
492  if (ast_strlen_zero(media[i])) {
493  ast_log(LOG_ERROR, "Attempted to play media on channel '%s' but no media URI was provided.\n",
495  ao2_ref(playback, -1);
496  return NULL;
497  }
498 
499  media_uri = ast_malloc(strlen(media[i]) + 1);
500  if (!media_uri) {
501  ao2_ref(playback, -1);
502  return NULL;
503  }
504 
505  ast_debug(3, "%s: Sending play(%s) command\n",
506  stasis_app_control_get_channel_id(control), media[i]);
507 
508  /* safe */
509  strcpy(media_uri, media[i]);
510  if (AST_VECTOR_APPEND(&playback->medias, media_uri)) {
511  ao2_ref(playback, -1);
512  ast_free(media_uri);
513  return NULL;
514  }
515  }
516 
517  if (skipms == 0) {
518  skipms = PLAYBACK_DEFAULT_SKIPMS;
519  }
520 
521  ast_string_field_set(playback, media, AST_VECTOR_GET(&playback->medias, 0));
522  ast_string_field_set(playback, language, language);
523  set_target_uri(playback, target_type, target_id);
524  playback->skipms = skipms;
525  playback->offsetms = offsetms;
526  ao2_link(playbacks, playback);
527 
528  playback->state = STASIS_PLAYBACK_STATE_QUEUED;
529  playback_publish(playback);
530 
532 
533  return playback;
534 }
535 
537  struct stasis_app_playback *control)
538 {
539  SCOPED_AO2LOCK(lock, control);
540  return control->state;
541 }
542 
544  struct stasis_app_playback *control)
545 {
546  /* id is immutable; no lock needed */
547  return control->id;
548 }
549 
551 {
552  return ao2_find(playbacks, id, OBJ_KEY);
553 }
554 
556  const struct stasis_app_playback *playback)
557 {
558  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
559 
560  if (playback == NULL) {
561  return NULL;
562  }
563 
564  if (playback->media_index == AST_VECTOR_SIZE(&playback->medias) - 1) {
565  json = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
566  "id", playback->id,
567  "media_uri", playback->media,
568  "target_uri", playback->target,
569  "language", playback->language,
570  "state", state_to_string(playback->state));
571  } else {
572  json = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s}",
573  "id", playback->id,
574  "media_uri", playback->media,
575  "next_media_uri", AST_VECTOR_GET(&playback->medias, playback->media_index + 1),
576  "target_uri", playback->target,
577  "language", playback->language,
578  "state", state_to_string(playback->state));
579  }
580 
581  return ast_json_ref(json);
582 }
583 
584 typedef int (*playback_opreation_cb)(struct stasis_app_playback *playback);
585 
586 static int playback_noop(struct stasis_app_playback *playback)
587 {
588  return 0;
589 }
590 
591 static int playback_cancel(struct stasis_app_playback *playback)
592 {
593  SCOPED_AO2LOCK(lock, playback);
594  playback->state = STASIS_PLAYBACK_STATE_CANCELED;
595  return 0;
596 }
597 
598 static int playback_stop(struct stasis_app_playback *playback)
599 {
600  SCOPED_AO2LOCK(lock, playback);
601 
602  if (!playback->controllable) {
603  return -1;
604  }
605 
606  playback->state = STASIS_PLAYBACK_STATE_STOPPED;
607  return stasis_app_control_queue_control(playback->control,
609 }
610 
611 static int playback_restart(struct stasis_app_playback *playback)
612 {
613  SCOPED_AO2LOCK(lock, playback);
614 
615  if (!playback->controllable) {
616  return -1;
617  }
618 
619  return stasis_app_control_queue_control(playback->control,
621 }
622 
623 static int playback_pause(struct stasis_app_playback *playback)
624 {
625  SCOPED_AO2LOCK(lock, playback);
626 
627  if (!playback->controllable) {
628  return -1;
629  }
630 
631  playback->state = STASIS_PLAYBACK_STATE_PAUSED;
632  playback_publish(playback);
633 
634  return stasis_app_control_queue_control(playback->control,
636 }
637 
638 static int playback_unpause(struct stasis_app_playback *playback)
639 {
640  SCOPED_AO2LOCK(lock, playback);
641 
642  if (!playback->controllable) {
643  return -1;
644  }
645 
646  playback->state = STASIS_PLAYBACK_STATE_PLAYING;
647  playback_publish(playback);
648 
649  return stasis_app_control_queue_control(playback->control,
651 }
652 
653 static int playback_reverse(struct stasis_app_playback *playback)
654 {
655  SCOPED_AO2LOCK(lock, playback);
656 
657  if (!playback->controllable) {
658  return -1;
659  }
660 
661  return stasis_app_control_queue_control(playback->control,
663 }
664 
665 static int playback_forward(struct stasis_app_playback *playback)
666 {
667  SCOPED_AO2LOCK(lock, playback);
668 
669  if (!playback->controllable) {
670  return -1;
671  }
672 
673  return stasis_app_control_queue_control(playback->control,
675 }
676 
677 /*!
678  * \brief A sparse array detailing how commands should be handled in the
679  * various playback states. Unset entries imply invalid operations.
680  */
684 
691 
698 
702 
706 };
707 
709  struct stasis_app_playback *playback,
711 {
713  SCOPED_AO2LOCK(lock, playback);
714 
715  ast_assert((unsigned int)playback->state < STASIS_PLAYBACK_STATE_MAX);
716 
717  if (operation >= STASIS_PLAYBACK_MEDIA_OP_MAX) {
718  ast_log(LOG_ERROR, "Invalid playback operation %u\n", operation);
719  return -1;
720  }
721 
722  cb = operations[playback->state][operation];
723 
724  if (!cb) {
725  if (playback->state != STASIS_PLAYBACK_STATE_PLAYING) {
726  /* So we can be specific in our error message. */
728  } else {
729  /* And, really, all operations should be valid during
730  * playback */
732  "Unhandled operation during playback: %u\n",
733  operation);
735  }
736  }
737 
738  return cb(playback) ?
740 }
741 
742 static int load_module(void)
743 {
744  int r;
745 
747  if (r != 0) {
749  }
750 
753  if (!playbacks) {
756  }
758 }
759 
760 static int unload_module(void)
761 {
762  ao2_cleanup(playbacks);
763  playbacks = NULL;
765  return 0;
766 }
767 
768 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis application playback support",
769  .support_level = AST_MODULE_SUPPORT_CORE,
770  .load = load_module,
771  .unload = unload_module,
772  .requires = "res_stasis,res_stasis_recording"
773 );
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
enum stasis_playback_oper_results stasis_app_playback_operation(struct stasis_app_playback *playback, enum stasis_app_playback_media_operation operation)
Controls the media for a given playback operation.
static const char type[]
Definition: chan_ooh323.c:109
struct ast_bridge * stasis_app_get_bridge(struct stasis_app_control *control)
Gets the bridge currently associated with a control object.
Definition: control.c:931
enum sip_cc_notify_state state
Definition: chan_sip.c:957
Tone Indication Support.
void stasis_app_control_publish(struct stasis_app_control *control, struct stasis_message *message)
Publish a message to the control&#39;s channel&#39;s topic.
Definition: control.c:1436
Stasis Application Recording API. See StasisApplication API" for detailed documentation.
Main Channel structure associated with a channel.
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
Asterisk main include file. File version handling, generic pbx functions.
static void set_target_uri(struct stasis_app_playback *playback, enum stasis_app_playback_target_type target_type, const char *target_id)
static int playback_restart(struct stasis_app_playback *playback)
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:591
stasis_app_playback_target_type
#define AST_UUID_STR_LEN
Definition: uuid.h:27
static int playback_cmp(void *obj, void *arg, int flags)
stasis_app_playback_media_operation
int ast_control_streamfile_lang(struct ast_channel *chan, const char *file, const char *fwd, const char *rev, const char *stop, const char *suspend, const char *restart, int skipms, const char *lang, long *offsetms)
Version of ast_control_streamfile() which allows the language of the media file to be specified...
Definition: main/app.c:1250
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4291
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
#define OBJ_KEY
Definition: astobj2.h:1155
struct ast_json * blob
static int playback_cancel(struct stasis_app_playback *playback)
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1501
static void play_on_channel_in_bridge(struct ast_bridge_channel *bridge_channel, const char *playback_id)
Special case code to play while a channel is in a bridge.
#define OBJ_POINTER
Definition: astobj2.h:1154
#define LOG_WARNING
Definition: logger.h:274
static struct ao2_container * playbacks
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
static void playback_dtor(void *obj)
Universally unique identifier support.
static int playback_reverse(struct stasis_app_playback *playback)
const char * stasis_app_stored_recording_get_file(struct stasis_app_stored_recording *recording)
Returns the filename for this recording, for use with streamfile.
Definition: stored.c:53
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition: stasis.h:1523
int ast_say_digit_str(struct ast_channel *chan, const char *num, const char *ints, const char *lang)
says digits of a string
Definition: channel.c:8254
int ast_control_tone(struct ast_channel *chan, const char *tone)
Controls playback of a tone.
Definition: main/app.c:1322
ast_channel_state
ast_channel states
Definition: channelstate.h:35
unsigned int stop
Definition: app_meetme.c:1096
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:337
#define ast_assert(a)
Definition: utils.h:650
enum stasis_app_playback_state stasis_app_playback_get_state(struct stasis_app_playback *control)
Gets the current state of a playback operation.
STASIS_MESSAGE_TYPE_DEFN(stasis_app_playback_snapshot_type,.to_json=playback_to_json,)
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
struct ast_bridge_channel * bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan)
Definition: bridge.c:1469
#define PLAYBACK_BUCKETS
#define NULL
Definition: resample.c:96
#define PLAYBACK_DEFAULT_SKIPMS
Structure containing callbacks for Stasis message sanitization.
Definition: stasis.h:200
const char * stasis_app_playback_get_id(struct stasis_app_playback *control)
Gets the unique id of a playback object.
const ast_string_field target
const ast_string_field id
Blob of data associated with a channel.
Number structure.
Definition: app_followme.c:154
#define ao2_bump(obj)
Definition: astobj2.h:491
static int playback_first_update(struct stasis_app_playback *playback, const char *uniqueid)
const ast_string_field media
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
#define ast_log
Definition: astobj2.c:42
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
static const char * state_to_string(enum stasis_app_playback_state state)
Asterisk file paths, configured in asterisk.conf.
#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:851
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
struct stasis_app_playback * stasis_app_playback_find_by_id(const char *id)
Finds the playback object with the given id.
#define DIGITS_URI_SCHEME
ast_mutex_t lock
Definition: app_meetme.c:1091
const struct timeval * stasis_message_timestamp(const struct stasis_message *msg)
Get the time when a message was created.
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:299
#define ao2_ref(o, delta)
Definition: astobj2.h:464
static int playback_unpause(struct stasis_app_playback *playback)
struct stasis_message_type * stasis_app_playback_snapshot_type(void)
Message type for playback updates. The data is an ast_channel_blob.
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
int stasis_app_send_command_async(struct stasis_app_control *control, stasis_app_command_cb command, void *data, command_data_destructor_fn data_destructor)
Asynchronous version of stasis_app_send_command().
Definition: control.c:904
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
int ast_say_character_str(struct ast_channel *chan, const char *num, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity)
function to pronounce character and phonetic strings
Definition: channel.c:8260
struct ast_json * ast_json_timeval(const struct timeval tv, const char *zone)
Construct a timeval as JSON.
Definition: json.c:649
#define CHARACTERS_URI_SCHEME
playback_opreation_cb operations[STASIS_PLAYBACK_STATE_MAX][STASIS_PLAYBACK_MEDIA_OP_MAX]
A sparse array detailing how commands should be handled in the various playback states. Unset entries imply invalid operations.
static struct ast_json * playback_to_json(struct stasis_message *message, const struct stasis_message_sanitizer *sanitize)
const char * ast_channel_uniqueid(const struct ast_channel *chan)
stasis_playback_oper_results
static int skipms
Structure that contains information about a bridge.
Definition: bridge.h:357
static void remove_from_playbacks(void *data)
RAII_VAR function to remove a playback from the global list when leaving scope.
Backend API for implementing components of res_stasis.
#define LOG_ERROR
Definition: logger.h:285
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
const char * stasis_app_control_get_channel_id(const struct stasis_app_control *control)
Returns the uniqueid of the channel associated with this control.
Definition: control.c:1430
static void play_on_channel(struct stasis_app_playback *playback, struct ast_channel *chan)
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct stasis_app_playback * stasis_app_control_play_uri(struct stasis_app_control *control, const char **media, size_t media_count, const char *language, const char *target_id, enum stasis_app_playback_target_type target_type, int skipms, long offsetms, const char *id)
Play a file to the control&#39;s channel.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
#define LOG_NOTICE
Definition: logger.h:263
int(* playback_opreation_cb)(struct stasis_app_playback *playback)
#define NUMBER_URI_SCHEME
#define ast_strlen_zero(a)
Definition: muted.c:73
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition: bridge.h:493
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:602
#define ast_free(a)
Definition: astmm.h:182
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition: uuid.c:143
static int play_uri(struct stasis_app_control *control, struct ast_channel *chan, void *data)
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static int playback_pause(struct stasis_app_playback *playback)
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
static int load_module(void)
static int unload_module(void)
static int playback_stop(struct stasis_app_playback *playback)
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:550
static struct stasis_app_playback * playback_create(struct stasis_app_control *control, const char *id)
static int playback_forward(struct stasis_app_playback *playback)
Support for logging to various files, console and syslog Configuration in file logger.conf.
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
struct stasis_message * ast_channel_blob_create_from_cache(const char *uniqueid, struct stasis_message_type *type, struct ast_json *blob)
Create a ast_channel_blob message, pulling channel state from the cache.
#define RECORDING_URI_SCHEME
#define ast_bridge_lock(bridge)
Lock the bridge.
Definition: bridge.h:480
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
struct ast_channel * chan
Structure that contains information regarding a channel in a bridge.
Stasis Application Playback API. See StasisApplication API" for detailed documentation.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Definition: channel.c:8236
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
#define TONE_URI_SCHEME
const char * ast_channel_name(const struct ast_channel *chan)
struct stasis_app_stored_recording * stasis_app_stored_recording_find_by_name(const char *name)
Creates a stored recording object, with the given name.
Definition: stored.c:310
int stasis_app_control_queue_control(struct stasis_app_control *control, enum ast_control_frame_type frame_type)
Queue a control frame without payload.
Definition: control.c:1445
stasis_app_playback_state
#define ao2_unlink_flags(container, obj, flags)
Definition: astobj2.h:1622
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Definition: strings.h:94
static void playback_publish(struct stasis_app_playback *playback)
Abstract JSON element (object, array, string, int, ...).
#define SOUND_URI_SCHEME
Private Bridging API.
static void playback_final_update(struct stasis_app_playback *playback, long playedms, int res, const char *uniqueid)
Generic container type.
int ast_bridge_channel_queue_playfile_sync(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
Synchronously queue a bridge action play file frame onto the bridge channel.
const ast_string_field language
static int playback_noop(struct stasis_app_playback *playback)
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Bridging API.
Asterisk module definitions.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct ast_json * stasis_app_playback_to_json(const struct stasis_app_playback *playback)
Convert a playback to its JSON representation.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1206
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514
static int playback_hash(const void *obj, int flags)
#define ao2_link(container, obj)
Definition: astobj2.h:1549