Wed Jan 8 2020 09:49:55

Asterisk developer's documentation


app_record.c File Reference

Trivial application to record a sound file. More...

#include "asterisk.h"
#include "asterisk/file.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
#include "asterisk/dsp.h"

Go to the source code of this file.

Enumerations

enum  {
  OPTION_APPEND = (1 << 0), OPTION_NOANSWER = (1 << 1), OPTION_QUIET = (1 << 2), OPTION_SKIP = (1 << 3),
  OPTION_STAR_TERMINATE = (1 << 4), OPTION_IGNORE_TERMINATE = (1 << 5), OPTION_KEEP = (1 << 6), FLAG_HAS_PERCENT = (1 << 7),
  OPTION_ANY_TERMINATE = (1 << 8)
}
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
static int load_module (void)
 
static int record_exec (struct ast_channel *chan, const char *data)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Trivial Record Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, }
 
static char * app = "Record"
 
static struct ast_app_option app_opts [128] = { [ 'a' ] = { .flag = OPTION_APPEND }, [ 'k' ] = { .flag = OPTION_KEEP }, [ 'n' ] = { .flag = OPTION_NOANSWER }, [ 'q' ] = { .flag = OPTION_QUIET }, [ 's' ] = { .flag = OPTION_SKIP }, [ 't' ] = { .flag = OPTION_STAR_TERMINATE }, [ 'y' ] = { .flag = OPTION_ANY_TERMINATE }, [ 'x' ] = { .flag = OPTION_IGNORE_TERMINATE }, }
 
static struct ast_module_infoast_module_info = &__mod_info
 

Detailed Description

Trivial application to record a sound file.

Author
Matthew Fredrickson cresl.nosp@m.in@d.nosp@m.igium.nosp@m..com

Definition in file app_record.c.

Enumeration Type Documentation

anonymous enum
Enumerator
OPTION_APPEND 
OPTION_NOANSWER 
OPTION_QUIET 
OPTION_SKIP 
OPTION_STAR_TERMINATE 
OPTION_IGNORE_TERMINATE 
OPTION_KEEP 
FLAG_HAS_PERCENT 
OPTION_ANY_TERMINATE 

Definition at line 118 of file app_record.c.

118  {
119  OPTION_APPEND = (1 << 0),
120  OPTION_NOANSWER = (1 << 1),
121  OPTION_QUIET = (1 << 2),
122  OPTION_SKIP = (1 << 3),
123  OPTION_STAR_TERMINATE = (1 << 4),
124  OPTION_IGNORE_TERMINATE = (1 << 5),
125  OPTION_KEEP = (1 << 6),
126  FLAG_HAS_PERCENT = (1 << 7),
127  OPTION_ANY_TERMINATE = (1 << 8),
128 };

Function Documentation

static void __reg_module ( void  )
static

Definition at line 446 of file app_record.c.

static void __unreg_module ( void  )
static

Definition at line 446 of file app_record.c.

static int load_module ( void  )
static

Definition at line 441 of file app_record.c.

References ast_register_application_xml, and record_exec().

442 {
444 }
static int record_exec(struct ast_channel *chan, const char *data)
Definition: app_record.c:141
static char * app
Definition: app_record.c:116
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
static int record_exec ( struct ast_channel chan,
const char *  data 
)
static

Definition at line 141 of file app_record.c.

