app_mp3.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 1999 - 2005, Digium, Inc.
  5. *
  6. * Mark Spencer <markster@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. /*! \file
  19. *
  20. * \brief Silly application to play an MP3 file -- uses mpg123
  21. *
  22. * \author Mark Spencer <markster@digium.com>
  23. *
  24. * \ingroup applications
  25. */
  26. /*** MODULEINFO
  27. <depend>working_fork</depend>
  28. ***/
  29. #include "asterisk.h"
  30. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  31. #include <sys/time.h>
  32. #include <signal.h>
  33. #ifdef HAVE_CAP
  34. #include <sys/capability.h>
  35. #endif /* HAVE_CAP */
  36. #include "asterisk/lock.h"
  37. #include "asterisk/file.h"
  38. #include "asterisk/channel.h"
  39. #include "asterisk/frame.h"
  40. #include "asterisk/pbx.h"
  41. #include "asterisk/module.h"
  42. #include "asterisk/translate.h"
  43. #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
  44. #define MPG_123 "/usr/bin/mpg123"
  45. static char *app = "MP3Player";
  46. static char *synopsis = "Play an MP3 file or stream";
  47. static char *descrip =
  48. " MP3Player(location): Executes mpg123 to play the given location,\n"
  49. "which typically would be a filename or a URL. User can exit by pressing\n"
  50. "any key on the dialpad, or by hanging up.";
  51. static int mp3play(char *filename, int fd)
  52. {
  53. int res;
  54. int x;
  55. sigset_t fullset, oldset;
  56. #ifdef HAVE_CAP
  57. cap_t cap;
  58. #endif
  59. sigfillset(&fullset);
  60. pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
  61. res = fork();
  62. if (res < 0)
  63. ast_log(LOG_WARNING, "Fork failed\n");
  64. if (res) {
  65. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  66. return res;
  67. }
  68. #ifdef HAVE_CAP
  69. cap = cap_from_text("cap_net_admin-eip");
  70. if (cap_set_proc(cap)) {
  71. /* Careful with order! Logging cannot happen after we close FDs */
  72. ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
  73. }
  74. cap_free(cap);
  75. #endif
  76. if (ast_opt_high_priority)
  77. ast_set_priority(0);
  78. signal(SIGPIPE, SIG_DFL);
  79. pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
  80. dup2(fd, STDOUT_FILENO);
  81. for (x=STDERR_FILENO + 1;x<256;x++) {
  82. close(x);
  83. }
  84. /* Execute mpg123, but buffer if it's a net connection */
  85. if (!strncasecmp(filename, "http://", 7)) {
  86. /* Most commonly installed in /usr/local/bin */
  87. execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
  88. /* But many places has it in /usr/bin */
  89. execl(MPG_123, "mpg123", "-q", "-s", "-b", "1024","-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
  90. /* As a last-ditch effort, try to use PATH */
  91. execlp("mpg123", "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
  92. }
  93. else {
  94. /* Most commonly installed in /usr/local/bin */
  95. execl(MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
  96. /* But many places has it in /usr/bin */
  97. execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
  98. /* As a last-ditch effort, try to use PATH */
  99. execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
  100. }
  101. ast_log(LOG_WARNING, "Execute of mpg123 failed\n");
  102. _exit(0);
  103. }
  104. static int timed_read(int fd, void *data, int datalen, int timeout)
  105. {
  106. int res;
  107. struct pollfd fds[1];
  108. fds[0].fd = fd;
  109. fds[0].events = POLLIN;
  110. res = ast_poll(fds, 1, timeout);
  111. if (res < 1) {
  112. ast_log(LOG_NOTICE, "Poll timed out/errored out with %d\n", res);
  113. return -1;
  114. }
  115. return read(fd, data, datalen);
  116. }
  117. static int mp3_exec(struct ast_channel *chan, void *data)
  118. {
  119. int res=0;
  120. int fds[2];
  121. int ms = -1;
  122. int pid = -1;
  123. int owriteformat;
  124. int timeout = 2000;
  125. struct timeval next;
  126. struct ast_frame *f;
  127. struct myframe {
  128. struct ast_frame f;
  129. char offset[AST_FRIENDLY_OFFSET];
  130. short frdata[160];
  131. } myf = {
  132. .f = { 0, },
  133. };
  134. if (ast_strlen_zero(data)) {
  135. ast_log(LOG_WARNING, "MP3 Playback requires an argument (filename)\n");
  136. return -1;
  137. }
  138. if (pipe(fds)) {
  139. ast_log(LOG_WARNING, "Unable to create pipe\n");
  140. return -1;
  141. }
  142. ast_stopstream(chan);
  143. owriteformat = chan->writeformat;
  144. res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
  145. if (res < 0) {
  146. ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
  147. return -1;
  148. }
  149. res = mp3play((char *)data, fds[1]);
  150. if (!strncasecmp((char *)data, "http://", 7)) {
  151. timeout = 10000;
  152. }
  153. /* Wait 1000 ms first */
  154. next = ast_tvnow();
  155. next.tv_sec += 1;
  156. if (res >= 0) {
  157. pid = res;
  158. /* Order is important -- there's almost always going to be mp3... we want to prioritize the
  159. user */
  160. for (;;) {
  161. ms = ast_tvdiff_ms(next, ast_tvnow());
  162. if (ms <= 0) {
  163. res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata), timeout);
  164. if (res > 0) {
  165. myf.f.frametype = AST_FRAME_VOICE;
  166. myf.f.subclass = AST_FORMAT_SLINEAR;
  167. myf.f.datalen = res;
  168. myf.f.samples = res / 2;
  169. myf.f.mallocd = 0;
  170. myf.f.offset = AST_FRIENDLY_OFFSET;
  171. myf.f.src = __PRETTY_FUNCTION__;
  172. myf.f.delivery.tv_sec = 0;
  173. myf.f.delivery.tv_usec = 0;
  174. myf.f.data = myf.frdata;
  175. if (ast_write(chan, &myf.f) < 0) {
  176. res = -1;
  177. break;
  178. }
  179. } else {
  180. ast_debug(1, "No more mp3\n");
  181. res = 0;
  182. break;
  183. }
  184. next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
  185. } else {
  186. ms = ast_waitfor(chan, ms);
  187. if (ms < 0) {
  188. ast_debug(1, "Hangup detected\n");
  189. res = -1;
  190. break;
  191. }
  192. if (ms) {
  193. f = ast_read(chan);
  194. if (!f) {
  195. ast_debug(1, "Null frame == hangup() detected\n");
  196. res = -1;
  197. break;
  198. }
  199. if (f->frametype == AST_FRAME_DTMF) {
  200. ast_debug(1, "User pressed a key\n");
  201. ast_frfree(f);
  202. res = 0;
  203. break;
  204. }
  205. ast_frfree(f);
  206. }
  207. }
  208. }
  209. }
  210. close(fds[0]);
  211. close(fds[1]);
  212. if (pid > -1)
  213. kill(pid, SIGKILL);
  214. if (!res && owriteformat)
  215. ast_set_write_format(chan, owriteformat);
  216. return res;
  217. }
  218. static int unload_module(void)
  219. {
  220. return ast_unregister_application(app);
  221. }
  222. static int load_module(void)
  223. {
  224. return ast_register_application(app, mp3_exec, synopsis, descrip);
  225. }
  226. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly MP3 Application");