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