Asterisk - The Open Source Telephony Project  18.5.0
resource_bridges.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012 - 2013, Digium, Inc.
5  *
6  * David M. Lee, II <[email protected]>
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 Implementation for ARI stubs.
22  *
23  * \author David M. Lee, II <[email protected]>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include "resource_bridges.h"
33 #include "asterisk/stasis.h"
35 #include "asterisk/stasis_app.h"
40 #include "asterisk/core_unreal.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/bridge.h"
43 #include "asterisk/format_cap.h"
44 #include "asterisk/file.h"
45 #include "asterisk/musiconhold.h"
46 #include "asterisk/format_cache.h"
47 
48 /*!
49  * \brief Finds a bridge, filling the response with an error, if appropriate.
50  *
51  * \param[out] response Response to fill with an error if control is not found.
52  * \param bridge_id ID of the bridge to lookup.
53  *
54  * \return Bridget.
55  * \return \c NULL if bridge does not exist.
56  */
57 static struct ast_bridge *find_bridge(
58  struct ast_ari_response *response,
59  const char *bridge_id)
60 {
61  RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
62 
63  ast_assert(response != NULL);
64 
65  bridge = stasis_app_bridge_find_by_id(bridge_id);
66  if (bridge == NULL) {
67  RAII_VAR(struct ast_bridge_snapshot *, snapshot,
69  if (!snapshot) {
70  ast_ari_response_error(response, 404, "Not found",
71  "Bridge not found");
72  return NULL;
73  }
74 
75  ast_ari_response_error(response, 409, "Conflict",
76  "Bridge not in Stasis application");
77  return NULL;
78  }
79 
80  ao2_ref(bridge, +1);
81  return bridge;
82 }
83 
84 /*!
85  * \brief Finds the control object for a channel, filling the response with an
86  * error, if appropriate.
87  * \param[out] response Response to fill with an error if control is not found.
88  * \param channel_id ID of the channel to lookup.
89  * \return Channel control object.
90  * \return \c NULL if control object does not exist.
91  */
93  struct ast_ari_response *response,
94  const char *channel_id)
95 {
96  RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
97 
98  ast_assert(response != NULL);
99 
100  control = stasis_app_control_find_by_channel_id(channel_id);
101  if (control == NULL) {
102  /* Distinguish between 400 and 422 errors */
103  RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL,
104  ao2_cleanup);
105  snapshot = ast_channel_snapshot_get_latest(channel_id);
106  if (snapshot == NULL) {
107  ast_log(LOG_DEBUG, "Couldn't find '%s'\n", channel_id);
108  ast_ari_response_error(response, 400, "Bad Request",
109  "Channel not found");
110  return NULL;
111  }
112 
113  ast_log(LOG_DEBUG, "Found non-stasis '%s'\n", channel_id);
114  ast_ari_response_error(response, 422, "Unprocessable Entity",
115  "Channel not in Stasis application");
116  return NULL;
117  }
118 
119  ao2_ref(control, +1);
120  return control;
121 }
122 
123 struct control_list {
124  size_t count;
126 };
127 
128 static void control_list_dtor(void *obj) {
129  struct control_list *list = obj;
130  size_t i;
131 
132  for (i = 0; i < list->count; ++i) {
133  ao2_cleanup(list->controls[i]);
134  list->controls[i] = NULL;
135  }
136 }
137 
138 static struct control_list *control_list_create(struct ast_ari_response *response, size_t count, const char **channels) {
139  RAII_VAR(struct control_list *, list, NULL, ao2_cleanup);
140  size_t i;
141 
142  if (count == 0 || !channels) {
143  ast_ari_response_error(response, 400, "Bad Request", "Missing parameter channel");
144  return NULL;
145  }
146 
147  list = ao2_alloc(sizeof(*list) + count * sizeof(list->controls[0]), control_list_dtor);
148  if (!list) {
150  return NULL;
151  }
152 
153  for (i = 0; i < count; ++i) {
154  if (ast_strlen_zero(channels[i])) {
155  continue;
156  }
157  list->controls[list->count] =
158  find_channel_control(response, channels[i]);
159  if (!list->controls[list->count]) {
160  /* response filled in by find_channel_control() */
161  return NULL;
162  }
163  ++list->count;
164  }
165 
166  if (list->count == 0) {
167  ast_ari_response_error(response, 400, "Bad Request", "Missing parameter channel");
168  return NULL;
169  }
170 
171  ao2_ref(list, +1);
172  return list;
173 }
174 
175 static int check_add_remove_channel(struct ast_ari_response *response,
176  struct stasis_app_control *control,
178 {
179  switch (result) {
182  response, 409, "Conflict", "Channel %s currently recording",
184  return -1;
186  return 0;
187  }
188  return 0;
189 }
190 
193  struct ast_ari_response *response)
194 {
195  RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
196  RAII_VAR(struct control_list *, list, NULL, ao2_cleanup);
197  size_t i;
198  int has_error = 0;
199 
200  if (!bridge) {
201  /* Response filled in by find_bridge() */
202  return;
203  }
204 
205  list = control_list_create(response, args->channel_count, args->channel);
206  if (!list) {
207  /* Response filled in by control_list_create() */
208  return;
209  }
210 
211  for (i = 0; i < list->count; ++i) {
212  stasis_app_control_clear_roles(list->controls[i]);
213  if (!ast_strlen_zero(args->role)) {
214  if (stasis_app_control_add_role(list->controls[i], args->role)) {
216  return;
217  }
218  }
219 
220  /* Apply bridge features to each of the channel controls */
221  if (!stasis_app_control_bridge_features_init(list->controls[i])) {
222  stasis_app_control_absorb_dtmf_in_bridge(list->controls[i], args->absorb_dtmf);
223  stasis_app_control_mute_in_bridge(list->controls[i], args->mute);
225  }
226  }
227 
228  for (i = 0; i < list->count; ++i) {
229  if ((has_error = check_add_remove_channel(response, list->controls[i],
231  list->controls[i], bridge)))) {
232  break;
233  }
234  }
235 
236  if (!has_error) {
237  ast_ari_response_no_content(response);
238  }
239 }
240 
243  struct ast_ari_response *response)
244 {
245  RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
246  RAII_VAR(struct control_list *, list, NULL, ao2_cleanup);
247  size_t i;
248 
249  if (!bridge) {
250  /* Response filled in by find_bridge() */
251  return;
252  }
253 
254  list = control_list_create(response, args->channel_count, args->channel);
255  if (!list) {
256  /* Response filled in by control_list_create() */
257  return;
258  }
259 
260  /* Make sure all of the channels are in this bridge */
261  for (i = 0; i < list->count; ++i) {
262  if (stasis_app_get_bridge(list->controls[i]) != bridge) {
263  ast_log(LOG_WARNING, "Channel %s not in bridge %s\n",
264  args->channel[i], args->bridge_id);
265  ast_ari_response_error(response, 422,
266  "Unprocessable Entity",
267  "Channel not in this bridge");
268  return;
269  }
270  }
271 
272  /* Now actually remove it */
273  for (i = 0; i < list->count; ++i) {
275  bridge);
276  }
277 
278  ast_ari_response_no_content(response);
279 }
280 
285  char bridge_id[0];
286 };
287 
288 static void *bridge_channel_control_thread(void *data)
289 {
290  struct bridge_channel_control_thread_data *thread_data = data;
291  struct ast_channel *bridge_channel = thread_data->bridge_channel;
292  struct stasis_app_control *control = thread_data->control;
293  struct stasis_forward *forward = thread_data->forward;
294  ast_callid callid = ast_channel_callid(bridge_channel);
295  char *bridge_id = ast_strdupa(thread_data->bridge_id);
296 
297  if (callid) {
299  }
300 
301  ast_free(thread_data);
302  thread_data = NULL;
303 
304  stasis_app_control_execute_until_exhausted(bridge_channel, control);
306 
307  stasis_app_bridge_playback_channel_remove(bridge_id, control);
308  stasis_forward_cancel(forward);
309  ao2_cleanup(control);
310  ast_hangup(bridge_channel);
311  return NULL;
312 }
313 
314 static struct ast_channel *prepare_bridge_media_channel(const char *type)
315 {
316  RAII_VAR(struct ast_format_cap *, cap, NULL, ao2_cleanup);
317  struct ast_channel *chan;
318 
320  if (!cap) {
321  return NULL;
322  }
323 
325 
326  chan = ast_request(type, cap, NULL, NULL, "ARI", NULL);
327  if (!chan) {
328  return NULL;
329  }
330 
332  ast_channel_cleanup(chan);
333  return NULL;
334  }
335  return chan;
336 }
337 
338 /*!
339  * \brief Performs common setup for a bridge playback operation
340  * with both new controls and when existing controls are found.
341  *
342  * \param args_media medias to play
343  * \param args_media_count number of media items in \c media
344  * \param args_lang language string split from arguments
345  * \param args_offset_ms milliseconds offset split from arguments
346  * \param args_playback_id string to use for playback split from
347  * arguments (null valid)
348  * \param response ARI response being built
349  * \param bridge Bridge the playback is being peformed on
350  * \param control Control being used for the playback channel
351  * \param json contents of the response to ARI
352  * \param playback_url stores playback URL for use with response
353  *
354  * \retval -1 operation failed
355  * \retval operation was successful
356  */
357 static int ari_bridges_play_helper(const char **args_media,
358  size_t args_media_count,
359  const char *args_lang,
360  int args_offset_ms,
361  int args_skipms,
362  const char *args_playback_id,
363  struct ast_ari_response *response,
364  struct ast_bridge *bridge,
365  struct stasis_app_control *control,
366  struct ast_json **json,
367  char **playback_url)
368 {
369  RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
370  RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
371 
372  const char *language;
373 
374  snapshot = stasis_app_control_get_snapshot(control);
375  if (!snapshot) {
377  response, 500, "Internal Error", "Failed to get control snapshot");
378  return -1;
379  }
380 
381  language = S_OR(args_lang, snapshot->base->language);
382 
383  playback = stasis_app_control_play_uri(control, args_media, args_media_count,
384  language, bridge->uniqueid, STASIS_PLAYBACK_TARGET_BRIDGE, args_skipms,
385  args_offset_ms, args_playback_id);
386 
387  if (!playback) {
389  return -1;
390  }
391 
392  if (ast_asprintf(playback_url, "/playbacks/%s",
393  stasis_app_playback_get_id(playback)) == -1) {
395  return -1;
396  }
397 
398  *json = stasis_app_playback_to_json(playback);
399  if (!*json) {
401  return -1;
402  }
403 
404  return 0;
405 }
406 
407 static void ari_bridges_play_new(const char **args_media,
408  size_t args_media_count,
409  const char *args_lang,
410  int args_offset_ms,
411  int args_skipms,
412  const char *args_playback_id,
413  struct ast_ari_response *response,
414  struct ast_bridge *bridge)
415 {
416  RAII_VAR(struct ast_channel *, play_channel, NULL, ast_hangup);
417  RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
418  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
419  RAII_VAR(struct stasis_forward *, channel_forward, NULL, stasis_forward_cancel);
420  RAII_VAR(char *, playback_url, NULL, ast_free);
421 
422  struct stasis_topic *channel_topic;
423  struct stasis_topic *bridge_topic;
424  struct bridge_channel_control_thread_data *thread_data;
425  pthread_t threadid;
426 
427  if (!(play_channel = prepare_bridge_media_channel("Announcer"))) {
429  response, 500, "Internal Error", "Could not create playback channel");
430  return;
431  }
432  ast_debug(1, "Created announcer channel '%s'\n", ast_channel_name(play_channel));
433 
434  bridge_topic = ast_bridge_topic(bridge);
435  channel_topic = ast_channel_topic(play_channel);
436 
437  /* Forward messages from the playback channel topic to the bridge topic so that anything listening for
438  * messages on the bridge topic will receive the playback start/stop messages. Other messages that would
439  * go to this channel will be suppressed since the channel is marked as internal.
440  */
441  if (!bridge_topic || !channel_topic || !(channel_forward = stasis_forward_all(channel_topic, bridge_topic))) {
443  response, 500, "Internal Error", "Could not forward playback channel stasis messages to bridge topic");
444  return;
445  }
446 
447  if (ast_unreal_channel_push_to_bridge(play_channel, bridge,
450  response, 500, "Internal Error", "Failed to put playback channel into the bridge");
451  return;
452  }
453 
454  control = stasis_app_control_create(play_channel);
455  if (control == NULL) {
457  return;
458  }
459 
460  ao2_lock(control);
461  if (ari_bridges_play_helper(args_media, args_media_count, args_lang,
462  args_offset_ms, args_skipms, args_playback_id, response, bridge,
463  control, &json, &playback_url)) {
465  return;
466  }
468 
469  if (stasis_app_bridge_playback_channel_add(bridge, play_channel, control)) {
471  return;
472  }
473 
474  /* Give play_channel and control reference to the thread data */
475  thread_data = ast_malloc(sizeof(*thread_data) + strlen(bridge->uniqueid) + 1);
476  if (!thread_data) {
479  return;
480  }
481 
482  thread_data->bridge_channel = play_channel;
483  thread_data->control = control;
484  thread_data->forward = channel_forward;
485  /* Safe */
486  strcpy(thread_data->bridge_id, bridge->uniqueid);
487 
488  if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) {
491  ast_free(thread_data);
492  return;
493  }
494 
495  /* These are owned by the other thread now, so we don't want RAII_VAR disposing of them. */
496  play_channel = NULL;
497  control = NULL;
498  channel_forward = NULL;
499 
500  ast_ari_response_created(response, playback_url, ast_json_ref(json));
501 }
502 
507 };
508 
509 /*!
510  * \brief Performs common setup for a bridge playback operation
511  * with both new controls and when existing controls are found.
512  *
513  * \param args_media medias to play
514  * \param args_media_count number of media items in \c media
515  * \param args_lang language string split from arguments
516  * \param args_offset_ms milliseconds offset split from arguments
517  * \param args_playback_id string to use for playback split from
518  * arguments (null valid)
519  * \param response ARI response being built
520  * \param bridge Bridge the playback is being peformed on
521  * \param found_channel The channel that was found controlling playback
522  *
523  * \retval PLAY_FOUND_SUCCESS The operation was successful
524  * \retval PLAY_FOUND_FAILURE The operation failed (terminal failure)
525  * \retval PLAY_FOUND_CHANNEL_UNAVAILABLE The operation failed because
526  * the channel requested to playback with is breaking down.
527  */
528 static enum play_found_result ari_bridges_play_found(const char **args_media,
529  size_t args_media_count,
530  const char *args_lang,
531  int args_offset_ms,
532  int args_skipms,
533  const char *args_playback_id,
534  struct ast_ari_response *response,
535  struct ast_bridge *bridge,
536  struct ast_channel *found_channel)
537 {
538  RAII_VAR(struct ast_channel *, play_channel, found_channel, ao2_cleanup);
540  RAII_VAR(char *, playback_url, NULL, ast_free);
541  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
542 
544  if (!control) {
546  }
547 
548  ao2_lock(control);
550  /* We failed to queue the action. Bailout and return that we aren't terminal. */
553  }
554 
555  if (ari_bridges_play_helper(args_media, args_media_count,
556  args_lang, args_offset_ms, args_skipms, args_playback_id,
557  response, bridge, control, &json, &playback_url)) {
559  return PLAY_FOUND_FAILURE;
560  }
562 
563  ast_ari_response_created(response, playback_url, ast_json_ref(json));
564  return PLAY_FOUND_SUCCESS;
565 }
566 
568  const char *args_bridge_id,
569  const char **args_media,
570  size_t args_media_count,
571  const char *args_lang,
572  int args_offset_ms,
573  int args_skipms,
574  const char *args_playback_id,
575  struct ast_ari_response *response)
576 {
577  RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args_bridge_id), ao2_cleanup);
578  struct ast_channel *play_channel;
579 
580  ast_assert(response != NULL);
581 
582  if (!bridge) {
583  return;
584  }
585 
586  while ((play_channel = stasis_app_bridge_playback_channel_find(bridge))) {
587  /* If ari_bridges_play_found fails because the channel is unavailable for
588  * playback, The channel will be removed from the playback list soon. We
589  * can keep trying to get channels from the list until we either get one
590  * that will work or else there isn't a channel for this bridge anymore,
591  * in which case we'll revert to ari_bridges_play_new.
592  */
593  if (ari_bridges_play_found(args_media, args_media_count, args_lang,
594  args_offset_ms, args_skipms, args_playback_id, response,bridge,
595  play_channel) == PLAY_FOUND_CHANNEL_UNAVAILABLE) {
596  continue;
597  }
598  return;
599  }
600 
601  ari_bridges_play_new(args_media, args_media_count, args_lang, args_offset_ms,
602  args_skipms, args_playback_id, response, bridge);
603 }
604 
605 
606 void ast_ari_bridges_play(struct ast_variable *headers,
608  struct ast_ari_response *response)
609 {
611  args->media,
612  args->media_count,
613  args->lang,
614  args->offsetms,
615  args->skipms,
616  args->playback_id,
617  response);
618 }
619 
622  struct ast_ari_response *response)
623 {
625  args->media,
626  args->media_count,
627  args->lang,
628  args->offsetms,
629  args->skipms,
630  args->playback_id,
631  response);
632 }
633 
636  struct ast_ari_response *response)
637 {
638  RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
639  RAII_VAR(struct ast_channel *, record_channel, NULL, ast_hangup);
640  RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
641  RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
642  RAII_VAR(char *, recording_url, NULL, ast_free);
643  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
645  RAII_VAR(char *, uri_encoded_name, NULL, ast_free);
646  RAII_VAR(struct stasis_forward *, channel_forward, NULL, stasis_forward_cancel);
647 
648  struct stasis_topic *channel_topic;
649  struct stasis_topic *bridge_topic;
650  size_t uri_name_maxlen;
651  struct bridge_channel_control_thread_data *thread_data;
652  pthread_t threadid;
653 
654  ast_assert(response != NULL);
655 
656  if (bridge == NULL) {
657  return;
658  }
659 
660  if (!(record_channel = prepare_bridge_media_channel("Recorder"))) {
662  response, 500, "Internal Server Error", "Failed to create recording channel");
663  return;
664  }
665 
666  bridge_topic = ast_bridge_topic(bridge);
667  channel_topic = ast_channel_topic(record_channel);
668 
669  /* Forward messages from the recording channel topic to the bridge topic so that anything listening for
670  * messages on the bridge topic will receive the recording start/stop messages. Other messages that would
671  * go to this channel will be suppressed since the channel is marked as internal.
672  */
673  if (!bridge_topic || !channel_topic || !(channel_forward = stasis_forward_all(channel_topic, bridge_topic))) {
675  response, 500, "Internal Error", "Could not forward record channel stasis messages to bridge topic");
676  return;
677  }
678 
679  if (ast_unreal_channel_push_to_bridge(record_channel, bridge,
682  response, 500, "Internal Error", "Failed to put recording channel into the bridge");
683  return;
684  }
685 
686  control = stasis_app_control_create(record_channel);
687  if (control == NULL) {
689  return;
690  }
691 
693  if (options == NULL) {
695  return;
696  }
697 
698  ast_string_field_build(options, target, "bridge:%s", args->bridge_id);
699  options->max_silence_seconds = args->max_silence_seconds;
700  options->max_duration_seconds = args->max_duration_seconds;
701  options->terminate_on =
703  options->if_exists =
705  options->beep = args->beep;
706 
707  if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) {
709  response, 400, "Bad Request",
710  "terminateOn invalid");
711  return;
712  }
713 
714  if (options->if_exists == AST_RECORD_IF_EXISTS_ERROR) {
716  response, 400, "Bad Request",
717  "ifExists invalid");
718  return;
719  }
720 
721  if (!ast_get_format_for_file_ext(options->format)) {
723  response, 422, "Unprocessable Entity",
724  "specified format is unknown on this system");
725  return;
726  }
727 
729  if (recording == NULL) {
730  switch(errno) {
731  case EINVAL:
732  /* While the arguments are invalid, we should have
733  * caught them prior to calling record.
734  */
736  response, 500, "Internal Server Error",
737  "Error parsing request");
738  break;
739  case EEXIST:
740  ast_ari_response_error(response, 409, "Conflict",
741  "Recording '%s' already exists and can not be overwritten",
742  args->name);
743  break;
744  case ENOMEM:
746  break;
747  case EPERM:
749  response, 400, "Bad Request",
750  "Recording name invalid");
751  break;
752  default:
754  "Unrecognized recording error: %s\n",
755  strerror(errno));
757  response, 500, "Internal Server Error",
758  "Internal Server Error");
759  break;
760  }
761  return;
762  }
763 
764  uri_name_maxlen = strlen(args->name) * 3;
765  uri_encoded_name = ast_malloc(uri_name_maxlen);
766  if (!uri_encoded_name) {
768  return;
769  }
770  ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen, ast_uri_http);
771 
772  if (ast_asprintf(&recording_url, "/recordings/live/%s",
773  uri_encoded_name) == -1) {
774  recording_url = NULL;
776  return;
777  }
778 
779  json = stasis_app_recording_to_json(recording);
780  if (!json) {
782  return;
783  }
784 
785  thread_data = ast_calloc(1, sizeof(*thread_data));
786  if (!thread_data) {
788  return;
789  }
790 
791  thread_data->bridge_channel = record_channel;
792  thread_data->control = control;
793  thread_data->forward = channel_forward;
794 
795  if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) {
797  ast_free(thread_data);
798  return;
799  }
800 
801  /* These are owned by the other thread now, so we don't want RAII_VAR disposing of them. */
802  record_channel = NULL;
803  control = NULL;
804  channel_forward = NULL;
805 
806  ast_ari_response_created(response, recording_url, ast_json_ref(json));
807 }
808 
811  struct ast_ari_response *response)
812 {
813  RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
814  struct ast_channel *moh_channel;
815  const char *moh_class = args->moh_class;
816 
817  if (!bridge) {
818  /* The response is provided by find_bridge() */
819  return;
820  }
821 
822  moh_channel = stasis_app_bridge_moh_channel(bridge);
823  if (!moh_channel) {
825  return;
826  }
827 
828  ast_moh_start(moh_channel, moh_class, NULL);
829  ast_channel_cleanup(moh_channel);
830 
831  ast_ari_response_no_content(response);
832 
833 }
834 
837  struct ast_ari_response *response)
838 {
839  RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
840 
841  if (!bridge) {
842  /* the response is provided by find_bridge() */
843  return;
844  }
845 
846  if (stasis_app_bridge_moh_stop(bridge)) {
848  response, 409, "Conflict",
849  "Bridge isn't playing music");
850  return;
851  }
852 
853  ast_ari_response_no_content(response);
854 }
855 
856 void ast_ari_bridges_get(struct ast_variable *headers,
858  struct ast_ari_response *response)
859 {
861  if (!snapshot) {
863  response, 404, "Not Found",
864  "Bridge not found");
865  return;
866  }
867 
868  ast_ari_response_ok(response,
870 }
871 
874  struct ast_ari_response *response)
875 {
876  RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
877  if (!bridge) {
878  return;
879  }
880 
882  ast_ari_response_no_content(response);
883 }
884 
885 void ast_ari_bridges_list(struct ast_variable *headers,
887  struct ast_ari_response *response)
888 {
890  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
891  struct ao2_iterator i;
892  struct ast_bridge *bridge;
893 
894  bridges = ast_bridges();
895  if (!bridges) {
897  return;
898  }
899 
900  json = ast_json_array_create();
901  if (!json) {
903  return;
904  }
905 
906  i = ao2_iterator_init(bridges, 0);
907  while ((bridge = ao2_iterator_next(&i))) {
908  struct ast_bridge_snapshot *snapshot = ast_bridge_get_snapshot(bridge);
909  /* ast_bridge_snapshot_to_json will return NULL if snapshot is NULL */
910  struct ast_json *json_bridge = ast_bridge_snapshot_to_json(snapshot, stasis_app_get_sanitizer());
911 
912  ao2_ref(bridge, -1);
913  ao2_cleanup(snapshot);
914  if (!json_bridge || ast_json_array_append(json, json_bridge)) {
917  return;
918  }
919  }
921 
922  ast_ari_response_ok(response, ast_json_ref(json));
923 }
924 
927  struct ast_ari_response *response)
928 {
929  RAII_VAR(struct ast_bridge *, bridge, stasis_app_bridge_create(args->type, args->name, args->bridge_id), ao2_cleanup);
930  RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
931 
932  if (!bridge) {
934  response, 500, "Internal Error",
935  "Unable to create bridge");
936  return;
937  }
938 
939  ast_bridge_lock(bridge);
940  snapshot = ast_bridge_snapshot_create(bridge);
941  ast_bridge_unlock(bridge);
942 
943  if (!snapshot) {
945  response, 500, "Internal Error",
946  "Unable to create snapshot for new bridge");
947  return;
948  }
949 
950  ast_ari_response_ok(response,
952 }
953 
956  struct ast_ari_response *response)
957 {
958  RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
959  RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
960 
961  if (bridge) {
962  /* update */
963  if (!ast_strlen_zero(args->name)
964  && strcmp(args->name, bridge->name)) {
966  response, 500, "Internal Error",
967  "Changing bridge name is not implemented");
968  return;
969  }
970  if (!ast_strlen_zero(args->type)) {
972  response, 500, "Internal Error",
973  "Supplying a bridge type when updating a bridge is not allowed.");
974  return;
975  }
976  ast_ari_response_ok(response,
978  return;
979  }
980 
981  bridge = stasis_app_bridge_create(args->type, args->name, args->bridge_id);
982  if (!bridge) {
984  response, 500, "Internal Error",
985  "Unable to create bridge");
986  return;
987  }
988 
989  ast_bridge_lock(bridge);
990  snapshot = ast_bridge_snapshot_create(bridge);
991  ast_bridge_unlock(bridge);
992 
993  if (!snapshot) {
995  response, 500, "Internal Error",
996  "Unable to create snapshot for new bridge");
997  return;
998  }
999 
1000  ast_ari_response_ok(response,
1002 }
1003 
1005  struct ast_channel *chan, void *data)
1006 {
1007  struct ast_bridge *bridge = data;
1008 
1009  ast_bridge_lock(bridge);
1011  ast_bridge_unlock(bridge);
1012 
1013  return 0;
1014 }
1015 
1018 {
1019  struct ast_bridge *bridge;
1020  struct stasis_app_control *control;
1021 
1022  bridge = find_bridge(response, args->bridge_id);
1023  if (!bridge) {
1024  return;
1025  }
1026 
1027  control = find_channel_control(response, args->channel_id);
1028  if (!control) {
1029  ao2_ref(bridge, -1);
1030  return;
1031  }
1032 
1033  if (stasis_app_get_bridge(control) != bridge) {
1034  ast_ari_response_error(response, 422,
1035  "Unprocessable Entity",
1036  "Channel not in this bridge");
1037  ao2_ref(bridge, -1);
1038  ao2_ref(control, -1);
1039  return;
1040  }
1041 
1043  ao2_bump(bridge), __ao2_cleanup);
1044 
1045  ao2_ref(bridge, -1);
1046  ao2_ref(control, -1);
1047 
1048  ast_ari_response_no_content(response);
1049 }
1050 
1053 {
1054  struct ast_bridge *bridge;
1055 
1056  bridge = find_bridge(response, args->bridge_id);
1057  if (!bridge) {
1058  return;
1059  }
1060 
1061  ast_bridge_lock(bridge);
1063  ast_bridge_unlock(bridge);
1064 
1065  ao2_ref(bridge, -1);
1066  ast_ari_response_no_content(response);
1067 }
static void control_list_dtor(void *obj)
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
Stasis Application Recording API. See StasisApplication API" for detailed documentation.
Main Channel structure associated with a channel.
Music on hold handling.
struct ao2_container * ast_bridges(void)
Returns the global bridges container.
Definition: bridge.c:174
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
void stasis_app_control_absorb_dtmf_in_bridge(struct stasis_app_control *control, int absorb)
Set whether DTMF from the channel is absorbed instead of passing through to the bridge.
Definition: control.c:1464
int stasis_app_control_add_role(struct stasis_app_control *control, const char *role)
Apply a bridge role to a channel controlled by a stasis app control.
Definition: control.c:316
int stasis_app_control_bridge_features_init(struct stasis_app_control *control)
Initialize bridge features into a channel control.
Definition: control.c:1451
void ast_ari_bridges_start_moh(struct ast_variable *headers, struct ast_ari_bridges_start_moh_args *args, struct ast_ari_response *response)
Play music on hold to a bridge or change the MOH class that is playing.
int stasis_app_control_is_done(struct stasis_app_control *control)
Check if a control is marked as done.
Definition: res_stasis.c:1273
Asterisk main include file. File version handling, generic pbx functions.
const ast_string_field uniqueid
Definition: bridge.h:409
static struct ast_bridge * find_bridge(struct ast_ari_response *response, const char *bridge_id)
Finds a bridge, filling the response with an error, if appropriate.
void ast_ari_bridges_play_with_id(struct ast_variable *headers, struct ast_ari_bridges_play_with_id_args *args, struct ast_ari_response *response)
Start playback of media on a bridge.
static void ari_bridges_play_new(const char **args_media, size_t args_media_count, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response, struct ast_bridge *bridge)
#define ast_pthread_create_detached(a, b, c, d)
Definition: utils.h:563
void ast_ari_bridges_get(struct ast_variable *headers, struct ast_ari_bridges_get_args *args, struct ast_ari_response *response)
Get bridge details.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
static int check_add_remove_channel(struct ast_ari_response *response, struct stasis_app_control *control, enum stasis_app_control_channel_result result)
struct stasis_app_control * stasis_app_control_create(struct ast_channel *chan)
Creates a control handler for a channel that isn&#39;t in a stasis app.
Definition: res_stasis.c:333
void ast_ari_bridges_add_channel(struct ast_variable *headers, struct ast_ari_bridges_add_channel_args *args, struct ast_ari_response *response)
Add a channel to a bridge.
void ast_ari_response_created(struct ast_ari_response *response, const char *url, struct ast_json *message)
Fill in a Created (201) ast_ari_response.
Definition: res_ari.c:305
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
#define LOG_WARNING
Definition: logger.h:274
Structure that contains a snapshot of information about a bridge.
Definition: bridge.h:322
void __ao2_cleanup(void *obj)
Definition: astobj2.c:674
int stasis_app_control_remove_channel_from_bridge(struct stasis_app_control *control, struct ast_bridge *bridge)
Remove a channel from the bridge.
Definition: control.c:1420
Structure for variables, used for configurations and for channel variables.
char * ast_uri_encode(const char *string, char *outbuf, int buflen, struct ast_flags spec)
Turn text string to URI-encoded XX version.
Definition: main/utils.c:577
Structure representing a snapshot of channel state.
int stasis_app_send_command(struct stasis_app_control *control, stasis_app_command_cb command, void *data, command_data_destructor_fn data_destructor)
Invokes a command on a control&#39;s channel.
Definition: control.c:898
void ast_ari_bridges_remove_channel(struct ast_variable *headers, struct ast_ari_bridges_remove_channel_args *args, struct ast_ari_response *response)
Remove a channel from a bridge.
void stasis_app_bridge_playback_channel_remove(char *bridge_id, struct stasis_app_control *control)
remove channel from list of ARI playback channels for bridges.
Definition: res_stasis.c:743
struct ast_json * stasis_app_playback_to_json(const struct stasis_app_playback *playback)
Convert a playback to its JSON representation.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
void ast_ari_bridges_create(struct ast_variable *headers, struct ast_ari_bridges_create_args *args, struct ast_ari_response *response)
Create a new bridge.
unsigned int ast_callid
Definition: logger.h:87
#define ast_assert(a)
Definition: utils.h:695
#define ao2_unlock(a)
Definition: astobj2.h:730
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char stasis_app_recording_termination_parse(const char *str)
Parse a string into the recording termination enum.
const char * args
#define NULL
Definition: resample.c:96
static int bridge_set_video_source_cb(struct stasis_app_control *control, struct ast_channel *chan, void *data)
#define LOG_DEBUG
Definition: logger.h:241
static struct control_list * control_list_create(struct ast_ari_response *response, size_t count, const char **channels)
void ast_ari_response_alloc_failed(struct ast_ari_response *response)
Fill in response with a 500 message for allocation failures.
Definition: res_ari.c:298
void ast_ari_bridges_play(struct ast_variable *headers, struct ast_ari_bridges_play_args *args, struct ast_ari_response *response)
Start playback of media on a bridge.
int stasis_app_channel_unreal_set_internal(struct ast_channel *chan)
Mark this unreal channel and it&#39;s other half as being internal to Stasis.
Definition: res_stasis.c:2288
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:269
#define ast_strlen_zero(foo)
Definition: strings.h:52
struct ast_channel * ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
Requests a channel.
Definition: channel.c:6444
#define ao2_bump(obj)
Definition: astobj2.h:491
int ast_callid_threadassoc_add(ast_callid callid)
Adds a known callid to thread storage of the calling thread.
Definition: logger.c:1984
const char * stasis_app_playback_get_id(struct stasis_app_playback *playback)
Gets the unique id of a playback object.
struct ast_json * stasis_app_recording_to_json(const struct stasis_app_recording *recording)
Construct a JSON model of a recording.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
void ast_ari_bridges_stop_moh(struct ast_variable *headers, struct ast_ari_bridges_stop_moh_args *args, struct ast_ari_response *response)
Stop playing music on hold to a bridge.
#define STASIS_APP_RECORDING_TERMINATE_INVALID
play_found_result
struct ast_bridge_snapshot * ast_bridge_get_snapshot(struct ast_bridge *bridge)
Returns the current snapshot for the bridge.
General Asterisk PBX channel definitions.
stasis_app_control_channel_result
Result codes used when adding/removing channels to/from bridges.
Definition: stasis_app.h:777
#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:911
static void * bridge_channel_control_thread(void *data)
void ast_ari_response_ok(struct ast_ari_response *response, struct ast_json *message)
Fill in an OK (200) ast_ari_response.
Definition: res_ari.c:276
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2992
#define ao2_ref(o, delta)
Definition: astobj2.h:464
int ast_unreal_channel_push_to_bridge(struct ast_channel *ast, struct ast_bridge *bridge, unsigned int flags)
Push the semi2 unreal channel into a bridge from either member of the unreal pair.
Definition: core_unreal.c:928
static int ari_bridges_play_helper(const char **args_media, size_t args_media_count, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response, struct ast_bridge *bridge, struct stasis_app_control *control, struct ast_json **json, char **playback_url)
Performs common setup for a bridge playback operation with both new controls and when existing contro...
#define ao2_lock(a)
Definition: astobj2.h:718
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
#define ast_format_cap_append(cap, format, framing)
Definition: format_cap.h:103
static char language[MAX_LANGUAGE]
Definition: chan_alsa.c:117
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
static struct channel_usage channels
#define ast_format_cap_alloc(flags)
Definition: format_cap.h:52
struct stasis_app_control * controls[]
struct ast_json * ast_json_array_create(void)
Create a empty JSON array.
Definition: json.c:352
static struct stasis_app_control * find_channel_control(struct ast_ari_response *response, const char *channel_id)
Finds the control object for a channel, filling the response with an error, if appropriate.
int ast_json_array_append(struct ast_json *array, struct ast_json *value)
Append to an array.
Definition: json.c:368
Format Capabilities API.
void stasis_app_control_flush_queue(struct stasis_app_control *control)
Flush the control command queue.
Definition: res_stasis.c:1278
void ast_ari_bridges_destroy(struct ast_variable *headers, struct ast_ari_bridges_destroy_args *args, struct ast_ari_response *response)
Shut down a bridge.
int stasis_app_control_add_channel_to_bridge(struct stasis_app_control *control, struct ast_bridge *bridge)
Add a channel to the bridge.
Definition: control.c:1383
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
Structure that contains information about a bridge.
Definition: bridge.h:357
Backend API for implementing components of res_stasis.
struct stasis_message_sanitizer * stasis_app_get_sanitizer(void)
Get the Stasis message sanitizer for app_stasis applications.
Definition: res_stasis.c:2264
enum ast_record_if_exists stasis_app_recording_if_exists_parse(const char *str)
Parse a string into the if_exists enum.
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
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
int stasis_app_bridge_moh_stop(struct ast_bridge *bridge)
Breaks down MOH channels playing on the bridge created by stasis_app_bridge_moh_channel.
Definition: res_stasis.c:649
int errno
struct ast_bridge * stasis_app_bridge_find_by_id(const char *bridge_id)
Returns the bridge with the given id.
Definition: res_stasis.c:774
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:7866
void stasis_app_bridge_destroy(const char *bridge_id)
Destroy the bridge.
Definition: res_stasis.c:861
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
void ast_ari_bridges_clear_video_source(struct ast_variable *headers, struct ast_ari_bridges_clear_video_source_args *args, struct ast_ari_response *response)
Removes any explicit video source in a multi-party mixing bridge. This operation has no effect on bri...
struct ast_bridge_snapshot * ast_bridge_snapshot_create(struct ast_bridge *bridge)
Generate a snapshot of the bridge state. This is an ao2 object, so ao2_cleanup() to deallocate...
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
struct ast_channel_snapshot * stasis_app_control_get_snapshot(const struct stasis_app_control *control)
Returns the most recent snapshot for the associated channel.
Definition: control.c:860
static struct ao2_container * bridges
Definition: bridge.c:123
void ast_ari_bridges_list(struct ast_variable *headers, struct ast_ari_bridges_list_args *args, struct ast_ari_response *response)
List all active bridges in Asterisk.
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition: bridge.h:493
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
void ast_ari_response_error(struct ast_ari_response *response, int response_code, const char *response_text, const char *message_fmt,...)
Fill in an error ast_ari_response.
Definition: res_ari.c:259
static enum play_found_result ari_bridges_play_found(const char **args_media, size_t args_media_count, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response, struct ast_bridge *bridge, struct ast_channel *found_channel)
Performs common setup for a bridge playback operation with both new controls and when existing contro...
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2548
void ast_bridge_set_talker_src_video_mode(struct ast_bridge *bridge)
Set the bridge to pick the strongest talker supporting video as the single source video feed...
Definition: bridge.c:3833
struct stasis_app_recording_options * stasis_app_recording_options_create(const char *name, const char *format)
Allocate a recording options object.
struct stasis_forward * stasis_forward_cancel(struct stasis_forward *forward)
Definition: stasis.c:1548
void ast_ari_bridges_create_with_id(struct ast_variable *headers, struct ast_ari_bridges_create_with_id_args *args, struct ast_ari_response *response)
Create a new bridge or updates an existing one.
void ast_ari_response_no_content(struct ast_ari_response *response)
Fill in a No Content (204) ast_ari_response.
Definition: res_ari.c:284
struct ast_channel * stasis_app_bridge_playback_channel_find(struct ast_bridge *bridge)
Finds an existing ARI playback channel in a bridge.
Definition: res_stasis.c:759
struct ast_format * ast_get_format_for_file_ext(const char *file_ext)
Get the ast_format associated with the given file extension.
Definition: file.c:1938
void stasis_app_control_execute_until_exhausted(struct ast_channel *chan, struct stasis_app_control *control)
Act on a stasis app control queue until it is empty.
Definition: res_stasis.c:1250
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:550
struct stasis_app_control * stasis_app_control_find_by_channel_id(const char *channel_id)
Returns the handler for the channel with the given id.
Definition: res_stasis.c:349
void stasis_app_control_inhibit_colp_in_bridge(struct stasis_app_control *control, int inhibit_colp)
Set whether COLP frames should be generated when joining the bridge.
Definition: control.c:1476
void ast_bridge_set_single_src_video_mode(struct ast_bridge *bridge, struct ast_channel *video_src_chan)
Set a bridge to feed a single video source to all participants.
Definition: bridge.c:3816
static struct ast_channel * prepare_bridge_media_channel(const char *type)
#define ast_bridge_lock(bridge)
Lock the bridge.
Definition: bridge.h:480
Generated file - declares stubs to be implemented in res/ari/resource_bridges.c.
void ast_ari_bridges_record(struct ast_variable *headers, struct ast_ari_bridges_record_args *args, struct ast_ari_response *response)
Start a recording.
void stasis_app_control_clear_roles(struct stasis_app_control *control)
Clear bridge roles currently applied to a channel controlled by a stasis app control.
Definition: control.c:338
Stasis Application Playback API. See StasisApplication API" for detailed documentation.
int stasis_app_bridge_playback_channel_add(struct ast_bridge *bridge, struct ast_channel *chan, struct stasis_app_control *control)
Adds a channel to the list of ARI playback channels for bridges.
Definition: res_stasis.c:705
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object...
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
struct ast_json * ast_bridge_snapshot_to_json(const struct ast_bridge_snapshot *snapshot, const struct stasis_message_sanitizer *sanitize)
Build a JSON object from a ast_bridge_snapshot.
const char * ast_channel_name(const struct ast_channel *chan)
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.
static PGresult * result
Definition: cel_pgsql.c:88
Abstract JSON element (object, array, string, int, ...).
Forwarding information.
Definition: stasis.c:1531
const struct ast_flags ast_uri_http
Definition: main/utils.c:573
Stasis Application API. See Stasis Application API for detailed documentation.
Generic container type.
void ast_ari_bridges_set_video_source(struct ast_variable *headers, struct ast_ari_bridges_set_video_source_args *args, struct ast_ari_response *response)
Set a channel as the video source in a multi-party mixing bridge. This operation has no effect on bri...
static struct test_options options
struct stasis_topic * ast_bridge_topic(struct ast_bridge *bridge)
A topic which publishes the events for a particular bridge.
struct stasis_forward * stasis_forward_all(struct stasis_topic *from_topic, struct stasis_topic *to_topic)
Create a subscription which forwards all messages from one topic to another.
Definition: stasis.c:1578
Bridging API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
struct stasis_app_control * control
struct ast_channel * stasis_app_bridge_moh_channel(struct ast_bridge *bridge)
Finds or creates an announcer channel in a bridge that can play music on hold.
Definition: res_stasis.c:629
ast_callid ast_channel_callid(const struct ast_channel *chan)
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
void stasis_app_control_mute_in_bridge(struct stasis_app_control *control, int mute)
Set whether audio from the channel is muted instead of passing through to the bridge.
Definition: control.c:1470
struct stasis_app_recording * stasis_app_control_record(struct stasis_app_control *control, struct stasis_app_recording_options *options)
Record media from a channel.
struct ast_bridge_snapshot * ast_bridge_get_snapshot_by_uniqueid(const char *bridge_id)
Returns the current snapshot for the bridge.
Media Format Cache API.
struct ast_bridge * stasis_app_bridge_create(const char *type, const char *name, const char *id)
Create a bridge of the specified type.
Definition: res_stasis.c:851
Unreal channel derivative framework.
static void ari_bridges_handle_play(const char *args_bridge_id, const char **args_media, size_t args_media_count, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response)
struct stasis_app_control * stasis_app_control_find_by_channel(const struct ast_channel *chan)
Returns the handler for the given channel.
Definition: res_stasis.c:338