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