Asterisk - The Open Source Telephony Project GIT-master-d856a3e
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 <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 recording support.
22 *
23 * \author David M. Lee, II <dlee@digium.com>
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 */
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 */
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,
95 "recording", blob);
96}
97
99 .to_json = recording_to_json,
100);
101
102static 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
109static 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
144static void recording_options_dtor(void *obj)
145{
147
149}
150
152 const char *name, const char *format)
153{
156
158
159 if (!options || ast_string_field_init(options, 128)) {
160 return NULL;
161 }
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
217static 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
252static 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
277static void recording_fail(struct stasis_app_control *control,
278 struct stasis_app_recording *recording,
279 const char *cause)
280{
282
284 recording, STASIS_APP_RECORDING_STATE_FAILED, cause);
285}
286
287static 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
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",
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
357
358 return 0;
359}
360
361static 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
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",
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,
438
440
441 old_recording = ao2_find(recordings, options->name,
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
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{
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",
501 }
502
503 return ast_json_ref(json);
504}
505
506typedef int (*recording_operation_cb)(struct stasis_app_recording *recording);
507
508static int recording_noop(struct stasis_app_recording *recording)
509{
510 return 0;
511}
512
513static int recording_disregard(struct stasis_app_recording *recording)
514{
516 return 0;
517}
518
519static int recording_cancel(struct stasis_app_recording *recording)
520{
521 int res = 0;
525 res |= ast_filedelete(recording->absolute_name, NULL);
526 return res;
527}
528
529static int recording_stop(struct stasis_app_recording *recording)
530{
534}
535
536static int recording_pause(struct stasis_app_recording *recording)
537{
541}
542
543static int recording_unpause(struct stasis_app_recording *recording)
544{
548}
549
550static int toggle_recording_mute(struct stasis_app_recording *recording, int desired_mute_state)
551{
552 if (recording->muted == desired_mute_state) {
553 /* already in desired state */
554 return 0;
555 }
556
557 recording->muted = desired_mute_state;
558
561}
562
563static int recording_mute(struct stasis_app_recording *recording)
564{
565 return toggle_recording_mute(recording, 1);
566}
567
568static int recording_unmute(struct stasis_app_recording *recording)
569{
570 return toggle_recording_mute(recording, 0);
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) {
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
629static int load_module(void)
630{
631 int r;
632
634 if (r != 0) {
636 }
637
640 if (!recordings) {
643 }
645}
646
647static int unload_module(void)
648{
652 return 0;
653}
654
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
const char * str
Definition: app_jack.c:147
ast_mutex_t lock
Definition: app_sla.c:331
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_log
Definition: astobj2.c:42
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028
#define OBJ_KEY
Definition: astobj2.h:1151
#define OBJ_POINTER
Definition: astobj2.h:1150
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_unlink_flags(container, obj, flags)
Remove an object from a container.
Definition: astobj2.h:1600
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition: astobj2.h:1063
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_UNLINK
Definition: astobj2.h:1039
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#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
enum cc_state state
Definition: ccss.c:393
static const char type[]
Definition: chan_ooh323.c:109
int ast_auto_answer(struct ast_channel *chan)
Answer a channel, if it's not already answered.
Definition: channel.c:2830
const char * ast_channel_uniqueid(const struct ast_channel *chan)
Convenient Signal Processing routines.
Generic File Format Support. Should be included by clients of the file handling routines....
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1141
static const char name[]
Definition: format_mp3.c:68
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.
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:2149
ast_record_if_exists
@ AST_RECORD_IF_EXISTS_FAIL
@ AST_RECORD_IF_EXISTS_APPEND
@ AST_RECORD_IF_EXISTS_OVERWRITE
@ AST_RECORD_IF_EXISTS_ERROR
@ AST_CONTROL_RECORD_CANCEL
@ AST_CONTROL_RECORD_STOP
@ AST_CONTROL_RECORD_MUTE
@ AST_CONTROL_RECORD_SUSPEND
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_WARNING
struct ast_json * ast_json_string_create(const char *value)
Construct a JSON string from value.
Definition: json.c:278
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
struct ast_json * ast_json_timeval(const struct timeval tv, const char *zone)
Construct a timeval as JSON.
Definition: json.c:670
struct ast_json * ast_json_integer_create(intmax_t value)
Create a JSON integer.
Definition: json.c:327
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
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:414
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:283
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
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:604
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_APP_DEPEND
Definition: module.h:342
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_RECORDING_DIR
Definition: options.c:156
struct stasis_app_recording_options * stasis_app_recording_options_create(const char *name, const char *format)
Allocate a recording options object.
static void recording_cleanup(void *data)
static int recording_unpause(struct stasis_app_recording *recording)
static int recording_pause(struct stasis_app_recording *recording)
static struct ast_json * recording_to_json(struct stasis_message *message, const struct stasis_message_sanitizer *sanitize)
static int recording_noop(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.
static void recording_options_dtor(void *obj)
static void recording_dtor(void *obj)
static int recording_disregard(struct stasis_app_recording *recording)
enum stasis_app_recording_state stasis_app_recording_get_state(struct stasis_app_recording *recording)
Gets the current state of a recording operation.
static int recording_hash(const void *obj, int flags)
static int recording_mute(struct stasis_app_recording *recording)
static int recording_unmute(struct stasis_app_recording *recording)
static int record_file(struct stasis_app_control *control, struct ast_channel *chan, void *data)
STASIS_MESSAGE_TYPE_DEFN(stasis_app_recording_snapshot_type,.to_json=recording_to_json,)
struct stasis_app_recording * stasis_app_recording_find_by_name(const char *name)
Finds the recording object with the given name.
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.
int(* recording_operation_cb)(struct stasis_app_recording *recording)
#define RECORDING_BUCKETS
recording_operation_cb operations[STASIS_APP_RECORDING_STATE_MAX][STASIS_APP_RECORDING_OPER_MAX]
static struct stasis_app_control_rule rule_recording
static enum stasis_app_control_channel_result check_rule_recording(const struct stasis_app_control *control)
struct stasis_app_recording * stasis_app_control_record(struct stasis_app_control *control, struct stasis_app_recording_options *options)
Record media from a channel.
static struct ao2_container * recordings
static int recording_cmp(void *obj, void *arg, int flags)
enum ast_record_if_exists stasis_app_recording_if_exists_parse(const char *str)
Parse a string into the if_exists enum.
static int toggle_recording_mute(struct stasis_app_recording *recording, int desired_mute_state)
char stasis_app_recording_termination_parse(const char *str)
Parse a string into the recording termination enum.
static int load_module(void)
static void recording_fail(struct stasis_app_control *control, struct stasis_app_recording *recording, const char *cause)
static int recording_stop(struct stasis_app_recording *recording)
static void recording_set_state(struct stasis_app_recording *recording, enum stasis_app_recording_state state, const char *cause)
static int unload_module(void)
const char * stasis_app_recording_get_name(struct stasis_app_recording *recording)
Gets the unique name of a recording object.
static const char * state_to_string(enum stasis_app_recording_state state)
static int recording_cancel(struct stasis_app_recording *recording)
static void recording_publish(struct stasis_app_recording *recording, const char *cause)
#define NULL
Definition: resample.c:96
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition: stasis.h:1515
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1493
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
const struct timeval * stasis_message_timestamp(const struct stasis_message *msg)
Get the time when a message was created.
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:1439
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:1454
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:1445
struct ast_bridge * stasis_app_get_bridge(struct stasis_app_control *control)
Gets the bridge currently associated with a control object.
Definition: control.c:940
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:226
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:219
stasis_app_control_channel_result
Result codes used when adding/removing channels to/from bridges.
Definition: stasis_app.h:776
@ STASIS_APP_CHANNEL_RECORDING
Definition: stasis_app.h:780
Backend API for implementing components of res_stasis.
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:913
Stasis Application Recording API. See StasisApplication API" for detailed documentation.
struct stasis_message_type * stasis_app_recording_snapshot_type(void)
Message type for recording updates. The data is an ast_channel_blob.
#define STASIS_APP_RECORDING_TERMINATE_NONE
#define STASIS_APP_RECORDING_TERMINATE_ANY
stasis_app_recording_oper_results
Possible results from a recording operation.
@ STASIS_APP_RECORDING_OPER_NOT_RECORDING
@ STASIS_APP_RECORDING_OPER_FAILED
@ STASIS_APP_RECORDING_OPER_OK
stasis_app_recording_state
@ STASIS_APP_RECORDING_STATE_CANCELED
@ STASIS_APP_RECORDING_STATE_FAILED
@ STASIS_APP_RECORDING_STATE_COMPLETE
@ STASIS_APP_RECORDING_STATE_RECORDING
@ STASIS_APP_RECORDING_STATE_PAUSED
@ STASIS_APP_RECORDING_STATE_QUEUED
@ STASIS_APP_RECORDING_STATE_MAX
#define STASIS_APP_RECORDING_TERMINATE_INVALID
stasis_app_recording_media_operation
@ STASIS_APP_RECORDING_PAUSE
@ STASIS_APP_RECORDING_UNPAUSE
@ STASIS_APP_RECORDING_UNMUTE
@ STASIS_APP_RECORDING_STOP
@ STASIS_APP_RECORDING_OPER_MAX
@ STASIS_APP_RECORDING_MUTE
@ STASIS_APP_RECORDING_CANCEL
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Generic container type.
Blob of data associated with a channel.
struct ast_json * blob
Main Channel structure associated with a channel.
Abstract JSON element (object, array, string, int, ...).
Rule to check to see if an operation is allowed.
Definition: stasis_app.h:345
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
enum ast_record_if_exists if_exists
struct stasis_app_recording::@480 duration
struct stasis_app_recording_options * options
enum stasis_app_recording_state state
struct stasis_app_control * control
Structure containing callbacks for Stasis message sanitization.
Definition: stasis.h:200
static struct test_options options
#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
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: utils.c:2584
#define ast_assert(a)
Definition: utils.h:739