app_ices.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * Stream to an icecast server via ICES (see contrib/asterisk-ices.xml)
  5. *
  6. * Copyright (C) 1999, Mark Spencer
  7. *
  8. * Mark Spencer <markster@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/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. #include <errno.h>
  29. #include "../astconf.h"
  30. #define ICES "/usr/bin/ices"
  31. #define LOCAL_ICES "/usr/local/bin/ices"
  32. static char *tdesc = "Encode and Stream via icecast and ices";
  33. static char *app = "ICES";
  34. static char *synopsis = "Encode and stream using 'ices'";
  35. static char *descrip =
  36. " ICES(config.xml) Streams to an icecast server using ices\n"
  37. "(available separately). A configuration file must be supplied\n"
  38. "for ices (see examples/asterisk-ices.conf). Returns -1 on\n"
  39. "hangup or 0 otherwise.\n";
  40. STANDARD_LOCAL_USER;
  41. LOCAL_USER_DECL;
  42. static int icesencode(char *filename, int fd)
  43. {
  44. int res;
  45. int x;
  46. res = fork();
  47. if (res < 0)
  48. ast_log(LOG_WARNING, "Fork failed\n");
  49. if (res)
  50. return res;
  51. dup2(fd, STDIN_FILENO);
  52. for (x=STDERR_FILENO + 1;x<256;x++) {
  53. if ((x != STDIN_FILENO) && (x != STDOUT_FILENO))
  54. close(x);
  55. }
  56. /* Most commonly installed in /usr/local/bin */
  57. execl(ICES, "ices", filename, (char *)NULL);
  58. /* But many places has it in /usr/bin */
  59. execl(LOCAL_ICES, "ices", filename, (char *)NULL);
  60. /* As a last-ditch effort, try to use PATH */
  61. execlp("ices", "ices", filename, (char *)NULL);
  62. ast_log(LOG_WARNING, "Execute of ices failed\n");
  63. return -1;
  64. }
  65. static int ices_exec(struct ast_channel *chan, void *data)
  66. {
  67. int res=0;
  68. struct localuser *u;
  69. int fds[2];
  70. int ms = -1;
  71. int pid = -1;
  72. int flags;
  73. int oreadformat;
  74. struct timeval last;
  75. struct ast_frame *f;
  76. char filename[256]="";
  77. char *c;
  78. last.tv_usec = 0;
  79. last.tv_sec = 0;
  80. if (!data || !strlen(data)) {
  81. ast_log(LOG_WARNING, "ICES requires an argument (configfile.xml)\n");
  82. return -1;
  83. }
  84. if (pipe(fds)) {
  85. ast_log(LOG_WARNING, "Unable to create pipe\n");
  86. return -1;
  87. }
  88. flags = fcntl(fds[1], F_GETFL);
  89. fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
  90. LOCAL_USER_ADD(u);
  91. ast_stopstream(chan);
  92. if (chan->_state != AST_STATE_UP)
  93. res = ast_answer(chan);
  94. if (res) {
  95. close(fds[0]);
  96. close(fds[1]);
  97. ast_log(LOG_WARNING, "Answer failed!\n");
  98. return -1;
  99. }
  100. oreadformat = chan->readformat;
  101. res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
  102. if (res < 0) {
  103. close(fds[0]);
  104. close(fds[1]);
  105. ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
  106. return -1;
  107. }
  108. if (((char *)data)[0] == '/')
  109. strncpy(filename, (char *)data, sizeof(filename) - 1);
  110. else
  111. snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, (char *)data);
  112. /* Placeholder for options */
  113. c = strchr(filename, '|');
  114. if (c)
  115. *c = '\0';
  116. res = icesencode(filename, fds[0]);
  117. close(fds[0]);
  118. if (res >= 0) {
  119. pid = res;
  120. for (;;) {
  121. /* Wait for audio, and stream */
  122. ms = ast_waitfor(chan, -1);
  123. if (ms < 0) {
  124. ast_log(LOG_DEBUG, "Hangup detected\n");
  125. res = -1;
  126. break;
  127. }
  128. f = ast_read(chan);
  129. if (!f) {
  130. ast_log(LOG_DEBUG, "Null frame == hangup() detected\n");
  131. res = -1;
  132. break;
  133. }
  134. if (f->frametype == AST_FRAME_VOICE) {
  135. res = write(fds[1], f->data, f->datalen);
  136. if (res < 0) {
  137. if (errno != EAGAIN) {
  138. ast_log(LOG_WARNING, "Write failed to pipe: %s\n", strerror(errno));
  139. res = -1;
  140. break;
  141. }
  142. }
  143. }
  144. ast_frfree(f);
  145. }
  146. }
  147. close(fds[1]);
  148. LOCAL_USER_REMOVE(u);
  149. if (pid > -1)
  150. kill(pid, SIGKILL);
  151. if (!res && oreadformat)
  152. ast_set_read_format(chan, oreadformat);
  153. return res;
  154. }
  155. int unload_module(void)
  156. {
  157. STANDARD_HANGUP_LOCALUSERS;
  158. return ast_unregister_application(app);
  159. }
  160. int load_module(void)
  161. {
  162. return ast_register_application(app, ices_exec, synopsis, descrip);
  163. }
  164. char *description(void)
  165. {
  166. return tdesc;
  167. }
  168. int usecount(void)
  169. {
  170. int res;
  171. STANDARD_USECOUNT(res);
  172. return res;
  173. }
  174. char *key()
  175. {
  176. return ASTERISK_GPL_KEY;
  177. }