Asterisk - The Open Source Telephony Project  18.5.0
res_stasis_recording.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 <[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 res_stasis recording support.
22  *
23  * \author David M. Lee, II <[email protected]>
24  */
25 
26 /*** MODULEINFO
27  <depend type="module">res_stasis</depend>
28  <support_level>core</support_level>
29  ***/
30 
31 #include "asterisk.h"
32 
33 #include "asterisk/dsp.h"
34 #include "asterisk/file.h"
35 #include "asterisk/module.h"
36 #include "asterisk/paths.h"
40 
41 /*! Number of hash buckets for recording container. Keep it prime! */
42 #define RECORDING_BUCKETS 127
43 
44 /*! Comment is ignored by most formats, so we will ignore it, too. */
45 #define RECORDING_COMMENT NULL
46 
47 /*! Recording check is unimplemented. le sigh */
48 #define RECORDING_CHECK 0
49 
50 /*! Container of all current recordings */
51 static struct ao2_container *recordings;
52 
54  /*! Recording options. */
56  /*! Absolute path (minus extension) of the recording */
58  /*! Control object for the channel we're recording */
60  /*! Current state of the recording. */
62  /*! Duration calculations */
63  struct {
64  /*! Total duration */
65  int total;
66  /*! Duration minus any silence */
68  } duration;
69  /*! Indicates whether the recording is currently muted */
70  int muted:1;
71 };
72 
74  const struct stasis_message_sanitizer *sanitize)
75 {
76  struct ast_channel_blob *channel_blob = stasis_message_data(message);
77  struct ast_json *blob = channel_blob->blob;
78  const char *state =
80  const char *type;
81 
82  if (!strcmp(state, "recording")) {
83  type = "RecordingStarted";
84  } else if (!strcmp(state, "done") || !strcasecmp(state, "canceled")) {
85  type = "RecordingFinished";
86  } else if (!strcmp(state, "failed")) {
87  type = "RecordingFailed";
88  } else {
89  return NULL;
90  }
91 
92  return ast_json_pack("{s: s, s: o?, s: O}",
93  "type", type,
94  "timestamp", ast_json_timeval(*stasis_message_timestamp(message), NULL),
95  "recording", blob);
96 }
97 
99  .to_json = recording_to_json,
100 );
101 
102 static int recording_hash(const void *obj, int flags)
103 {
104  const struct stasis_app_recording *recording = obj;
105  const char *id = flags & OBJ_KEY ? obj : recording->options->name;
106  return ast_str_hash(id);
107 }
108 
109 static int recording_cmp(void *obj, void *arg, int flags)
110 {
111  struct stasis_app_recording *lhs = obj;
112  struct stasis_app_recording *rhs = arg;
113  const char *rhs_id = flags & OBJ_KEY ? arg : rhs->options->name;
114 
115  if (strcmp(lhs->options->name, rhs_id) == 0) {
116  return CMP_MATCH | CMP_STOP;
117  } else {
118  return 0;
119  }
120 }
121 
123 {
124  switch (state) {
126  return "queued";
128  return "recording";
130  return "paused";
132  return "done";
134  return "failed";
136  return "canceled";
138  return "?";
139  }
140 
141  return "?";
142 }
143 
144 static void recording_options_dtor(void *obj)
145 {
147 
149 }
150 
152  const char *name, const char *format)
153 {
155  ao2_cleanup);
156 
158 
159  if (!options || ast_string_field_init(options, 128)) {
160  return NULL;
161  }
162  ast_string_field_set(options, name, name);
163  ast_string_field_set(options, format, format);
164 
165  ao2_ref(options, +1);
166  return options;
167 }
168 
170 {
171  if (ast_strlen_zero(str)) {
173  }
174 
175  if (strcasecmp(str, "none") == 0) {
177  }
178 
179  if (strcasecmp(str, "any") == 0) {
181  }
182 
183  if (strcasecmp(str, "#") == 0) {
184  return '#';
185  }
186 
187  if (strcasecmp(str, "*") == 0) {
188  return '*';
189  }
190 
192 }
193 
195  const char *str)
196 {
197  if (ast_strlen_zero(str)) {
198  /* Default value */
200  }
201 
202  if (strcasecmp(str, "fail") == 0) {
204  }
205 
206  if (strcasecmp(str, "overwrite") == 0) {
208  }
209 
210  if (strcasecmp(str, "append") == 0) {
212  }
213 
215 }
216 
217 static void recording_publish(struct stasis_app_recording *recording, const char *cause)
218 {
219  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
221 
222  ast_assert(recording != NULL);
223 
224  json = stasis_app_recording_to_json(recording);
225  if (json == NULL) {
226  return;
227  }
228 
229  if (!ast_strlen_zero(cause)) {
230  struct ast_json *failure_cause = ast_json_string_create(cause);
231 
232  if (!failure_cause) {
233  return;
234  }
235 
236  if (ast_json_object_set(json, "cause", failure_cause)) {
237  return;
238  }
239  }
240 
244  if (message == NULL) {
245  return;
246  }
247 
249 }
250 
251 
252 static void recording_set_state(struct stasis_app_recording *recording,
254  const char *cause)
255 {
256  SCOPED_AO2LOCK(lock, recording);
257  recording->state = state;
258  recording_publish(recording, cause);
259 }
260 
262  const struct stasis_app_control *control)
263 {
265 }
266 
267 /*
268  * XXX This only works because there is one and only one rule in
269  * the system so it can be added to any number of channels
270  * without issue. However, as soon as there is another rule then
271  * watch out for weirdness because of cross linked lists.
272  */
275 };
276 
278  struct stasis_app_recording *recording,
279  const char *cause)
280 {
281  stasis_app_control_unregister_add_rule(control, &rule_recording);
282 
284  recording, STASIS_APP_RECORDING_STATE_FAILED, cause);
285 }
286 
287 static void recording_cleanup(void *data)
288 {
289  struct stasis_app_recording *recording = data;
290 
291  ao2_unlink_flags(recordings, recording,
293  ao2_ref(recording, -1);
294 }
295 
297  struct ast_channel *chan, void *data)
298 {
299  struct stasis_app_recording *recording = data;
300  char *acceptdtmf;
301  int res;
302 
303  ast_assert(recording != NULL);
304 
305  if (stasis_app_get_bridge(control)) {
306  ast_log(LOG_ERROR, "Cannot record channel while in bridge\n");
307  recording_fail(control, recording, "Cannot record channel while in bridge");
308  return -1;
309  }
310 
311  switch (recording->options->terminate_on) {
314  acceptdtmf = "";
315  break;
317  acceptdtmf = "#*0123456789abcd";
318  break;
319  default:
320  acceptdtmf = ast_alloca(2);
321  acceptdtmf[0] = recording->options->terminate_on;
322  acceptdtmf[1] = '\0';
323  }
324 
325  res = ast_auto_answer(chan);
326  if (res != 0) {
327  ast_debug(3, "%s: Failed to answer\n",
328  ast_channel_uniqueid(chan));
329  recording_fail(control, recording, "Failed to answer channel");
330  return -1;
331  }
332 
336  NULL, /* playfile */
337  recording->absolute_name,
338  recording->options->max_duration_seconds,
339  recording->options->format,
340  &recording->duration.total,
341  recording->options->max_silence_seconds ? &recording->duration.energy_only : NULL,
342  recording->options->beep,
343  -1, /* silencethreshold */
344  recording->options->max_silence_seconds * 1000,
345  NULL, /* path */
346  acceptdtmf,
347  NULL, /* canceldtmf */
348  1, /* skip_confirmation_sound */
349  recording->options->if_exists);
350 
351  ast_debug(3, "%s: Recording complete\n", ast_channel_uniqueid(chan));
352 
355 
356  stasis_app_control_unregister_add_rule(control, &rule_recording);
357 
358  return 0;
359 }
360 
361 static void recording_dtor(void *obj)
362 {
363  struct stasis_app_recording *recording = obj;
364 
365  ast_free(recording->absolute_name);
366  ao2_cleanup(recording->control);
367  ao2_cleanup(recording->options);
368 }
369 
371  struct stasis_app_control *control,
373 {
374  struct stasis_app_recording *recording;
375  char *last_slash;
376 
377  errno = 0;
378 
379  if (options == NULL ||
380  ast_strlen_zero(options->name) ||
381  ast_strlen_zero(options->format) ||
382  options->max_silence_seconds < 0 ||
383  options->max_duration_seconds < 0) {
384  errno = EINVAL;
385  return NULL;
386  }
387 
388  ast_debug(3, "%s: Sending record(%s.%s) command\n",
389  stasis_app_control_get_channel_id(control), options->name,
390  options->format);
391 
392  recording = ao2_alloc(sizeof(*recording), recording_dtor);
393  if (!recording) {
394  errno = ENOMEM;
395  return NULL;
396  }
397  recording->duration.total = -1;
398  recording->duration.energy_only = -1;
399 
400  ast_asprintf(&recording->absolute_name, "%s/%s",
402 
403  if (recording->absolute_name == NULL) {
404  errno = ENOMEM;
405  ao2_ref(recording, -1);
406  return NULL;
407  }
408 
409  if ((last_slash = strrchr(recording->absolute_name, '/'))) {
410  *last_slash = '\0';
412  recording->absolute_name, 0777) != 0) {
413  /* errno set by ast_mkdir */
414  ao2_ref(recording, -1);
415  return NULL;
416  }
417  *last_slash = '/';
418  }
419 
420  ao2_ref(options, +1);
421  recording->options = options;
422  ao2_ref(control, +1);
423  recording->control = control;
425 
426  if ((recording->options->if_exists == AST_RECORD_IF_EXISTS_FAIL) &&
427  (ast_fileexists(recording->absolute_name, NULL, NULL))) {
428  ast_log(LOG_WARNING, "Recording file '%s' already exists and ifExists option is failure.\n",
429  recording->absolute_name);
430  errno = EEXIST;
431  ao2_ref(recording, -1);
432  return NULL;
433  }
434 
435  {
436  RAII_VAR(struct stasis_app_recording *, old_recording, NULL,
437  ao2_cleanup);
438 
439  SCOPED_AO2LOCK(lock, recordings);
440 
441  old_recording = ao2_find(recordings, options->name,
442  OBJ_KEY | OBJ_NOLOCK);
443  if (old_recording) {
445  "Recording %s already in progress\n",
446  recording->options->name);
447  errno = EEXIST;
448  ao2_ref(recording, -1);
449  return NULL;
450  }
451  ao2_link(recordings, recording);
452  }
453 
454  stasis_app_control_register_add_rule(control, &rule_recording);
455 
457 
458  return recording;
459 }
460 
462  struct stasis_app_recording *recording)
463 {
464  return recording->state;
465 }
466 
468  struct stasis_app_recording *recording)
469 {
470  return recording->options->name;
471 }
472 
474 {
475  return ao2_find(recordings, name, OBJ_KEY);
476 }
477 
479  const struct stasis_app_recording *recording)
480 {
481  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
482 
483  if (recording == NULL) {
484  return NULL;
485  }
486 
487  json = ast_json_pack("{s: s, s: s, s: s, s: s}",
488  "name", recording->options->name,
489  "format", recording->options->format,
490  "state", state_to_string(recording->state),
491  "target_uri", recording->options->target);
492  if (json && recording->duration.total > -1) {
493  ast_json_object_set(json, "duration",
495  }
496  if (json && recording->duration.energy_only > -1) {
497  ast_json_object_set(json, "talking_duration",
499  ast_json_object_set(json, "silence_duration",
500  ast_json_integer_create(recording->duration.total - recording->duration.energy_only));
501  }
502 
503  return ast_json_ref(json);
504 }
505 
506 typedef int (*recording_operation_cb)(struct stasis_app_recording *recording);
507 
508 static int recording_noop(struct stasis_app_recording *recording)
509 {
510  return 0;
511 }
512 
513 static int recording_disregard(struct stasis_app_recording *recording)
514 {
516  return 0;
517 }
518 
519 static int recording_cancel(struct stasis_app_recording *recording)
520 {
521  int res = 0;
523  res |= stasis_app_control_queue_control(recording->control,
525  res |= ast_filedelete(recording->absolute_name, NULL);
526  return res;
527 }
528 
529 static int recording_stop(struct stasis_app_recording *recording)
530 {
532  return stasis_app_control_queue_control(recording->control,
534 }
535 
536 static int recording_pause(struct stasis_app_recording *recording)
537 {
539  return stasis_app_control_queue_control(recording->control,
541 }
542 
543 static int recording_unpause(struct stasis_app_recording *recording)
544 {
546  return stasis_app_control_queue_control(recording->control,
548 }
549 
550 static int recording_mute(struct stasis_app_recording *recording)
551 {
552  if (recording->muted) {
553  /* already muted */
554  return 0;
555  }
556 
557  recording->muted = 1;
558  return stasis_app_control_queue_control(recording->control,
560 }
561 
562 static int recording_unmute(struct stasis_app_recording *recording)
563 {
564  if (!recording->muted) {
565  /* already unmuted */
566  return 0;
567  }
568 
569  return stasis_app_control_queue_control(recording->control,
571 }
572 
588 };
589 
591  struct stasis_app_recording *recording,
593 {
595  SCOPED_AO2LOCK(lock, recording);
596 
597  if ((unsigned int)recording->state >= STASIS_APP_RECORDING_STATE_MAX) {
598  ast_log(LOG_WARNING, "Invalid recording state %u\n",
599  recording->state);
600  return -1;
601  }
602 
603  if ((unsigned int)operation >= STASIS_APP_RECORDING_OPER_MAX) {
604  ast_log(LOG_WARNING, "Invalid recording operation %u\n",
605  operation);
606  return -1;
607  }
608 
609  cb = operations[recording->state][operation];
610 
611  if (!cb) {
612  if (recording->state != STASIS_APP_RECORDING_STATE_RECORDING) {
613  /* So we can be specific in our error message. */
615  } else {
616  /* And, really, all operations should be valid during
617  * recording */
619  "Unhandled operation during recording: %u\n",
620  operation);
622  }
623  }
624 
625  return cb(recording) ?
627 }
628 
629 static int load_module(void)
630 {
631  int r;
632 
634  if (r != 0) {
636  }
637 
640  if (!recordings) {
643  }
645 }
646 
647 static int unload_module(void)
648 {
649  ao2_cleanup(recordings);
650  recordings = NULL;
652  return 0;
653 }
654 
655 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis application recording support",
656  .support_level = AST_MODULE_SUPPORT_CORE,
657  .load = load_module,
658  .unload = unload_module,
659  .requires = "res_stasis",
660  .load_pri = AST_MODPRI_APP_DEPEND
661 );
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
static int recording_cancel(struct stasis_app_recording *recording)
enum stasis_app_control_channel_result(* check_rule)(const struct stasis_app_control *control)
Checks to see if an operation is allowed on the control.
Definition: stasis_app.h:352
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.
enum stasis_app_recording_state state
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 int recording_noop(struct stasis_app_recording *recording)
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:591
static enum stasis_app_control_channel_result check_rule_recording(const struct stasis_app_control *control)
stasis_app_recording_media_operation
int ast_auto_answer(struct ast_channel *chan)
Answer a channel, if it&#39;s not already answered.
Definition: channel.c:2820
static int recording_stop(struct stasis_app_recording *recording)
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
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1501
Convenient Signal Processing routines.
struct stasis_app_control * control
#define OBJ_POINTER
Definition: astobj2.h:1154
#define LOG_WARNING
Definition: logger.h:274
recording_operation_cb operations[STASIS_APP_RECORDING_STATE_MAX][STASIS_APP_RECORDING_OPER_MAX]
static int unload_module(void)
#define RECORDING_BUCKETS
ast_record_if_exists
static int recording_disregard(struct stasis_app_recording *recording)
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition: stasis.h:1523
static int load_module(void)
Assume that the ao2_container is already locked.
Definition: astobj2.h:1067
static int recording_cmp(void *obj, void *arg, int flags)
static int recording_unmute(struct stasis_app_recording *recording)
#define ast_assert(a)
Definition: utils.h:695
const char * str
Definition: app_jack.c:147
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
#define NULL
Definition: resample.c:96
char stasis_app_recording_termination_parse(const char *str)
Parse a string into the recording termination enum.
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1098
Structure containing callbacks for Stasis message sanitization.
Definition: stasis.h:200
static int recording_mute(struct stasis_app_recording *recording)
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
Definition: json.c:404
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:269
Blob of data associated with a channel.
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ao2_bump(obj)
Definition: astobj2.h:491
void stasis_app_control_register_add_rule(struct stasis_app_control *control, struct stasis_app_control_rule *rule)
Registers an add channel to bridge rule.
Definition: control.c:217
static int recording_hash(const void *obj, int flags)
static struct stasis_app_control_rule rule_recording
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define STASIS_APP_RECORDING_TERMINATE_ANY
const char * stasis_app_recording_get_name(struct stasis_app_recording *recording)
Gets the unique name of a recording object.
#define STASIS_APP_RECORDING_TERMINATE_INVALID
int(* recording_operation_cb)(struct stasis_app_recording *recording)
stasis_app_control_channel_result
Result codes used when adding/removing channels to/from bridges.
Definition: stasis_app.h:777
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:911
struct ast_json * ast_json_string_create(const char *value)
Construct a JSON string from value.
Definition: json.c:268
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
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.
struct stasis_app_recording_options * stasis_app_recording_options_create(const char *name, const char *format)
Allocate a recording options object.
#define ao2_ref(o, delta)
Definition: astobj2.h:464
int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists)
Record a file based on input from a channel This function will play "auth-thankyou" upon successful r...
Definition: main/app.c:1992
static void recording_dtor(void *obj)
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
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
Rule to check to see if an operation is allowed.
Definition: stasis_app.h:345
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_json * ast_json_timeval(const struct timeval tv, const char *zone)
Construct a timeval as JSON.
Definition: json.c:649
static struct ast_json * recording_to_json(struct stasis_message *message, const struct stasis_message_sanitizer *sanitize)
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
const char * ast_channel_uniqueid(const struct ast_channel *chan)
struct stasis_app_recording_options * options
static void recording_options_dtor(void *obj)
struct stasis_app_recording * stasis_app_recording_find_by_name(const char *name)
Finds the recording object with the given name.
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
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
static int recording_unpause(struct stasis_app_recording *recording)
int errno
enum stasis_app_recording_state stasis_app_recording_get_state(struct stasis_app_recording *recording)
Gets the current state of a recording operation.
stasis_app_recording_oper_results
Possible results from a recording operation.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
struct stasis_message_type * stasis_app_recording_snapshot_type(void)
Message type for recording updates. The data is an ast_channel_blob.
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:602
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
struct stasis_app_recording::@498 duration
int ast_safe_mkdir(const char *base_path, const char *path, int mode)
Recursively create directory path, but only if it resolves within the given base_path.
Definition: main/utils.c:2336
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
void stasis_app_control_unregister_add_rule(struct stasis_app_control *control, struct stasis_app_control_rule *rule)
UnRegister an add channel to bridge rule.
Definition: control.c:224
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
stasis_app_recording_state
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.
static int record_file(struct stasis_app_control *control, struct ast_channel *chan, void *data)
static void recording_set_state(struct stasis_app_recording *recording, enum stasis_app_recording_state state, const char *cause)
const char * ast_config_AST_RECORDING_DIR
Definition: options.c:156
static int recording_pause(struct stasis_app_recording *recording)
struct ast_json * stasis_app_recording_to_json(const struct stasis_app_recording *recording)
Construct a JSON model of a recording.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
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
static void recording_fail(struct stasis_app_control *control, struct stasis_app_recording *recording, const char *cause)
enum ast_record_if_exists if_exists
enum ast_record_if_exists stasis_app_recording_if_exists_parse(const char *str)
Parse a string into the if_exists enum.
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1086
static void recording_cleanup(void *data)
enum stasis_app_recording_oper_results stasis_app_recording_operation(struct stasis_app_recording *recording, enum stasis_app_recording_media_operation operation)
Controls the media for a given recording operation.
STASIS_MESSAGE_TYPE_DEFN(stasis_app_recording_snapshot_type,.to_json=recording_to_json,)
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
#define ao2_unlink_flags(container, obj, flags)
Definition: astobj2.h:1622
Abstract JSON element (object, array, string, int, ...).
Generic container type.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
static snd_pcm_format_t format
Definition: chan_alsa.c:102
static const char * state_to_string(enum stasis_app_recording_state state)
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
static void recording_publish(struct stasis_app_recording *recording, const char *cause)
static struct ao2_container * recordings
#define STASIS_APP_RECORDING_TERMINATE_NONE
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
struct ast_json * ast_json_integer_create(intmax_t value)
Create a JSON integer.
Definition: json.c:317
#define ao2_link(container, obj)
Definition: astobj2.h:1549