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