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