00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "asterisk.h"
00033
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 182945 $")
00035
00036 #include <sys/time.h>
00037 #include <signal.h>
00038 #ifdef HAVE_CAP
00039 #include <sys/capability.h>
00040 #endif
00041
00042 #include "asterisk/lock.h"
00043 #include "asterisk/file.h"
00044 #include "asterisk/channel.h"
00045 #include "asterisk/frame.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/translate.h"
00049
00050 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
00051 #define MPG_123 "/usr/bin/mpg123"
00052
00053 static char *app = "MP3Player";
00054
00055 static char *synopsis = "Play an MP3 file or stream";
00056
00057 static char *descrip =
00058 " MP3Player(location): Executes mpg123 to play the given location,\n"
00059 "which typically would be a filename or a URL. User can exit by pressing\n"
00060 "any key on the dialpad, or by hanging up.";
00061
00062
00063 static int mp3play(char *filename, int fd)
00064 {
00065 int res;
00066 int x;
00067 sigset_t fullset, oldset;
00068 #ifdef HAVE_CAP
00069 cap_t cap;
00070 #endif
00071
00072 sigfillset(&fullset);
00073 pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
00074
00075 res = fork();
00076 if (res < 0)
00077 ast_log(LOG_WARNING, "Fork failed\n");
00078 if (res) {
00079 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
00080 return res;
00081 }
00082 #ifdef HAVE_CAP
00083 cap = cap_from_text("cap_net_admin-eip");
00084
00085 if (cap_set_proc(cap)) {
00086
00087 ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
00088 }
00089 cap_free(cap);
00090 #endif
00091 if (ast_opt_high_priority)
00092 ast_set_priority(0);
00093 signal(SIGPIPE, SIG_DFL);
00094 pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
00095
00096 dup2(fd, STDOUT_FILENO);
00097 for (x=STDERR_FILENO + 1;x<256;x++) {
00098 close(x);
00099 }
00100
00101 if (!strncasecmp(filename, "http://", 7)) {
00102
00103 execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
00104
00105 execl(MPG_123, "mpg123", "-q", "-s", "-b", "1024","-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
00106
00107 execlp("mpg123", "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
00108 }
00109 else {
00110
00111 execl(MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
00112
00113 execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
00114
00115 execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
00116 }
00117 ast_log(LOG_WARNING, "Execute of mpg123 failed\n");
00118 _exit(0);
00119 }
00120
00121 static int timed_read(int fd, void *data, int datalen, int timeout)
00122 {
00123 int res;
00124 struct pollfd fds[1];
00125 fds[0].fd = fd;
00126 fds[0].events = POLLIN;
00127 res = ast_poll(fds, 1, timeout);
00128 if (res < 1) {
00129 ast_log(LOG_NOTICE, "Poll timed out/errored out with %d\n", res);
00130 return -1;
00131 }
00132 return read(fd, data, datalen);
00133
00134 }
00135
00136 static int mp3_exec(struct ast_channel *chan, void *data)
00137 {
00138 int res=0;
00139 int fds[2];
00140 int ms = -1;
00141 int pid = -1;
00142 int owriteformat;
00143 int timeout = 2000;
00144 struct timeval next;
00145 struct ast_frame *f;
00146 struct myframe {
00147 struct ast_frame f;
00148 char offset[AST_FRIENDLY_OFFSET];
00149 short frdata[160];
00150 } myf;
00151
00152 if (ast_strlen_zero(data)) {
00153 ast_log(LOG_WARNING, "MP3 Playback requires an argument (filename)\n");
00154 return -1;
00155 }
00156
00157 if (pipe(fds)) {
00158 ast_log(LOG_WARNING, "Unable to create pipe\n");
00159 return -1;
00160 }
00161
00162 ast_stopstream(chan);
00163
00164 owriteformat = chan->writeformat;
00165 res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
00166 if (res < 0) {
00167 ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
00168 return -1;
00169 }
00170
00171 res = mp3play((char *)data, fds[1]);
00172 if (!strncasecmp((char *)data, "http://", 7)) {
00173 timeout = 10000;
00174 }
00175
00176 next = ast_tvnow();
00177 next.tv_sec += 1;
00178 if (res >= 0) {
00179 pid = res;
00180
00181
00182 for (;;) {
00183 ms = ast_tvdiff_ms(next, ast_tvnow());
00184 if (ms <= 0) {
00185 res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata), timeout);
00186 if (res > 0) {
00187 myf.f.frametype = AST_FRAME_VOICE;
00188 myf.f.subclass = AST_FORMAT_SLINEAR;
00189 myf.f.datalen = res;
00190 myf.f.samples = res / 2;
00191 myf.f.mallocd = 0;
00192 myf.f.offset = AST_FRIENDLY_OFFSET;
00193 myf.f.src = __PRETTY_FUNCTION__;
00194 myf.f.delivery.tv_sec = 0;
00195 myf.f.delivery.tv_usec = 0;
00196 myf.f.data = myf.frdata;
00197 if (ast_write(chan, &myf.f) < 0) {
00198 res = -1;
00199 break;
00200 }
00201 } else {
00202 ast_debug(1, "No more mp3\n");
00203 res = 0;
00204 break;
00205 }
00206 next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
00207 } else {
00208 ms = ast_waitfor(chan, ms);
00209 if (ms < 0) {
00210 ast_debug(1, "Hangup detected\n");
00211 res = -1;
00212 break;
00213 }
00214 if (ms) {
00215 f = ast_read(chan);
00216 if (!f) {
00217 ast_debug(1, "Null frame == hangup() detected\n");
00218 res = -1;
00219 break;
00220 }
00221 if (f->frametype == AST_FRAME_DTMF) {
00222 ast_debug(1, "User pressed a key\n");
00223 ast_frfree(f);
00224 res = 0;
00225 break;
00226 }
00227 ast_frfree(f);
00228 }
00229 }
00230 }
00231 }
00232 close(fds[0]);
00233 close(fds[1]);
00234
00235 if (pid > -1)
00236 kill(pid, SIGKILL);
00237 if (!res && owriteformat)
00238 ast_set_write_format(chan, owriteformat);
00239
00240 return res;
00241 }
00242
00243 static int unload_module(void)
00244 {
00245 return ast_unregister_application(app);
00246 }
00247
00248 static int load_module(void)
00249 {
00250 return ast_register_application(app, mp3_exec, synopsis, descrip);
00251 }
00252
00253 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly MP3 Application");