app_mp3.c 6.0 KB

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