References ast_channel::_state, app_opts, args, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_closestream(), AST_CONTROL_VIDUPDATE, ast_copy_string(), ast_debug, AST_DECLARE_APP_ARGS, ast_dsp_free(), ast_dsp_get_threshold_from_settings(), ast_dsp_new(), ast_dsp_set_threshold(), ast_dsp_silence(), AST_FILE_MODE, ast_filedelete(), ast_fileexists(), AST_FORMAT_SLINEAR, AST_FRAME_DTMF, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_frfree, ast_indicate(), ast_log(), ast_mkdir(), AST_NONSTANDARD_APP_ARGS, ast_opt_transmit_silence, ast_read(), ast_remaining_ms(), ast_set_flag, ast_set_read_format(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stopstream(), ast_strdupa, ast_stream_rewind(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_truncstream(), ast_tvnow(), ast_waitfor(), ast_waitstream(), ast_writefile(), ast_writestream(), ext, f, FLAG_HAS_PERCENT, ast_frame::frametype, ast_frame_subclass::integer, ast_channel::language, LOG_WARNING, ast_channel::name, OPTION_ANY_TERMINATE, OPTION_APPEND, OPTION_IGNORE_TERMINATE, OPTION_KEEP, OPTION_NOANSWER, OPTION_QUIET, OPTION_SKIP, OPTION_STAR_TERMINATE, parse(), pbx_builtin_setvar_helper(), ast_channel::readformat, ast_frame::subclass, THRESHOLD_SILENCE, and ast_dsp::totalsilence.

Referenced by load_module().

142 {
143  int res = 0;
144  int count = 0;
145  char *ext = NULL, *opts[0];
146  char *parse, *dir, *file;
147  int i = 0;
148  char tmp[256];
149 
150  struct ast_filestream *s = NULL;
151  struct ast_frame *f = NULL;
152 
153  struct ast_dsp *sildet = NULL; /* silence detector dsp */
154  int totalsilence = 0;
155  int dspsilence = 0;
156  int silence = 0; /* amount of silence to allow */
157  int gotsilence = 0; /* did we timeout for silence? */
158  int maxduration = 0; /* max duration of recording in milliseconds */
159  int gottimeout = 0; /* did we timeout for maxduration exceeded? */
160  int terminator = '#';
161  int rfmt = 0;
162  int ioflags;
163  struct ast_silence_generator *silgen = NULL;
164  struct ast_flags flags = { 0, };
166  AST_APP_ARG(filename);
167  AST_APP_ARG(silence);
168  AST_APP_ARG(maxduration);
169  AST_APP_ARG(options);
170  );
171  int ms;
172  struct timeval start;
173 
174  /* The next few lines of code parse out the filename and header from the input string */
175  if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
176  ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
177  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
178  return -1;
179  }
180 
181  parse = ast_strdupa(data);
182  AST_STANDARD_APP_ARGS(args, parse);
183  if (args.argc == 4)
184  ast_app_parse_options(app_opts, &flags, opts, args.options);
185 
186  if (!ast_strlen_zero(args.filename)) {
187  if (strstr(args.filename, "%d"))
189  ext = strrchr(args.filename, '.'); /* to support filename with a . in the filename, not format */
190  if (!ext)
191  ext = strchr(args.filename, ':');
192  if (ext) {
193  *ext = '\0';
194  ext++;
195  }
196  }
197  if (!ext) {
198  ast_log(LOG_WARNING, "No extension specified to filename!\n");
199  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
200  return -1;
201  }
202  if (args.silence) {
203  if ((sscanf(args.silence, "%30d", &i) == 1) && (i > -1)) {
204  silence = i * 1000;
205  } else if (!ast_strlen_zero(args.silence)) {
206  ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", args.silence);
207  }
208  }
209 
210  if (args.maxduration) {
211  if ((sscanf(args.maxduration, "%30d", &i) == 1) && (i > -1))
212  /* Convert duration to milliseconds */
213  maxduration = i * 1000;
214  else if (!ast_strlen_zero(args.maxduration))
215  ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", args.maxduration);
216  }
217 
219  terminator = '*';
221  terminator = '\0';
222 
223  /* done parsing */
224 
225  /* these are to allow the use of the %d in the config file for a wild card of sort to
226  create a new file with the inputed name scheme */
227  if (ast_test_flag(&flags, FLAG_HAS_PERCENT)) {
228  AST_DECLARE_APP_ARGS(fname,
229  AST_APP_ARG(piece)[100];
230  );
231  char *tmp2 = ast_strdupa(args.filename);
232  char countstring[15];
233  int idx;
234 
235  /* Separate each piece out by the format specifier */
236  AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%');
237  do {
238  int tmplen;
239  /* First piece has no leading percent, so it's copied verbatim */
240  ast_copy_string(tmp, fname.piece[0], sizeof(tmp));
241  tmplen = strlen(tmp);
242  for (idx = 1; idx < fname.argc; idx++) {
243  if (fname.piece[idx][0] == 'd') {
244  /* Substitute the count */
245  snprintf(countstring, sizeof(countstring), "%d", count);
246  ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen);
247  tmplen += strlen(countstring);
248  } else if (tmplen + 2 < sizeof(tmp)) {
249  /* Unknown format specifier - just copy it verbatim */
250  tmp[tmplen++] = '%';
251  tmp[tmplen++] = fname.piece[idx][0];
252  }
253  /* Copy the remaining portion of the piece */
254  ast_copy_string(tmp + tmplen, &(fname.piece[idx][1]), sizeof(tmp) - tmplen);
255  }
256  count++;
257  } while (ast_fileexists(tmp, ext, chan->language) > 0);
258  pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
259  } else
260  ast_copy_string(tmp, args.filename, sizeof(tmp));
261  /* end of routine mentioned */
262 
263  if (chan->_state != AST_STATE_UP) {
264  if (ast_test_flag(&flags, OPTION_SKIP)) {
265  /* At the user's option, skip if the line is not up */
266  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "SKIP");
267  return 0;
268  } else if (!ast_test_flag(&flags, OPTION_NOANSWER)) {
269  /* Otherwise answer unless we're supposed to record while on-hook */
270  res = ast_answer(chan);
271  }
272  }
273 
274  if (res) {
275  ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
276  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
277  goto out;
278  }
279 
280  if (!ast_test_flag(&flags, OPTION_QUIET)) {
281  /* Some code to play a nice little beep to signify the start of the record operation */
282  res = ast_streamfile(chan, "beep", chan->language);
283  if (!res) {
284  res = ast_waitstream(chan, "");
285  } else {
286  ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
287  }
288  ast_stopstream(chan);
289  }
290 
291  /* The end of beep code. Now the recording starts */
292 
293  if (silence > 0) {
294  rfmt = chan->readformat;
296  if (res < 0) {
297  ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
298  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
299  return -1;
300  }
301  sildet = ast_dsp_new();
302  if (!sildet) {
303  ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
304  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
305  return -1;
306  }
308  }
309 
310  /* Create the directory if it does not exist. */
311  dir = ast_strdupa(tmp);
312  if ((file = strrchr(dir, '/')))
313  *file++ = '\0';
314  ast_mkdir (dir, 0777);
315 
316  ioflags = ast_test_flag(&flags, OPTION_APPEND) ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
317  s = ast_writefile(tmp, ext, NULL, ioflags, 0, AST_FILE_MODE);
318 
319  if (!s) {
320  ast_log(LOG_WARNING, "Could not create file %s\n", args.filename);
321  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
322  goto out;
323  }
324 
327 
328  /* Request a video update */
330 
331  if (maxduration <= 0)
332  maxduration = -1;
333 
334  start = ast_tvnow();
335  while ((ms = ast_remaining_ms(start, maxduration))) {
336  ms = ast_waitfor(chan, ms);
337  if (ms < 0) {
338  break;
339  }
340 
341  if (maxduration > 0 && ms == 0) {
342  break;
343  }
344 
345  f = ast_read(chan);
346  if (!f) {
347  res = -1;
348  break;
349  }
350  if (f->frametype == AST_FRAME_VOICE) {
351  res = ast_writestream(s, f);
352 
353  if (res) {
354  ast_log(LOG_WARNING, "Problem writing frame\n");
355  ast_frfree(f);
356  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
357  break;
358  }
359 
360  if (silence > 0) {
361  dspsilence = 0;
362  ast_dsp_silence(sildet, f, &dspsilence);
363  if (dspsilence) {
364  totalsilence = dspsilence;
365  } else {
366  totalsilence = 0;
367  }
368  if (totalsilence > silence) {
369  /* Ended happily with silence */
370  ast_frfree(f);
371  gotsilence = 1;
372  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "SILENCE");
373  break;
374  }
375  }
376  } else if (f->frametype == AST_FRAME_VIDEO) {
377  res = ast_writestream(s, f);
378 
379  if (res) {
380  ast_log(LOG_WARNING, "Problem writing frame\n");
381  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
382  ast_frfree(f);
383  break;
384  }
385  } else if ((f->frametype == AST_FRAME_DTMF) &&
386  ((f->subclass.integer == terminator) ||
387  (ast_test_flag(&flags, OPTION_ANY_TERMINATE)))) {
388  ast_frfree(f);
389  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "DTMF");
390  break;
391  }
392  ast_frfree(f);
393  }
394 
395  if (maxduration > 0 && !ms) {
396  gottimeout = 1;
397  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "TIMEOUT");
398  }
399 
400  if (!f) {
401  ast_debug(1, "Got hangup\n");
402  res = -1;
403  pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "HANGUP");
404  if (!ast_test_flag(&flags, OPTION_KEEP)) {
405  ast_filedelete(args.filename, NULL);
406  }
407  }
408 
409  if (gotsilence) {
410  ast_stream_rewind(s, silence - 1000);
411  ast_truncstream(s);
412  } else if (!gottimeout) {
413  /* Strip off the last 1/4 second of it */
414  ast_stream_rewind(s, 250);
415  ast_truncstream(s);
416  }
417  ast_closestream(s);
418 
419  if (silgen)
421 
422 out:
423  if ((silence > 0) && rfmt) {
424  res = ast_set_read_format(chan, rfmt);
425  if (res) {
426  ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
427  }
428  }
429 
430  if (sildet) {
431  ast_dsp_free(sildet);
432  }
433  return res;
434 }
union ast_frame_subclass subclass
Definition: frame.h:146
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:946
#define ast_test_flag(p, flag)
Definition: utils.h:63
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4393
void ast_dsp_free(struct ast_dsp *dsp)
Definition: dsp.c:1650
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define LOG_WARNING
Definition: logger.h:144
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: app.c:2101
struct ast_dsp * ast_dsp_new(void)
Definition: dsp.c:1607
#define AST_FRAME_DTMF
Definition: frame.h:128
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
unsigned int flags
Definition: utils.h:201
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4383
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:142
#define ast_opt_transmit_silence
Definition: options.h:120
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:931
const char * ext
Definition: http.c:112
#define AST_FILE_MODE
Definition: asterisk.h:36
int totalsilence
Definition: dsp.c:393
int ast_set_read_format(struct ast_channel *chan, format_t format)
Sets read format on channel chan Set read format for channel to whichever component of &quot;format&quot; is be...
Definition: channel.c:5301
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
Definition: dsp.c:390
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold)
Set threshold value for silence.
Definition: dsp.c:1655
struct ast_silence_generator * ast_channel_start_silence_generator(struct ast_channel *chan)
Starts a silence generator on the given channel.
Definition: channel.c:8309
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
static struct @350 args
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: utils.c:1615
int ast_stream_rewind(struct ast_filestream *fs, off_t ms)
Rewind stream ms.
Definition: file.c:899
enum ast_channel_state _state
Definition: channel.h:839
const ast_string_field name
Definition: channel.h:787
struct ast_filestream * ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
Starts writing a file.
Definition: file.c:1049
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_silence_generator *state)
Stops a previously-started silence generator on the given channel.
Definition: channel.c:8355
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1858
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:904
static struct ast_format f[]
Definition: format_g726.c:181
Structure used to handle boolean flags.
Definition: utils.h:200
int ast_truncstream(struct ast_filestream *fs)
Trunc stream at current location.
Definition: file.c:884
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Definition: pbx.c:10546
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
Return non-zero if this is silence. Updates &quot;totalsilence&quot; with the total number of seconds of silenc...
Definition: dsp.c:1355
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:100
#define AST_FORMAT_SLINEAR
Definition: frame.h:254
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3539
format_t readformat
Definition: channel.h:853
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
Writes a frame to a stream.
Definition: file.c:150
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1343
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:919
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:3086
Data structure associated with a single frame of data.
Definition: frame.h:142
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
enum ast_frame_type frametype
Definition: frame.h:144
#define ast_frfree(fr)
Definition: frame.h:583
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
static struct ast_app_option app_opts[128]
Definition: app_record.c:139
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the &#39;nonstandard&#39; argument separation process for an application.
Definition: app.h:619
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:1880
const ast_string_field language
Definition: channel.h:787
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:128
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2151
static int unload_module ( void  )
static

Definition at line 436 of file app_record.c.

References ast_unregister_application().

437 {
439 }
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
static char * app
Definition: app_record.c:116

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Trivial Record Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, }
static

Definition at line 446 of file app_record.c.

char* app = "Record"
static

Definition at line 116 of file app_record.c.

struct ast_app_option app_opts[128] = { [ 'a' ] = { .flag = OPTION_APPEND }, [ 'k' ] = { .flag = OPTION_KEEP }, [ 'n' ] = { .flag = OPTION_NOANSWER }, [ 'q' ] = { .flag = OPTION_QUIET }, [ 's' ] = { .flag = OPTION_SKIP }, [ 't' ] = { .flag = OPTION_STAR_TERMINATE }, [ 'y' ] = { .flag = OPTION_ANY_TERMINATE }, [ 'x' ] = { .flag = OPTION_IGNORE_TERMINATE }, }
static

Definition at line 139 of file app_record.c.

Referenced by record_exec().

Definition at line 446 of file app_record.c.