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