Improve app_mp3
[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 ms = -1;
77         int pid = -1;
78         int owriteformat;
79         struct timeval last;
80         struct ast_frame *f;
81         struct myframe {
82                 struct ast_frame f;
83                 char offset[AST_FRIENDLY_OFFSET];
84                 short frdata[160];
85         } myf;
86         last.tv_usec = 0;
87         last.tv_sec = 0;
88         if (!data) {
89                 ast_log(LOG_WARNING, "MP3 Playback requires an argument (filename)\n");
90                 return -1;
91         }
92         if (pipe(fds)) {
93                 ast_log(LOG_WARNING, "Unable to create pipe\n");
94                 return -1;
95         }
96         LOCAL_USER_ADD(u);
97         ast_stopstream(chan);
98
99         owriteformat = chan->writeformat;
100         res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
101         if (res < 0) {
102                 ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
103                 return -1;
104         }
105         
106         res = mp3play((char *)data, fds[1]);
107         /* Wait 1000 ms first */
108         ms = 1000;
109         if (res >= 0) {
110                 pid = res;
111                 /* Order is important -- there's almost always going to be mp3...  we want to prioritize the
112                    user */
113                 for (;;) {
114                         ms = ast_waitfor(chan, ms);
115                         if (ms < 0) {
116                                 ast_log(LOG_DEBUG, "Hangup detected\n");
117                                 res = -1;
118                                 break;
119                         }
120                         if (ms) {
121                                 f = ast_read(chan);
122                                 if (!f) {
123                                         ast_log(LOG_DEBUG, "Null frame == hangup() detected\n");
124                                         res = -1;
125                                         break;
126                                 }
127                                 if (f->frametype == AST_FRAME_DTMF) {
128                                         ast_log(LOG_DEBUG, "User pressed a key\n");
129                                         ast_frfree(f);
130                                         res = 0;
131                                         break;
132                                 }
133                                 ast_frfree(f);
134                         } else  {
135                                 res = read(fds[0], myf.frdata, sizeof(myf.frdata));
136                                 if (res > 0) {
137                                         myf.f.frametype = AST_FRAME_VOICE;
138                                         myf.f.subclass = AST_FORMAT_SLINEAR;
139                                         myf.f.datalen = res;
140                                         myf.f.samples = res / 2;
141                                         myf.f.mallocd = 0;
142                                         myf.f.offset = AST_FRIENDLY_OFFSET;
143                                         myf.f.src = __PRETTY_FUNCTION__;
144                                         myf.f.data = myf.frdata;
145                                         if (ast_write(chan, &myf.f) < 0) {
146                                                 res = -1;
147                                                 break;
148                                         }
149                                 } else {
150                                         ast_log(LOG_DEBUG, "No more mp3\n");
151                                         res = 0;
152                                         break;
153                                 }
154                                 ms = res / 16;
155                         }
156                 }
157         }
158         close(fds[0]);
159         close(fds[1]);
160         LOCAL_USER_REMOVE(u);
161         if (pid > -1)
162                 kill(pid, SIGKILL);
163         if (!res && owriteformat)
164                 ast_set_write_format(chan, owriteformat);
165         return res;
166 }
167
168 int unload_module(void)
169 {
170         STANDARD_HANGUP_LOCALUSERS;
171         return ast_unregister_application(app);
172 }
173
174 int load_module(void)
175 {
176         return ast_register_application(app, mp3_exec, synopsis, descrip);
177 }
178
179 char *description(void)
180 {
181         return tdesc;
182 }
183
184 int usecount(void)
185 {
186         int res;
187         STANDARD_USECOUNT(res);
188         return res;
189 }
190
191 char *key()
192 {
193         return ASTERISK_GPL_KEY;
194 }