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