fixes bug that causes mp3 stream to hang is server is not fast enough
[oweals/openwrt.git] / package / fonera-mp3 / src / lib / mp3_stream.c
1 /*
2 * FOXMP3 
3 * Copyright (c) 2006 acmesystems.it - john@acmesystems.it
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
18 *
19 * Feedback, Bugs...  info@acmesystems.it
20 *
21 */ 
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <netdb.h>
30 #include <sys/types.h>
31 #include <netinet/in.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <sys/time.h>
35 #include <sys/poll.h>
36
37 #include "mp3.h"
38
39 typedef struct _MP3_STREAM {
40         unsigned char buf[MAX_PACKET_SIZE + 1];
41         int sockfd;
42         unsigned char mp3_buffer[MAX_BUFFER_SIZE];
43         unsigned long int mp3_buffer_write_pos;
44         unsigned long int mp3_buffer_read_pos;
45         unsigned long int mp3_data_in_buffer;
46         unsigned char transmit_success;
47         unsigned int buffer_error;
48         MP3_DATA mp3_data;
49         unsigned int numbytes;
50         unsigned int metainterval;
51 } MP3_STREAM;
52
53 static MP3_STREAM mp3_stream;
54
55 int connect_timeout (int sfd, struct sockaddr *addr, int addrlen, 
56                         struct timeval *timeout) {
57         struct timeval sv;
58         int svlen = sizeof sv;
59         int ret;
60
61         if (!timeout) {
62                 return connect (sfd, addr, addrlen);
63         };
64         if (getsockopt (sfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&sv, &svlen) < 0) {
65                 return -1;
66         };
67         if (setsockopt (sfd, SOL_SOCKET, SO_SNDTIMEO, timeout,sizeof *timeout) < 0) {
68                 return -1;
69         };
70         ret = connect (sfd, addr, addrlen);
71         setsockopt (sfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&sv, sizeof sv);
72             
73         return ret;
74 }
75
76 int mp3_stream_parse_url(unsigned char *url, unsigned char *ip, 
77         unsigned char *path, unsigned int *port){
78         int len = strlen(url) - 1;
79         while(((url[len] == '\n')||(url[len] == ' ')) && (len > 0)){
80                 url[len] = '\0';
81                 len--;
82         };
83         ip[0] = '\0';
84         printf("Parsing stream url : %s\n", url);
85         unsigned char *http = strstr(url, "http://");
86         *port = 80;
87         if(http){
88                 url = http + 7; 
89                 unsigned char *p = strstr(url, ":");
90                 if(p){
91                         *p = '\0';
92                         p ++;
93                         strcpy(ip, url);
94                         *port = atoi(p);
95                 }
96                 unsigned char *p2 = strstr((p)?(p):(url), "/");
97                 if(p2){
98                         strcpy(path, p2);
99                         *p2 = '\0';
100                         if(!p){
101                                 strcpy(ip, url);
102                         }
103
104                 } else {
105                         strcpy(path, "/");
106                 };
107                 printf("ip   -> %s\nport -> %d\npath -> %s\n", ip, *port, path);
108                 return MP3_OK;
109         };
110         return MP3_ERROR;
111 };
112
113 int mp3_stream_get_url(unsigned char *url, unsigned int type, 
114                 unsigned char *ip, unsigned int *port, unsigned char *path){
115         if(type == STREAM_PLS){
116                 if(mp3_pls_get_info(url, ip, path, port) == MP3_OK){
117                         return MP3_OK;
118                 };
119         } else if(type == STREAM_URL){
120                 if(mp3_stream_parse_url(url, ip, path, port) == MP3_OK){
121                         return MP3_OK;
122                 }; 
123         };
124         return MP3_ERROR;
125 };
126
127 int mp3_stream_setup(unsigned char *url, unsigned int type, unsigned char *ip, 
128                 unsigned char *path, unsigned int *port){
129         struct hostent *he;
130         struct sockaddr_in their_addr; 
131         unsigned int error = 0;
132         if(mp3_stream_get_url(url, type, ip, port, path) == MP3_ERROR){
133                 return MP3_ERROR;
134         };
135         
136         mp3_stream.mp3_buffer_write_pos = 0;
137         mp3_stream.mp3_buffer_read_pos = 0;
138         mp3_stream.mp3_data_in_buffer = 0;
139         mp3_stream.transmit_success = 1;
140         mp3_stream.buffer_error = 0;
141         mp3_stream.metainterval = 0;
142
143         mp3_reset();
144         
145         if ((he=gethostbyname(ip)) == NULL) { 
146                 perror("Error in gethostbyname. Wrong url/ip ?");
147                 return MP3_ERROR;
148         }
149         if ((mp3_stream.sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
150                 perror("Error opening stream socket");
151                 return MP3_ERROR;
152         }
153         
154         their_addr.sin_family = AF_INET; 
155         their_addr.sin_port = htons(*port); 
156         their_addr.sin_addr = *((struct in_addr *)he->h_addr);
157         memset(&(their_addr.sin_zero), '\0', 8);
158         
159         struct timeval tv;
160         tv.tv_sec = 4;
161         tv.tv_usec = 0;
162         
163         if (connect_timeout(mp3_stream.sockfd, (struct sockaddr *)&their_addr, 
164                                 sizeof(struct sockaddr), &tv) == -1) {
165                 perror("connect");
166                 return MP3_ERROR;
167         }
168         
169         unsigned char icy_request[1024];
170         sprintf(icy_request, 
171                         "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: A.LP-MP3\r\nAccept: */*\r\nicy-metadata:0\r\n\r\n", 
172                         path, 
173                         ip);
174         printf("Sending request :\n%s\n", icy_request);
175         send(mp3_stream.sockfd, icy_request, strlen(icy_request), 0);
176         mp3_stream.numbytes = 0;
177         while(mp3_stream.numbytes < MAX_PACKET_SIZE-1) {
178                 if ((mp3_stream.numbytes += recv(mp3_stream.sockfd, &mp3_stream.buf[mp3_stream.numbytes], MAX_PACKET_SIZE - 1 - mp3_stream.numbytes, 0)) == -1) {
179                         perror("recv");
180                         return MP3_ERROR;
181                 }
182         }
183         mp3_stream.buf[mp3_stream.numbytes] = '\0';
184         printf("numbytes = %d\n", mp3_stream.numbytes);
185         unsigned char *p = strstr(mp3_stream.buf, "\r\n\r\n");
186         if(p) {
187                 *p = '\0';
188                 p += 4;
189         } else {
190                 printf("funky p error in stream.c\n");
191         }
192         printf("Received: \n%s\n", mp3_stream.buf);
193         if(((unsigned char*)strstr(mp3_stream.buf, "ICY 200 OK") != mp3_stream.buf) && 
194                 ((unsigned char*)strstr(mp3_stream.buf, "HTTP/1.1 200 OK") != mp3_stream.buf) &&
195                 ((unsigned char*)strstr(mp3_stream.buf, "HTTP/1.0 200 OK") != mp3_stream.buf)) {
196                 return MP3_ERROR;
197         };
198         int p_buf = p - mp3_stream.buf; 
199         unsigned char *p2;
200         p2 = strstr(mp3_stream.buf, "icy-metaint:");
201         if(p2){
202                 p2 = strstr(p2, ":");
203                 p2++;
204                 unsigned char *p3 = strstr(p2, "\r");
205                 *p3 = '\0';
206                 mp3_stream.metainterval = atoi(p2);
207                 printf("META INT == %d\n", mp3_stream.metainterval);
208         }
209         
210         printf("starting to buffer\n");
211         memcpy(&mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_write_pos], 
212                                 p, p_buf);
213         mp3_stream.mp3_buffer_write_pos += p_buf;
214         mp3_stream.mp3_data_in_buffer += p_buf;
215
216         while(mp3_stream.mp3_data_in_buffer + (unsigned long int)MAX_PACKET_SIZE 
217                         < (unsigned long int)MAX_BUFFER_SIZE){
218                 if ((mp3_stream.numbytes=recv(mp3_stream.sockfd, mp3_stream.buf, 
219                                                 MAX_PACKET_SIZE-1, 0)) == -1) {
220                         perror("disconnected");
221                         printf("disconntected\n");
222                         return MP3_ERROR;
223                 }
224
225                 if(mp3_stream.numbytes == 0){
226                         sleep(1);
227                         if(++error > 3){
228                                 perror("disconnected");
229                                 printf("disconntected\n");
230                                 return MP3_ERROR;
231                         }                       
232                 }
233
234                 memcpy(&mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_write_pos], 
235                                 mp3_stream.buf, mp3_stream.numbytes);
236                 mp3_stream.mp3_buffer_write_pos += mp3_stream.numbytes;
237                 mp3_stream.mp3_data_in_buffer += mp3_stream.numbytes;
238                 printf("%ld  ", mp3_stream.mp3_data_in_buffer);
239                 fflush(stdout);
240                 
241         };
242         printf("\n");
243         mp3_stream.mp3_data.state = MP3_PLAYING;
244         while(mp3_stream.mp3_data_in_buffer >= 2 * MP3_CHUNK_SIZE){
245                 memcpy(mp3_stream.mp3_data.mp3, 
246                                 &mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_read_pos], 
247                                 MP3_CHUNK_SIZE);
248                 mp3_send_data_to_buffer(mp3_stream.mp3_data);
249                 mp3_stream.mp3_buffer_read_pos += MP3_CHUNK_SIZE;
250                 mp3_stream.mp3_data_in_buffer -= MP3_CHUNK_SIZE;
251         };
252
253         printf("Starting to play stream\n");
254         return MP3_OK;
255 }
256         
257 static int max_recv_errors = 10;
258 int mp3_stream_handle(void){
259         if(MAX_BUFFER_SIZE >= mp3_stream.mp3_data_in_buffer + MAX_PACKET_SIZE){
260                 struct pollfd ufds;
261                 ufds.fd = mp3_stream.sockfd;
262                 ufds.events = POLLIN|POLLHUP;
263                 
264                 if(poll(&ufds, 1, 2000) > 0){
265                         max_recv_errors = 10;
266                         if ((mp3_stream.numbytes=recv(mp3_stream.sockfd, mp3_stream.buf, MAX_PACKET_SIZE-1, 0)) == -1) {
267                                 perror("recv");
268                         }
269                         if((mp3_stream.numbytes != EAGAIN)&& (mp3_stream.numbytes != -1)){
270                                 if(mp3_stream.mp3_buffer_write_pos + mp3_stream.numbytes <= MAX_BUFFER_SIZE){
271                                         memcpy(&mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_write_pos],
272                                                         mp3_stream.buf, mp3_stream.numbytes);
273                                         mp3_stream.mp3_buffer_write_pos += mp3_stream.numbytes;
274                                         mp3_stream.mp3_data_in_buffer += mp3_stream.numbytes;
275                                         if(mp3_stream.mp3_buffer_write_pos == MAX_BUFFER_SIZE){
276                                                 mp3_stream.mp3_buffer_write_pos = 0;
277                                         };
278                                 } else {
279                                         unsigned int buffer_offset = MAX_BUFFER_SIZE - mp3_stream.mp3_buffer_write_pos;
280                                         memcpy(&mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_write_pos], 
281                                                         mp3_stream.buf, buffer_offset);
282                                         mp3_stream.mp3_buffer_write_pos = 
283                                                         mp3_stream.numbytes - buffer_offset;
284                                         memcpy(&mp3_stream.mp3_buffer[0], &mp3_stream.buf[buffer_offset],
285                                                         mp3_stream.mp3_buffer_write_pos);
286                                         mp3_stream.mp3_data_in_buffer += mp3_stream.numbytes;
287                                 };
288                         };
289                 } else {
290                         max_recv_errors--;
291                         if(max_recv_errors == 0){
292                                 printf("recv error\n");
293                                 return MP3_ERROR;
294                         };
295                 };
296         } 
297         
298         if(mp3_stream.mp3_data_in_buffer < MP3_CHUNK_SIZE){
299                 printf("radio_buffer is empty\n");
300                 mp3_stream.buffer_error ++;
301                 if(mp3_stream.buffer_error > MAX_BUFFER_ERROR){
302                         return MP3_ERROR;
303                 };                      
304         } else {
305                 mp3_stream.buffer_error = 0;
306                 do{
307                         if(mp3_stream.transmit_success){
308                                 if(MAX_BUFFER_SIZE >= mp3_stream.mp3_buffer_read_pos + MP3_CHUNK_SIZE){
309                                         memcpy(mp3_stream.mp3_data.mp3, 
310                                                 &mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_read_pos], MP3_CHUNK_SIZE);
311                                         mp3_stream.mp3_buffer_read_pos += MP3_CHUNK_SIZE;
312                                         mp3_stream.mp3_data_in_buffer -= MP3_CHUNK_SIZE;
313                                         if(mp3_stream.mp3_buffer_read_pos == MAX_BUFFER_SIZE){
314                                                 mp3_stream.mp3_buffer_read_pos = 0;
315                                         };
316                                         
317                                 } else {
318                                         unsigned int buffer_offset = MAX_BUFFER_SIZE - mp3_stream.mp3_buffer_read_pos;
319                                         memcpy(mp3_stream.mp3_data.mp3, 
320                                                         &mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_read_pos],
321                                                         buffer_offset);
322                                         mp3_stream.mp3_buffer_read_pos = MP3_CHUNK_SIZE - buffer_offset;
323                                         memcpy(&mp3_stream.mp3_data.mp3[buffer_offset], mp3_stream.mp3_buffer, 
324                                                         mp3_stream.mp3_buffer_read_pos);
325                                 };
326                         } 
327                         if(!mp3_send_data_to_buffer(mp3_stream.mp3_data)){
328                                 mp3_stream.transmit_success = 0;
329                         } else {
330                                 mp3_stream.transmit_success = 1;
331                         };      
332                 } while((mp3_stream.transmit_success)&&(mp3_stream.mp3_data_in_buffer > MP3_CHUNK_SIZE));
333         };
334         return MP3_OK;
335 };
336         
337 int mp3_stream_cleanup(void){
338         close(mp3_stream.sockfd);
339         return MP3_OK;
340 }