app_record.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * Trivial application to record a sound file
  5. *
  6. * Copyright (C) 2001, Linux Support Services, Inc.
  7. *
  8. * Matthew Fredrickson <creslin@linux-support.net>
  9. *
  10. * This program is free software, distributed under the terms of
  11. * the GNU General Public License
  12. */
  13. #include <asterisk/lock.h>
  14. #include <asterisk/file.h>
  15. #include <asterisk/logger.h>
  16. #include <asterisk/channel.h>
  17. #include <asterisk/pbx.h>
  18. #include <asterisk/module.h>
  19. #include <asterisk/translate.h>
  20. #include <asterisk/dsp.h>
  21. #include <string.h>
  22. #include <stdlib.h>
  23. static char *tdesc = "Trivial Record Application";
  24. static char *app = "Record";
  25. static char *synopsis = "Record to a file";
  26. static char *descrip =
  27. " Record(filename:format|silence[|maxduration][|option])\n\n"
  28. "Records from the channel into a given filename. If the file exists it will\n"
  29. "be overwritten.\n"
  30. "- 'format' is the format of the file type to be recorded (wav, gsm, etc).\n"
  31. "- 'silence' is the number of seconds of silence to allow before returning.\n"
  32. "- 'maxduration' is the maximum recording duration in seconds. If missing\n"
  33. "or 0 there is no maximum.\n"
  34. "- 'option' may be 'skip' to return immediately if the line is not up,\n"
  35. "or 'noanswer' to attempt to record even if the line is not up.\n\n"
  36. "If filename contains '%d', these characters will be replaced with a number\n"
  37. "incremented by one each time the file is recorded. \n\n"
  38. "Formats: g723, g729, gsm, h263, ulaw, alaw, vox, wav, WAV\n\n"
  39. "User can press '#' to terminate the recording and continue to the next priority.\n\n"
  40. "Returns -1 when the user hangs up.\n";
  41. STANDARD_LOCAL_USER;
  42. LOCAL_USER_DECL;
  43. static int record_exec(struct ast_channel *chan, void *data)
  44. {
  45. int res = 0;
  46. int count = 0;
  47. int percentflag = 0;
  48. char fil[256];
  49. char tmp[256];
  50. char ext[10];
  51. char *vdata;
  52. int i = 0;
  53. int j = 0;
  54. struct ast_filestream *s = '\0';
  55. struct localuser *u;
  56. struct ast_frame *f = NULL;
  57. struct ast_dsp *sildet = NULL; /* silence detector dsp */
  58. int totalsilence = 0;
  59. int dspsilence = 0;
  60. int silence = 0; /* amount of silence to allow */
  61. int gotsilence = 0; /* did we timeout for silence? */
  62. char silencestr[5];
  63. char durationstr[8];
  64. int maxduration = 0; /* max duration of recording */
  65. int gottimeout = 0; /* did we timeout for maxduration exceeded? */
  66. time_t timeout = 0;
  67. char option[16];
  68. int option_skip = 0;
  69. int option_noanswer = 0;
  70. int rfmt = 0;
  71. int flags;
  72. char *end=NULL;
  73. char *p=NULL;
  74. /* The next few lines of code parse out the filename and header from the input string */
  75. if (!data) { /* no data implies no filename or anything is present */
  76. ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
  77. return -1;
  78. }
  79. vdata = ast_strdupa(data);
  80. p = vdata;
  81. while(p && (p=strchr(p,':'))) {
  82. end=p;
  83. if(!strcasecmp(end,":end")) {
  84. *end='\0';
  85. end++;
  86. break;
  87. }
  88. p++;
  89. end=NULL;
  90. }
  91. for (; vdata[i] && (vdata[i] != ':') && (vdata[i] != '|'); i++ ) {
  92. if ((vdata[i] == '%') && (vdata[i+1] == 'd')) {
  93. percentflag = 1; /* the wildcard is used */
  94. }
  95. if (j < sizeof(fil) - 1)
  96. fil[j++] = vdata[i];
  97. }
  98. fil[j] = '\0';
  99. if (vdata[i] != ':') {
  100. ast_log(LOG_WARNING, "No extension found\n");
  101. return -1;
  102. }
  103. i++;
  104. j = 0;
  105. for (; vdata[i] && (vdata[i] != '|'); i++)
  106. if (j < sizeof(ext) - 1)
  107. ext[j++] = vdata[i];
  108. ext[j] = '\0';
  109. if (vdata[i] == '|')
  110. i++;
  111. j = 0;
  112. for (; vdata[i] && (vdata[i] != '|'); i++)
  113. if (j < sizeof(silencestr) - 1)
  114. silencestr[j++] = vdata[i];
  115. silencestr[j] = '\0';
  116. if (j > 0) {
  117. silence = atoi(silencestr);
  118. if (silence > 0)
  119. silence *= 1000;
  120. }
  121. if (vdata[i] == '|')
  122. i++;
  123. j = 0;
  124. for (; vdata[i] && (vdata[i] != '|'); i++)
  125. if (j < sizeof(durationstr) - 1)
  126. durationstr[j++] = vdata[i];
  127. durationstr[j] = '\0';
  128. if (j > 0)
  129. maxduration = atoi(durationstr);
  130. if (vdata[i] == '|')
  131. i++;
  132. j = 0;
  133. for (; vdata[i] && (vdata[i] != '|'); i++)
  134. if (j < sizeof(option) - 1)
  135. option[j++] = vdata[i];
  136. option[j] = '\0';
  137. if (!strcasecmp(option, "skip"))
  138. option_skip = 1;
  139. if (!strcasecmp(option, "noanswer"))
  140. option_noanswer = 1;
  141. /* done parsing */
  142. /* these are to allow the use of the %d in the config file for a wild card of sort to
  143. create a new file with the inputed name scheme */
  144. if (percentflag) {
  145. do {
  146. snprintf(tmp, sizeof(tmp), fil, count);
  147. count++;
  148. } while ( ast_fileexists(tmp, ext, chan->language) != -1 );
  149. pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
  150. } else
  151. strncpy(tmp, fil, sizeof(tmp)-1);
  152. /* end of routine mentioned */
  153. LOCAL_USER_ADD(u);
  154. if (chan->_state != AST_STATE_UP) {
  155. if (option_skip) {
  156. /* At the user's option, skip if the line is not up */
  157. LOCAL_USER_REMOVE(u);
  158. return 0;
  159. } else if (!option_noanswer) {
  160. /* Otherwise answer unless we're supposed to record while on-hook */
  161. res = ast_answer(chan);
  162. }
  163. }
  164. if (!res) {
  165. /* Some code to play a nice little beep to signify the start of the record operation */
  166. res = ast_streamfile(chan, "beep", chan->language);
  167. if (!res) {
  168. res = ast_waitstream(chan, "");
  169. } else {
  170. ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
  171. }
  172. ast_stopstream(chan);
  173. /* The end of beep code. Now the recording starts */
  174. if (silence > 0) {
  175. rfmt = chan->readformat;
  176. res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
  177. if (res < 0) {
  178. ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
  179. return -1;
  180. }
  181. sildet = ast_dsp_new();
  182. if (!sildet) {
  183. ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
  184. return -1;
  185. }
  186. ast_dsp_set_threshold(sildet, 256);
  187. }
  188. flags = end ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
  189. s = ast_writefile( tmp, ext, NULL, flags , 0, 0644);
  190. if (s) {
  191. if (maxduration > 0)
  192. timeout = time(NULL) + (time_t)maxduration;
  193. while (ast_waitfor(chan, -1) > -1) {
  194. if (maxduration > 0 && time(NULL) > timeout) {
  195. gottimeout = 1;
  196. break;
  197. }
  198. f = ast_read(chan);
  199. if (!f) {
  200. res = -1;
  201. break;
  202. }
  203. if (f->frametype == AST_FRAME_VOICE) {
  204. res = ast_writestream(s, f);
  205. if (res) {
  206. ast_log(LOG_WARNING, "Problem writing frame\n");
  207. break;
  208. }
  209. if (silence > 0) {
  210. dspsilence = 0;
  211. ast_dsp_silence(sildet, f, &dspsilence);
  212. if (dspsilence) {
  213. totalsilence = dspsilence;
  214. } else {
  215. totalsilence = 0;
  216. }
  217. if (totalsilence > silence) {
  218. /* Ended happily with silence */
  219. ast_frfree(f);
  220. gotsilence = 1;
  221. break;
  222. }
  223. }
  224. }
  225. if (f->frametype == AST_FRAME_VIDEO) {
  226. res = ast_writestream(s, f);
  227. if (res) {
  228. ast_log(LOG_WARNING, "Problem writing frame\n");
  229. break;
  230. }
  231. }
  232. if ((f->frametype == AST_FRAME_DTMF) &&
  233. (f->subclass == '#')) {
  234. ast_frfree(f);
  235. break;
  236. }
  237. ast_frfree(f);
  238. }
  239. if (!f) {
  240. ast_log(LOG_DEBUG, "Got hangup\n");
  241. res = -1;
  242. }
  243. if (gotsilence) {
  244. ast_stream_rewind(s, silence-1000);
  245. ast_truncstream(s);
  246. } else if (!gottimeout) {
  247. /* Strip off the last 1/4 second of it */
  248. ast_stream_rewind(s, 250);
  249. ast_truncstream(s);
  250. }
  251. ast_closestream(s);
  252. } else
  253. ast_log(LOG_WARNING, "Could not create file %s\n", fil);
  254. } else
  255. ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
  256. LOCAL_USER_REMOVE(u);
  257. if ((silence > 0) && rfmt) {
  258. res = ast_set_read_format(chan, rfmt);
  259. if (res)
  260. ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
  261. if (sildet)
  262. ast_dsp_free(sildet);
  263. }
  264. return res;
  265. }
  266. int unload_module(void)
  267. {
  268. STANDARD_HANGUP_LOCALUSERS;
  269. return ast_unregister_application(app);
  270. }
  271. int load_module(void)
  272. {
  273. return ast_register_application(app, record_exec, synopsis, descrip);
  274. }
  275. char *description(void)
  276. {
  277. return tdesc;
  278. }
  279. int usecount(void)
  280. {
  281. int res;
  282. STANDARD_USECOUNT(res);
  283. return res;
  284. }
  285. char *key()
  286. {
  287. return ASTERISK_GPL_KEY;
  288. }