Version 0.1.12 from FTP
[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 mp3_exec(struct ast_channel *chan, void *data)
72 {
73         int res=0;
74         struct localuser *u;
75         int fds[2];
76         int rfds[1 + AST_MAX_FDS];
77         int ms = -1;
78         int pid = -1;
79         int us;
80         int exception;
81         int owriteformat;
82         struct timeval tv;
83         struct timeval last;
84         struct ast_frame *f;
85         int x;
86         struct myframe {
87                 struct ast_frame f;
88                 char offset[AST_FRIENDLY_OFFSET];
89                 char frdata[160];
90         } myf;
91         last.tv_usec = 0;
92         last.tv_sec = 0;
93         if (!data) {
94                 ast_log(LOG_WARNING, "MP3 Playback requires an argument (filename)\n");
95                 return -1;
96         }
97         if (pipe(fds)) {
98                 ast_log(LOG_WARNING, "Unable to create pipe\n");
99                 return -1;
100         }
101         LOCAL_USER_ADD(u);
102         ast_stopstream(chan);
103
104         owriteformat = chan->writeformat;
105         res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
106         if (res < 0) {
107                 ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
108                 return -1;
109         }
110         
111         res = mp3play((char *)data, fds[1]);
112         if (res >= 0) {
113                 pid = res;
114                 /* Order is important -- there's almost always going to be mp3...  we want to prioritize the
115                    user */
116                 rfds[AST_MAX_FDS] = fds[0];
117                 for (;;) {
118                         CHECK_BLOCKING(chan);
119                         for (x=0;x<AST_MAX_FDS;x++) 
120                                 rfds[x] = chan->fds[x];
121                         res = ast_waitfor_n_fd(rfds, AST_MAX_FDS+1, &ms, &exception);
122                         chan->blocking = 0;
123                         if (res < 1) {
124                                 ast_log(LOG_DEBUG, "Hangup detected\n");
125                                 res = -1;
126                                 break;
127                         }
128                         for(x=0;x<AST_MAX_FDS;x++) 
129                                 if (res == chan->fds[x])
130                                         break;
131
132                         if (x < AST_MAX_FDS) {
133                                 if (exception)
134                                         chan->exception = 1;
135                                 f = ast_read(chan);
136                                 if (!f) {
137                                         ast_log(LOG_DEBUG, "Null frame == hangup() detected\n");
138                                         res = -1;
139                                         break;
140                                 }
141                                 if (f->frametype == AST_FRAME_DTMF) {
142                                         ast_log(LOG_DEBUG, "User pressed a key\n");
143                                         ast_frfree(f);
144                                         res = 0;
145                                         break;
146                                 }
147                                 ast_frfree(f);
148                         } else if (res == fds[0]) {
149                                 gettimeofday(&tv, NULL);
150                                 if (last.tv_sec || last.tv_usec) {
151                                         /* We should wait at least a frame length */
152                                         us = sizeof(myf.frdata) / 16 * 1000;
153                                         /* Subtract 1,000,000 us for each second late we've passed */
154                                         us -= (tv.tv_sec - last.tv_sec) * 1000000;
155                                         /* And one for each us late we've passed */
156                                         us -= (tv.tv_usec - last.tv_usec);
157                                         /* Sleep that long if needed */
158                                         if (us > 0)
159                                                 usleep(us);
160                                 }
161                                 last = tv;
162                                 res = read(fds[0], myf.frdata, sizeof(myf.frdata));
163                                 if (res > 0) {
164                                         myf.f.frametype = AST_FRAME_VOICE;
165                                         myf.f.subclass = AST_FORMAT_SLINEAR;
166                                         myf.f.datalen = res;
167                                         myf.f.timelen = res / 16;
168                                         myf.f.mallocd = 0;
169                                         myf.f.offset = AST_FRIENDLY_OFFSET;
170                                         myf.f.src = __PRETTY_FUNCTION__;
171                                         myf.f.data = myf.frdata;
172                                         if (ast_write(chan, &myf.f) < 0) {
173                                                 res = -1;
174                                                 break;
175                                         }
176                                 } else {
177                                         ast_log(LOG_DEBUG, "No more mp3\n");
178                                         res = 0;
179                                 }
180                         } else {
181                                 ast_log(LOG_DEBUG, "HuhHHH?\n");
182                                 res = -1;
183                                 break;
184                         }
185                 }
186         }
187         close(fds[0]);
188         close(fds[1]);
189         LOCAL_USER_REMOVE(u);
190         if (pid > -1)
191                 kill(pid, SIGKILL);
192         if (!res && owriteformat)
193                 ast_set_write_format(chan, owriteformat);
194         return res;
195 }
196
197 int unload_module(void)
198 {
199         STANDARD_HANGUP_LOCALUSERS;
200         return ast_unregister_application(app);
201 }
202
203 int load_module(void)
204 {
205         return ast_register_application(app, mp3_exec, synopsis, descrip);
206 }
207
208 char *description(void)
209 {
210         return tdesc;
211 }
212
213 int usecount(void)
214 {
215         int res;
216         STANDARD_USECOUNT(res);
217         return res;
218 }
219
220 char *key()
221 {
222         return ASTERISK_GPL_KEY;
223 }