Asterisk - The Open Source Telephony Project  21.4.1
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"
45 #include "asterisk/stasis_channels.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 */
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 
95 static struct ast_json *playback_to_json(struct stasis_message *message,
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 if (!strcmp(state, "failed")) {
111  type = "PlaybackFinished";
112  } else {
113  return NULL;
114  }
115 
116  return ast_json_pack("{s: s, s: o?, s: O}",
117  "type", type,
118  "timestamp", ast_json_timeval(*stasis_message_timestamp(message), NULL),
119  "playback", blob);
120 }
121 
123  .to_json = playback_to_json,
124 );
125 
126 static void playback_dtor(void *obj)
127 {
128  struct stasis_app_playback *playback = obj;
129  int i;
130 
131  for (i = 0; i < AST_VECTOR_SIZE(&playback->medias); i++) {
132  char *media = AST_VECTOR_GET(&playback->medias, i);
133 
134  ast_free(media);
135  }
136  AST_VECTOR_FREE(&playback->medias);
137 
138  ao2_cleanup(playback->control);
140 }
141 
142 static struct stasis_app_playback *playback_create(
143  struct stasis_app_control *control, const char *id)
144 {
145  RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
146  char uuid[AST_UUID_STR_LEN];
147 
148  if (!control) {
149  return NULL;
150  }
151 
152  playback = ao2_alloc(sizeof(*playback), playback_dtor);
153  if (!playback || ast_string_field_init(playback, 128)) {
154  return NULL;
155  }
156 
157  if (AST_VECTOR_INIT(&playback->medias, 8)) {
158  ao2_ref(playback, -1);
159  return NULL;
160  }
161 
162  if (!ast_strlen_zero(id)) {
163  ast_string_field_set(playback, id, id);
164  } else {
165  ast_uuid_generate_str(uuid, sizeof(uuid));
166  ast_string_field_set(playback, id, uuid);
167  }
168 
169  ao2_ref(control, +1);
170  playback->control = control;
171 
172  ao2_ref(playback, +1);
173  return playback;
174 }
175 
176 static int playback_hash(const void *obj, int flags)
177 {
178  const struct stasis_app_playback *playback = obj;
179  const char *id = flags & OBJ_KEY ? obj : playback->id;
180  return ast_str_hash(id);
181 }
182 
183 static int playback_cmp(void *obj, void *arg, int flags)
184 {
185  struct stasis_app_playback *lhs = obj;
186  struct stasis_app_playback *rhs = arg;
187  const char *rhs_id = flags & OBJ_KEY ? arg : rhs->id;
188 
189  if (strcmp(lhs->id, rhs_id) == 0) {
190  return CMP_MATCH | CMP_STOP;
191  } else {
192  return 0;
193  }
194 }
195 
196 static const char *state_to_string(enum stasis_app_playback_state state)
197 {
198  switch (state) {
200  return "queued";
202  return "playing";
204  return "paused";
206  return "continuing";
208  return "failed";
212  /* It doesn't really matter how we got here, but all of these
213  * states really just mean 'done' */
214  return "done";
216  break;
217  }
218 
219  return "?";
220 }
221 
222 static void playback_publish(struct stasis_app_playback *playback)
223 {
224  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
225  RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
226 
227  ast_assert(playback != NULL);
228 
229  json = stasis_app_playback_to_json(playback);
230  if (json == NULL) {
231  return;
232  }
233 
237  if (message == NULL) {
238  return;
239  }
240 
242 }
243 
244 static int playback_first_update(struct stasis_app_playback *playback,
245  const char *uniqueid)
246 {
247  int res;
248  SCOPED_AO2LOCK(lock, playback);
249 
250  if (playback->state == STASIS_PLAYBACK_STATE_CANCELED) {
251  ast_log(LOG_NOTICE, "%s: Playback canceled for %s\n",
252  uniqueid, playback->media);
253  res = -1;
254  } else {
255  res = 0;
257  }
258 
259  playback_publish(playback);
260  return res;
261 }
262 
263 static void playback_final_update(struct stasis_app_playback *playback,
264  long playedms, int res, int hangup, const char *uniqueid)
265 {
266  SCOPED_AO2LOCK(lock, playback);
267 
268  playback->playedms = playedms;
269  if (res == 0) {
270  if (playback->media_index == AST_VECTOR_SIZE(&playback->medias) - 1 || hangup ) {
272  } else {
274  }
275  } else {
276  if (playback->state == STASIS_PLAYBACK_STATE_STOPPED) {
277  ast_log(LOG_NOTICE, "%s: Playback stopped for %s\n",
278  uniqueid, playback->media);
279  } else {
280  ast_log(LOG_WARNING, "%s: Playback failed for %s\n",
281  uniqueid, playback->media);
282  if (playback->media_index == AST_VECTOR_SIZE(&playback->medias) - 1 || hangup ) {
284  } else {
286  }
287  }
288  }
289 
290  playback_publish(playback);
291 }
292 
293 static void play_on_channel(struct stasis_app_playback *playback,
294  struct ast_channel *chan)
295 {
296  int res;
297  int hangup;
298  long offsetms;
299  size_t index;
300 
301  /* Even though these local variables look fairly pointless, they avoid
302  * having a bunch of NULL's passed directly into
303  * ast_control_streamfile() */
304  const char *fwd = NULL;
305  const char *rev = NULL;
306  const char *stop = NULL;
307  const char *pause = NULL;
308  const char *restart = NULL;
309 
310  ast_assert(playback != NULL);
311 
312  if (ast_channel_state(chan) != AST_STATE_UP) {
314  }
315 
316  offsetms = playback->offsetms;
317 
318  for (index = 0; index < AST_VECTOR_SIZE(&playback->medias); index++) {
319  playback->media_index = index;
320 
321  /* Set the current media to play */
322  ast_string_field_set(playback, media, AST_VECTOR_GET(&playback->medias, playback->media_index));
323 
324  res = playback_first_update(playback, ast_channel_uniqueid(chan));
325  if (res != 0) {
326  return;
327  }
328 
329  if (ast_begins_with(playback->media, SOUND_URI_SCHEME)) {
330  playback->controllable = 1;
331 
332  /* Play sound */
333  res = ast_control_streamfile_lang(chan, playback->media + strlen(SOUND_URI_SCHEME),
334  fwd, rev, stop, pause, restart, playback->skipms, playback->language,
335  &offsetms);
336  } else if (ast_begins_with(playback->media, RECORDING_URI_SCHEME)) {
337  /* Play recording */
338  RAII_VAR(struct stasis_app_stored_recording *, recording, NULL,
339  ao2_cleanup);
340  const char *relname =
341  playback->media + strlen(RECORDING_URI_SCHEME);
342  recording = stasis_app_stored_recording_find_by_name(relname);
343 
344  if (!recording) {
345  ast_log(LOG_ERROR, "Attempted to play recording '%s' on channel '%s' but recording does not exist",
346  relname, ast_channel_name(chan));
347  continue;
348  }
349 
350  playback->controllable = 1;
351 
352  res = ast_control_streamfile_lang(chan,
353  stasis_app_stored_recording_get_file(recording), fwd, rev, stop, pause,
354  restart, playback->skipms, playback->language, &offsetms);
355  } else if (ast_begins_with(playback->media, NUMBER_URI_SCHEME)) {
356  int number;
357 
358  if (sscanf(playback->media + strlen(NUMBER_URI_SCHEME), "%30d", &number) != 1) {
359  ast_log(LOG_ERROR, "Attempted to play number '%s' on channel '%s' but number is invalid",
360  playback->media + strlen(NUMBER_URI_SCHEME), ast_channel_name(chan));
361  continue;
362  }
363 
364  res = ast_say_number(chan, number, stop, playback->language, NULL);
365  } else if (ast_begins_with(playback->media, DIGITS_URI_SCHEME)) {
366  res = ast_say_digit_str(chan, playback->media + strlen(DIGITS_URI_SCHEME),
367  stop, playback->language);
368  } else if (ast_begins_with(playback->media, CHARACTERS_URI_SCHEME)) {
369  res = ast_say_character_str(chan, playback->media + strlen(CHARACTERS_URI_SCHEME),
370  stop, playback->language, AST_SAY_CASE_NONE);
371  } else if (ast_begins_with(playback->media, TONE_URI_SCHEME)) {
372  playback->controllable = 1;
373  res = ast_control_tone(chan, playback->media + strlen(TONE_URI_SCHEME));
374  } else {
375  /* Play URL */
376  ast_log(LOG_ERROR, "Attempted to play URI '%s' on channel '%s' but scheme is unsupported\n",
377  playback->media, ast_channel_name(chan));
378  continue;
379  }
380 
381  hangup = ast_check_hangup(chan);
382 
383  playback_final_update(playback, offsetms, res, hangup,
384  ast_channel_uniqueid(chan));
385 
386  if (hangup) {
387  ast_log(LOG_DEBUG, "Channel: %s already hangup, stop playback\n", ast_channel_name(chan));
388  break;
389  }
390 
391  if (res == AST_CONTROL_STREAM_STOP) {
392  break;
393  }
394 
395  /* Reset offset for any subsequent media */
396  offsetms = 0;
397  }
398  return;
399 }
400 
401 /*!
402  * \brief Special case code to play while a channel is in a bridge.
403  *
404  * \param bridge_channel The channel's bridge_channel.
405  * \param playback_id Id of the playback to start.
406  */
407 static void play_on_channel_in_bridge(struct ast_bridge_channel *bridge_channel,
408  const char *playback_id)
409 {
410  RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
411 
412  playback = stasis_app_playback_find_by_id(playback_id);
413  if (!playback) {
414  ast_log(LOG_ERROR, "Couldn't find playback %s\n",
415  playback_id);
416  return;
417  }
418 
419  play_on_channel(playback, bridge_channel->chan);
420 }
421 
422 /*!
423  * \brief \ref RAII_VAR function to remove a playback from the global list when
424  * leaving scope.
425  */
426 static void remove_from_playbacks(void *data)
427 {
428  struct stasis_app_playback *playback = data;
429 
430  ao2_unlink_flags(playbacks, playback,
432  ao2_ref(playback, -1);
433 }
434 
435 static int play_uri(struct stasis_app_control *control,
436  struct ast_channel *chan, void *data)
437 {
438  struct stasis_app_playback *playback = data;
439  struct ast_bridge *bridge;
440 
441  if (!control) {
442  return -1;
443  }
444 
445  bridge = stasis_app_get_bridge(control);
446  if (bridge) {
447  struct ast_bridge_channel *bridge_chan;
448 
449  /* Queue up playback on the bridge */
450  ast_bridge_lock(bridge);
451  bridge_chan = ao2_bump(bridge_find_channel(bridge, chan));
452  ast_bridge_unlock(bridge);
453  if (bridge_chan) {
455  bridge_chan,
457  playback->id,
458  NULL); /* moh_class */
459  }
460  ao2_cleanup(bridge_chan);
461  } else {
462  play_on_channel(playback, chan);
463  }
464 
465  return 0;
466 }
467 
468 static void set_target_uri(
469  struct stasis_app_playback *playback,
470  enum stasis_app_playback_target_type target_type,
471  const char *target_id)
472 {
473  const char *type = NULL;
474  switch (target_type) {
476  type = "channel";
477  break;
479  type = "bridge";
480  break;
481  }
482 
483  ast_assert(type != NULL);
484 
485  ast_string_field_build(playback, target, "%s:%s", type, target_id);
486 }
487 
489  struct stasis_app_control *control, const char **media,
490  size_t media_count, const char *language, const char *target_id,
491  enum stasis_app_playback_target_type target_type,
492  int skipms, long offsetms, const char *id)
493 {
494  struct stasis_app_playback *playback;
495  size_t i;
496 
497  if (skipms < 0 || offsetms < 0 || media_count == 0) {
498  return NULL;
499  }
500 
501  playback = playback_create(control, id);
502  if (!playback) {
503  return NULL;
504  }
505 
506  for (i = 0; i < media_count; i++) {
507  char *media_uri;
508 
509  if (ast_strlen_zero(media[i])) {
510  ast_log(LOG_ERROR, "Attempted to play media on channel '%s' but no media URI was provided.\n",
512  ao2_ref(playback, -1);
513  return NULL;
514  }
515 
516  media_uri = ast_malloc(strlen(media[i]) + 1);
517  if (!media_uri) {
518  ao2_ref(playback, -1);
519  return NULL;
520  }
521 
522  ast_debug(3, "%s: Sending play(%s) command\n",
523  stasis_app_control_get_channel_id(control), media[i]);
524 
525  /* safe */
526  strcpy(media_uri, media[i]);
527  if (AST_VECTOR_APPEND(&playback->medias, media_uri)) {
528  ao2_ref(playback, -1);
529  ast_free(media_uri);
530  return NULL;
531  }
532  }
533 
534  if (skipms == 0) {
535  skipms = PLAYBACK_DEFAULT_SKIPMS;
536  }
537 
538  ast_string_field_set(playback, media, AST_VECTOR_GET(&playback->medias, 0));
539  ast_string_field_set(playback, language, language);
540  set_target_uri(playback, target_type, target_id);
541  playback->skipms = skipms;
542  playback->offsetms = offsetms;
543  ao2_link(playbacks, playback);
544 
546  playback_publish(playback);
547 
548  stasis_app_send_command_async(control, play_uri, ao2_bump(playback), remove_from_playbacks);
549 
550  return playback;
551 }
552 
554  struct stasis_app_playback *control)
555 {
556  SCOPED_AO2LOCK(lock, control);
557  return control->state;
558 }
559 
561  struct stasis_app_playback *control)
562 {
563  /* id is immutable; no lock needed */
564  return control->id;
565 }
566 
568 {
569  return ao2_find(playbacks, id, OBJ_KEY);
570 }
571 
573  const struct stasis_app_playback *playback)
574 {
575  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
576 
577  if (playback == NULL) {
578  return NULL;
579  }
580 
581  if (playback->media_index == AST_VECTOR_SIZE(&playback->medias) - 1) {
582  json = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
583  "id", playback->id,
584  "media_uri", playback->media,
585  "target_uri", playback->target,
586  "language", playback->language,
587  "state", state_to_string(playback->state));
588  } else {
589  json = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s}",
590  "id", playback->id,
591  "media_uri", playback->media,
592  "next_media_uri", AST_VECTOR_GET(&playback->medias, playback->media_index + 1),
593  "target_uri", playback->target,
594  "language", playback->language,
595  "state", state_to_string(playback->state));
596  }
597 
598  return ast_json_ref(json);
599 }
600 
601 typedef int (*playback_operation_cb)(struct stasis_app_playback *playback);
602 
603 static int playback_noop(struct stasis_app_playback *playback)
604 {
605  return 0;
606 }
607 
608 static int playback_cancel(struct stasis_app_playback *playback)
609 {
610  SCOPED_AO2LOCK(lock, playback);
612  return 0;
613 }
614 
615 static int playback_stop(struct stasis_app_playback *playback)
616 {
617  SCOPED_AO2LOCK(lock, playback);
618 
619  if (!playback->controllable) {
620  return -1;
621  }
622 
624  return stasis_app_control_queue_control(playback->control,
626 }
627 
628 static int playback_restart(struct stasis_app_playback *playback)
629 {
630  SCOPED_AO2LOCK(lock, playback);
631 
632  if (!playback->controllable) {
633  return -1;
634  }
635 
636  return stasis_app_control_queue_control(playback->control,
638 }
639 
640 static int playback_pause(struct stasis_app_playback *playback)
641 {
642  SCOPED_AO2LOCK(lock, playback);
643 
644  if (!playback->controllable) {
645  return -1;
646  }
647 
649  playback_publish(playback);
650 
651  return stasis_app_control_queue_control(playback->control,
653 }
654 
655 static int playback_unpause(struct stasis_app_playback *playback)
656 {
657  SCOPED_AO2LOCK(lock, playback);
658 
659  if (!playback->controllable) {
660  return -1;
661  }
662 
664  playback_publish(playback);
665 
666  return stasis_app_control_queue_control(playback->control,
668 }
669 
670 static int playback_reverse(struct stasis_app_playback *playback)
671 {
672  SCOPED_AO2LOCK(lock, playback);
673 
674  if (!playback->controllable) {
675  return -1;
676  }
677 
678  return stasis_app_control_queue_control(playback->control,
680 }
681 
682 static int playback_forward(struct stasis_app_playback *playback)
683 {
684  SCOPED_AO2LOCK(lock, playback);
685 
686  if (!playback->controllable) {
687  return -1;
688  }
689 
690  return stasis_app_control_queue_control(playback->control,
692 }
693 
694 /*!
695  * \brief A sparse array detailing how commands should be handled in the
696  * various playback states. Unset entries imply invalid operations.
697  */
701 
708 
715 
719 
724 };
725 
726 enum stasis_playback_oper_results stasis_app_playback_operation(
727  struct stasis_app_playback *playback,
729 {
730  playback_operation_cb cb;
731  SCOPED_AO2LOCK(lock, playback);
732 
733  ast_assert((unsigned int)playback->state < STASIS_PLAYBACK_STATE_MAX);
734 
735  if (operation >= STASIS_PLAYBACK_MEDIA_OP_MAX) {
736  ast_log(LOG_ERROR, "Invalid playback operation %u\n", operation);
737  return -1;
738  }
739 
740  cb = operations[playback->state][operation];
741 
742  if (!cb) {
743  if (playback->state != STASIS_PLAYBACK_STATE_PLAYING) {
744  /* So we can be specific in our error message. */
745  return STASIS_PLAYBACK_OPER_NOT_PLAYING;
746  } else {
747  /* And, really, all operations should be valid during
748  * playback */
749  ast_log(LOG_ERROR,
750  "Unhandled operation during playback: %u\n",
751  operation);
752  return STASIS_PLAYBACK_OPER_FAILED;
753  }
754  }
755 
756  return cb(playback) ?
757  STASIS_PLAYBACK_OPER_FAILED : STASIS_PLAYBACK_OPER_OK;
758 }
759 
760 static int load_module(void)
761 {
762  int r;
763 
765  if (r != 0) {
767  }
768 
770  playback_hash, NULL, playback_cmp);
771  if (!playbacks) {
774  }
776 }
777 
778 static int unload_module(void)
779 {
780  ao2_cleanup(playbacks);
781  playbacks = NULL;
783  return 0;
784 }
785 
786 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis application playback support",
787  .support_level = AST_MODULE_SUPPORT_CORE,
788  .load = load_module,
789  .unload = unload_module,
790  .requires = "res_stasis,res_stasis_recording"
791 );
#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.
struct ast_bridge * stasis_app_get_bridge(struct stasis_app_control *control)
Gets the bridge currently associated with a control object.
Definition: control.c:953
Tone Indication Support.
void stasis_app_control_publish(struct stasis_app_control *control, struct stasis_message *message)
Publish a message to the control's channel's topic.
Definition: control.c:1458
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.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
stasis_app_playback_target_type
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:1473
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4277
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:1151
playback_operation_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.
struct ast_json * blob
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1493
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:1150
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
Universally unique identifier support.
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:1515
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:8259
int ast_control_tone(struct ast_channel *chan, const char *tone)
Controls playback of a tone.
Definition: main/app.c:1545
ast_channel_state
ast_channel states
Definition: channelstate.h:35
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
enum stasis_app_playback_state stasis_app_playback_get_state(struct stasis_app_playback *control)
Gets the current state of a playback operation.
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
#define PLAYBACK_BUCKETS
#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)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
struct stasis_app_control * control
const ast_string_field media
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
ast_mutex_t lock
Asterisk file paths, configured in asterisk.conf.
enum stasis_app_playback_state state
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
struct stasis_app_playback * stasis_app_playback_find_by_id(const char *id)
Finds the playback object with the given id.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
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:303
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:283
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
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:926
#define ast_debug(level,...)
Log a DEBUG message.
#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:8271
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
struct ast_json * ast_json_timeval(const struct timeval tv, const char *zone)
Construct a timeval as JSON.
Definition: json.c:670
#define STASIS_MESSAGE_TYPE_DEFN(name,...)
Boiler-plate messaging macro for defining public message types.
Definition: stasis.h:1440
Structure that contains information about a bridge.
Definition: bridge.h:349
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 ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
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:1452
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's channel.
unsigned int stop
Definition: res_smdi.c:217
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition: bridge.h:481
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:604
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition: uuid.c:141
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:555
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 ast_bridge_lock(bridge)
Lock the bridge.
Definition: bridge.h:470
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680
struct ast_channel * chan
struct stasis_app_playback::@487 medias
Structure that contains information regarding a channel in a bridge.
Stasis Application Playback API. See StasisApplication API" for detailed documentation.
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:8235
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
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:314
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:1467
stasis_app_playback_state
#define ao2_unlink_flags(container, obj, flags)
Remove an object from a container.
Definition: astobj2.h:1600
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
Abstract JSON element (object, array, string, int, ...).
Private Bridging API.
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
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 RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
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:609
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532