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