61
|
1 /* BMP - Cross-platform multimedia player
|
|
2 * Copyright (C) 2003-2004 BMP development team.
|
|
3 *
|
|
4 * Based on XMMS:
|
|
5 * Copyright (C) 1998-2003 XMMS development team.
|
|
6 *
|
|
7 * This program is free software; you can redistribute it and/or modify
|
|
8 * it under the terms of the GNU General Public License as published by
|
|
9 * the Free Software Foundation; either version 2 of the License, or
|
|
10 * (at your option) any later version.
|
|
11 *
|
|
12 * This program is distributed in the hope that it will be useful,
|
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15 * GNU General Public License for more details.
|
|
16 *
|
|
17 * You should have received a copy of the GNU General Public License
|
|
18 * along with this program; if not, write to the Free Software
|
|
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
20 */
|
|
21
|
|
22 #include <glib.h>
|
|
23 #include <glib/gi18n.h>
|
|
24 #include <gtk/gtk.h>
|
|
25 #include <stdlib.h>
|
|
26 #include <string.h>
|
|
27 #include <stdio.h>
|
|
28
|
|
29 #include <fcntl.h>
|
|
30 #include <unistd.h>
|
|
31 #include <errno.h>
|
|
32 #include <sys/types.h>
|
|
33 #include <sys/socket.h>
|
|
34 #include <sys/time.h>
|
|
35 #include <netinet/in.h>
|
|
36 #include <arpa/inet.h>
|
|
37 #include <netdb.h>
|
|
38
|
|
39 #include <libaudacious/util.h>
|
|
40
|
|
41 #include "mpg123.h"
|
|
42
|
|
43
|
|
44 #define min(x,y) ((x)<(y)?(x):(y))
|
|
45 #define min3(x,y,z) (min(x,y)<(z)?min(x,y):(z))
|
|
46 #define min4(x,y,z,w) (min3(x,y,z)<(w)?min3(x,y,z):(w))
|
|
47
|
|
48 static gchar *icy_name = NULL;
|
|
49 static gint icy_metaint = 0;
|
|
50
|
|
51 #undef DEBUG_UDP
|
|
52
|
|
53 /* Static udp channel functions */
|
|
54 static gint udp_establish_listener(gint * sock);
|
|
55 static gint udp_check_for_data(gint sock);
|
|
56
|
|
57 extern gint mpg123_bitrate, mpg123_frequency, mpg123_stereo;
|
|
58 extern gboolean mpg123_stereo;
|
|
59
|
|
60 static gboolean prebuffering, going, eof = FALSE;
|
|
61 static gint sock, rd_index, wr_index, buffer_length, prebuffer_length;
|
|
62 static guint64 buffer_read = 0;
|
|
63 static gchar *buffer;
|
|
64 static GThread *thread;
|
|
65 static GtkWidget *error_dialog = NULL;
|
|
66
|
|
67 static VFSFile *output_file = NULL;
|
|
68
|
|
69 #define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
|
|
70
|
|
71 /* Encode the string S of length LENGTH to base64 format and place it
|
|
72 to STORE. STORE will be 0-terminated, and must point to a writable
|
|
73 buffer of at least 1+BASE64_LENGTH(length) bytes. */
|
|
74 static void
|
|
75 base64_encode(const gchar * s, gchar * store, gint length)
|
|
76 {
|
|
77 /* Conversion table. */
|
|
78 static gchar tbl[64] = {
|
|
79 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
|
80 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
|
81 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
|
82 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
|
83 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
|
84 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
|
85 'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
|
86 '4', '5', '6', '7', '8', '9', '+', '/'
|
|
87 };
|
|
88 gint i;
|
|
89 guchar *p = (guchar *) store;
|
|
90
|
|
91 /* Transform the 3x8 bits to 4x6 bits, as required by base64. */
|
|
92 for (i = 0; i < length; i += 3) {
|
|
93 *p++ = tbl[s[0] >> 2];
|
|
94 *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
|
|
95 *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
|
|
96 *p++ = tbl[s[2] & 0x3f];
|
|
97 s += 3;
|
|
98 }
|
|
99 /* Pad the result if necessary... */
|
|
100 if (i == length + 1)
|
|
101 *(p - 1) = '=';
|
|
102 else if (i == length + 2)
|
|
103 *(p - 1) = *(p - 2) = '=';
|
|
104 /* ...and zero-terminate it. */
|
|
105 *p = '\0';
|
|
106 }
|
|
107
|
|
108 /* Create the authentication header contents for the `Basic' scheme.
|
|
109 This is done by encoding the string `USER:PASS' in base64 and
|
|
110 prepending `HEADER: Basic ' to it. */
|
|
111 static gchar *
|
|
112 basic_authentication_encode(const gchar * user,
|
|
113 const gchar * passwd, const gchar * header)
|
|
114 {
|
|
115 gchar *t1, *t2, *res;
|
|
116 gint len1 = strlen(user) + 1 + strlen(passwd);
|
|
117 gint len2 = BASE64_LENGTH(len1);
|
|
118
|
|
119 t1 = g_strdup_printf("%s:%s", user, passwd);
|
|
120 t2 = g_malloc0(len2 + 1);
|
|
121 base64_encode(t1, t2, len1);
|
|
122 res = g_strdup_printf("%s: Basic %s\r\n", header, t2);
|
|
123 g_free(t2);
|
|
124 g_free(t1);
|
|
125
|
|
126 return res;
|
|
127 }
|
|
128
|
|
129 static void
|
|
130 parse_url(const gchar * url, gchar ** user, gchar ** pass,
|
|
131 gchar ** host, gint * port, gchar ** filename)
|
|
132 {
|
|
133 gchar *h, *p, *pt, *f, *temp, *ptr;
|
|
134
|
|
135 temp = g_strdup(url);
|
|
136 ptr = temp;
|
|
137
|
|
138 if (!strncasecmp("http://", ptr, 7))
|
|
139 ptr += 7;
|
|
140 h = strchr(ptr, '@');
|
|
141 f = strchr(ptr, '/');
|
|
142 if (h != NULL && (!f || h < f)) {
|
|
143 *h = '\0';
|
|
144 p = strchr(ptr, ':');
|
|
145 if (p != NULL && p < h) {
|
|
146 *p = '\0';
|
|
147 p++;
|
|
148 *pass = g_strdup(p);
|
|
149 }
|
|
150 else
|
|
151 *pass = NULL;
|
|
152 *user = g_strdup(ptr);
|
|
153 h++;
|
|
154 ptr = h;
|
|
155 }
|
|
156 else {
|
|
157 *user = NULL;
|
|
158 *pass = NULL;
|
|
159 h = ptr;
|
|
160 }
|
|
161 pt = strchr(ptr, ':');
|
|
162 if (pt != NULL && (f == NULL || pt < f)) {
|
|
163 *pt = '\0';
|
|
164 *port = atoi(pt + 1);
|
|
165 }
|
|
166 else {
|
|
167 if (f)
|
|
168 *f = '\0';
|
|
169 *port = 80;
|
|
170 }
|
|
171 *host = g_strdup(h);
|
|
172
|
|
173 if (f)
|
|
174 *filename = g_strdup(f + 1);
|
|
175 else
|
|
176 *filename = NULL;
|
|
177 g_free(temp);
|
|
178 }
|
|
179
|
|
180 void
|
|
181 mpg123_http_close(void)
|
|
182 {
|
|
183 going = FALSE;
|
|
184
|
|
185 g_thread_join(thread);
|
|
186 g_free(icy_name);
|
|
187 icy_name = NULL;
|
|
188 }
|
|
189
|
|
190
|
|
191 static gint
|
|
192 http_used(void)
|
|
193 {
|
|
194 if (wr_index >= rd_index)
|
|
195 return wr_index - rd_index;
|
|
196 return buffer_length - (rd_index - wr_index);
|
|
197 }
|
|
198
|
|
199 static gint
|
|
200 http_free(void)
|
|
201 {
|
|
202 if (rd_index > wr_index)
|
|
203 return (rd_index - wr_index) - 1;
|
|
204 return (buffer_length - (wr_index - rd_index)) - 1;
|
|
205 }
|
|
206
|
|
207 static void
|
|
208 http_wait_for_data(gint bytes)
|
|
209 {
|
|
210 while ((prebuffering || http_used() < bytes) && !eof && going
|
|
211 && mpg123_info->going)
|
|
212 xmms_usleep(10000);
|
|
213 }
|
|
214
|
|
215 static void
|
|
216 show_error_message(gchar * error)
|
|
217 {
|
|
218 if (!error_dialog) {
|
|
219 GDK_THREADS_ENTER();
|
|
220 error_dialog = xmms_show_message(_("Error"), error, _("Ok"), FALSE,
|
|
221 NULL, NULL);
|
|
222 g_signal_connect(G_OBJECT(error_dialog),
|
|
223 "destroy",
|
|
224 G_CALLBACK(gtk_widget_destroyed), &error_dialog);
|
|
225 GDK_THREADS_LEAVE();
|
|
226 }
|
|
227 }
|
|
228
|
|
229 int
|
|
230 mpg123_http_read(gpointer data, gint length)
|
|
231 {
|
|
232 gint len, cnt, off = 0, meta_len, meta_off = 0, i;
|
|
233 gchar *meta_data, **tags;
|
|
234
|
|
235 http_wait_for_data(length);
|
|
236
|
|
237 if (!going && !mpg123_info->going)
|
|
238 return 0;
|
|
239 len = min(http_used(), length);
|
|
240
|
|
241 while (len && http_used()) {
|
|
242 if ((icy_metaint > 0) && (buffer_read % icy_metaint) == 0 &&
|
|
243 (buffer_read > 0)) {
|
|
244 meta_len = *((guchar *) buffer + rd_index) * 16;
|
|
245 rd_index = (rd_index + 1) % buffer_length;
|
|
246 if (meta_len > 0) {
|
|
247 http_wait_for_data(meta_len);
|
|
248 meta_data = g_malloc0(meta_len);
|
|
249 if (http_used() >= meta_len) {
|
|
250 while (meta_len) {
|
|
251 cnt = min(meta_len, buffer_length - rd_index);
|
|
252 memcpy(meta_data + meta_off, buffer + rd_index, cnt);
|
|
253 rd_index = (rd_index + cnt) % buffer_length;
|
|
254 meta_len -= cnt;
|
|
255 meta_off += cnt;
|
|
256 }
|
|
257 tags = g_strsplit(meta_data, "';", 0);
|
|
258
|
|
259 for (i = 0; tags[i]; i++) {
|
|
260 if (!strncasecmp(tags[i], "StreamTitle=", 12)) {
|
|
261 gchar *temp = tags[i] + 13;
|
|
262 gchar *title =
|
|
263 g_strdup_printf("%s (%s)", temp, icy_name);
|
|
264 mpg123_ip.set_info(title, -1,
|
|
265 mpg123_bitrate * 1000,
|
|
266 mpg123_frequency,
|
|
267 mpg123_stereo);
|
|
268 g_free(title);
|
|
269 }
|
|
270
|
|
271 }
|
|
272 g_strfreev(tags);
|
|
273
|
|
274 }
|
|
275 g_free(meta_data);
|
|
276 }
|
|
277 if (!http_used())
|
|
278 http_wait_for_data(length - off);
|
|
279 cnt = min3(len, buffer_length - rd_index, http_used());
|
|
280 }
|
|
281 else if (icy_metaint > 0)
|
|
282 cnt =
|
|
283 min4(len, buffer_length - rd_index, http_used(),
|
|
284 icy_metaint - (buffer_read % icy_metaint));
|
|
285 else
|
|
286 cnt = min3(len, buffer_length - rd_index, http_used());
|
|
287 if (output_file)
|
|
288 vfs_fwrite(buffer + rd_index, 1, cnt, output_file);
|
|
289
|
|
290 memcpy((gchar *) data + off, buffer + rd_index, cnt);
|
|
291 rd_index = (rd_index + cnt) % buffer_length;
|
|
292 buffer_read += cnt;
|
|
293 len -= cnt;
|
|
294 off += cnt;
|
|
295 }
|
|
296 return off;
|
|
297 }
|
|
298
|
|
299 static gboolean
|
|
300 http_check_for_data(void)
|
|
301 {
|
|
302 fd_set set;
|
|
303 struct timeval tv;
|
|
304 gint ret;
|
|
305
|
|
306 tv.tv_sec = 0;
|
|
307 tv.tv_usec = 20000;
|
|
308 FD_ZERO(&set);
|
|
309 FD_SET(sock, &set);
|
|
310 ret = select(sock + 1, &set, NULL, NULL, &tv);
|
|
311 if (ret > 0)
|
|
312 return TRUE;
|
|
313 return FALSE;
|
|
314 }
|
|
315
|
|
316 gint
|
|
317 mpg123_http_read_line(gchar * buf, gint size)
|
|
318 {
|
|
319 gint i = 0;
|
|
320
|
|
321 while (going && i < size - 1) {
|
|
322 if (http_check_for_data()) {
|
|
323 if (read(sock, buf + i, 1) <= 0)
|
|
324 return -1;
|
|
325 if (buf[i] == '\n')
|
|
326 break;
|
|
327 if (buf[i] != '\r')
|
|
328 i++;
|
|
329 }
|
|
330 }
|
|
331 if (!going)
|
|
332 return -1;
|
|
333 buf[i] = '\0';
|
|
334 return i;
|
|
335 }
|
|
336
|
|
337 static gpointer
|
|
338 http_buffer_loop(gpointer arg)
|
|
339 {
|
|
340 gchar line[1024], *user, *pass, *host, *filename,
|
|
341 *status, *url, *temp, *file;
|
|
342 gchar *chost;
|
|
343 gint cnt, written, error, err_len, port, cport;
|
|
344 gboolean redirect;
|
|
345 gint udp_sock = 0;
|
|
346 fd_set set;
|
|
347 struct hostent *hp;
|
|
348 struct sockaddr_in address;
|
|
349 struct timeval tv;
|
|
350
|
|
351 url = (gchar *) arg;
|
|
352 do {
|
|
353 redirect = FALSE;
|
|
354
|
|
355 g_strstrip(url);
|
|
356
|
|
357 parse_url(url, &user, &pass, &host, &port, &filename);
|
|
358
|
|
359 if ((!filename || !*filename) && url[strlen(url) - 1] != '/')
|
|
360 temp = g_strconcat(url, "/", NULL);
|
|
361 else
|
|
362 temp = g_strdup(url);
|
|
363 g_free(url);
|
|
364 url = temp;
|
|
365
|
|
366 chost = mpg123_cfg.use_proxy ? mpg123_cfg.proxy_host : host;
|
|
367 cport = mpg123_cfg.use_proxy ? mpg123_cfg.proxy_port : port;
|
|
368
|
|
369 sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
370 fcntl(sock, F_SETFL, O_NONBLOCK);
|
|
371 address.sin_family = AF_INET;
|
|
372
|
|
373 status = g_strdup_printf(_("LOOKING UP %s"), chost);
|
|
374 mpg123_ip.set_info_text(status);
|
|
375 g_free(status);
|
|
376
|
|
377 if (!(hp = gethostbyname(chost))) {
|
|
378 status = g_strdup_printf(_("Couldn't look up host %s"), chost);
|
|
379 show_error_message(status);
|
|
380 g_free(status);
|
|
381
|
|
382 mpg123_ip.set_info_text(NULL);
|
|
383 eof = TRUE;
|
|
384 }
|
|
385
|
|
386 if (!eof) {
|
|
387 memcpy(&address.sin_addr.s_addr, *(hp->h_addr_list),
|
|
388 sizeof(address.sin_addr.s_addr));
|
|
389 address.sin_port = g_htons(cport);
|
|
390
|
|
391 status = g_strdup_printf(_("CONNECTING TO %s:%d"), chost, cport);
|
|
392 mpg123_ip.set_info_text(status);
|
|
393 g_free(status);
|
|
394 if (connect
|
|
395 (sock, (struct sockaddr *) &address,
|
|
396 sizeof(struct sockaddr_in)) == -1) {
|
|
397 if (errno != EINPROGRESS) {
|
|
398 status =
|
|
399 g_strdup_printf(_("Couldn't connect to host %s"),
|
|
400 chost);
|
|
401 show_error_message(status);
|
|
402 g_free(status);
|
|
403
|
|
404 mpg123_ip.set_info_text(NULL);
|
|
405 eof = TRUE;
|
|
406 }
|
|
407 }
|
|
408 while (going) {
|
|
409 tv.tv_sec = 0;
|
|
410 tv.tv_usec = 10000;
|
|
411 FD_ZERO(&set);
|
|
412 FD_SET(sock, &set);
|
|
413 if (select(sock + 1, NULL, &set, NULL, &tv) > 0) {
|
|
414 err_len = sizeof(error);
|
|
415 getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &err_len);
|
|
416 if (error) {
|
|
417 status =
|
|
418 g_strdup_printf(_
|
|
419 ("Couldn't connect to host %s"),
|
|
420 chost);
|
|
421 show_error_message(status);
|
|
422 g_free(status);
|
|
423
|
|
424 mpg123_ip.set_info_text(NULL);
|
|
425 eof = TRUE;
|
|
426
|
|
427 }
|
|
428 break;
|
|
429 }
|
|
430 }
|
|
431 if (!eof) {
|
|
432 gchar *auth = NULL, *proxy_auth = NULL;
|
|
433 gchar udpspace[30];
|
|
434 gint udp_port;
|
|
435
|
|
436 if (mpg123_cfg.use_udp_channel) {
|
|
437 udp_port = udp_establish_listener(&udp_sock);
|
|
438 if (udp_port > 0)
|
|
439 sprintf(udpspace, "x-audiocast-udpport: %d\r\n",
|
|
440 udp_port);
|
|
441 else
|
|
442 udp_sock = 0;
|
|
443 }
|
|
444
|
|
445 if (user && pass)
|
|
446 auth =
|
|
447 basic_authentication_encode(user, pass,
|
|
448 "Authorization");
|
|
449
|
|
450 if (mpg123_cfg.use_proxy) {
|
|
451 file = g_strdup(url);
|
|
452 if (mpg123_cfg.proxy_use_auth && mpg123_cfg.proxy_user
|
|
453 && mpg123_cfg.proxy_pass) {
|
|
454 proxy_auth =
|
|
455 basic_authentication_encode(mpg123_cfg.
|
|
456 proxy_user,
|
|
457 mpg123_cfg.
|
|
458 proxy_pass,
|
|
459 "Proxy-Authorization");
|
|
460 }
|
|
461 }
|
|
462 else
|
|
463 file = g_strconcat("/", filename, NULL);
|
|
464 temp = g_strdup_printf("GET %s HTTP/1.0\r\n"
|
|
465 "Host: %s\r\n"
|
|
466 "User-Agent: %s/%s\r\n"
|
|
467 "%s%s%s%s\r\n",
|
|
468 file, host, PACKAGE, VERSION,
|
|
469 proxy_auth ? proxy_auth : "",
|
|
470 auth ? auth : "",
|
|
471 "Icy-MetaData:1\r\n",
|
|
472 mpg123_cfg.
|
|
473 use_udp_channel ? udpspace : "");
|
|
474
|
|
475 g_free(file);
|
|
476 if (proxy_auth)
|
|
477 g_free(proxy_auth);
|
|
478 if (auth)
|
|
479 g_free(auth);
|
|
480 write(sock, temp, strlen(temp));
|
|
481 g_free(temp);
|
|
482 mpg123_ip.set_info_text(_("CONNECTED: WAITING FOR REPLY"));
|
|
483 while (going && !eof) {
|
|
484 if (http_check_for_data()) {
|
|
485 if (mpg123_http_read_line(line, 1024)) {
|
|
486 status = strchr(line, ' ');
|
|
487 if (status) {
|
|
488 if (status[1] == '2')
|
|
489 break;
|
|
490 else if (status[1] == '3'
|
|
491 && status[2] == '0'
|
|
492 && status[3] == '2') {
|
|
493 while (going) {
|
|
494 if (http_check_for_data()) {
|
|
495 if ((cnt =
|
|
496 mpg123_http_read_line
|
|
497 (line, 1024)) != -1) {
|
|
498 if (!cnt)
|
|
499 break;
|
|
500 if (!strncmp
|
|
501 (line, "Location:", 9)) {
|
|
502 g_free(url);
|
|
503 url = g_strdup(line + 10);
|
|
504 }
|
|
505 }
|
|
506 else {
|
|
507 eof = TRUE;
|
|
508 mpg123_ip.set_info_text(NULL);
|
|
509 break;
|
|
510 }
|
|
511 }
|
|
512 }
|
|
513 redirect = TRUE;
|
|
514 break;
|
|
515 }
|
|
516 else {
|
|
517 status =
|
|
518 g_strdup_printf(_
|
|
519 ("Couldn't connect to host %s\nServer reported: %s"),
|
|
520 chost, status);
|
|
521 show_error_message(status);
|
|
522 g_free(status);
|
|
523 break;
|
|
524 }
|
|
525 }
|
|
526 }
|
|
527 else {
|
|
528 eof = TRUE;
|
|
529 mpg123_ip.set_info_text(NULL);
|
|
530 }
|
|
531 }
|
|
532 }
|
|
533
|
|
534 while (going && !redirect) {
|
|
535 if (http_check_for_data()) {
|
|
536 if ((cnt = mpg123_http_read_line(line, 1024)) != -1) {
|
|
537 if (!cnt)
|
|
538 break;
|
|
539 if (!strncmp(line, "icy-name:", 9))
|
|
540 icy_name = g_strdup(line + 9);
|
|
541 else if (!strncmp(line, "x-audiocast-name:", 17))
|
|
542 icy_name = g_strdup(line + 17);
|
|
543 if (!strncmp(line, "icy-metaint:", 12))
|
|
544 icy_metaint = atoi(line + 12);
|
|
545 if (!strncmp(line, "x-audiocast-udpport:", 20)) {
|
|
546 #ifdef DEBUG_UDP
|
|
547 fprintf(stderr,
|
|
548 "Server wants udp messages on port %d\n",
|
|
549 atoi(line + 20));
|
|
550 #endif
|
|
551 /* udp_serverport = atoi (line + 20); */
|
|
552 }
|
|
553
|
|
554 }
|
|
555 else {
|
|
556 eof = TRUE;
|
|
557 mpg123_ip.set_info_text(NULL);
|
|
558 break;
|
|
559 }
|
|
560 }
|
|
561 }
|
|
562 }
|
|
563 }
|
|
564
|
|
565 if (redirect) {
|
|
566 if (output_file) {
|
|
567 vfs_fclose(output_file);
|
|
568 output_file = NULL;
|
|
569 }
|
|
570 close(sock);
|
|
571 g_free(user);
|
|
572 g_free(pass);
|
|
573 g_free(host);
|
|
574 g_free(filename);
|
|
575 }
|
|
576 } while (redirect);
|
|
577
|
|
578 if (mpg123_cfg.save_http_stream) {
|
|
579 gchar *output_name;
|
|
580 gint i = 1;
|
|
581
|
|
582 file = mpg123_http_get_title(url);
|
|
583 output_name = file;
|
|
584 if (!strncasecmp(output_name, "http://", 7))
|
|
585 output_name += 7;
|
|
586 temp = strrchr(output_name, '.');
|
|
587 if (temp && !strcasecmp(temp, ".mp3"))
|
|
588 *temp = '\0';
|
|
589
|
|
590 while ((temp = strchr(output_name, '/')))
|
|
591 *temp = '_';
|
|
592 output_name = g_strdup_printf("%s/%s.mp3",
|
|
593 mpg123_cfg.save_http_path, output_name);
|
|
594 while (!access(output_name, F_OK) && i < 100000) {
|
|
595 g_free(output_name);
|
|
596 output_name = g_strdup_printf("%s/%s-%d.mp3",
|
|
597 mpg123_cfg.save_http_path,
|
|
598 output_name, i++);
|
|
599 }
|
|
600
|
|
601 g_free(file);
|
|
602
|
|
603 output_file = vfs_fopen(output_name, "wb");
|
|
604 g_free(output_name);
|
|
605 }
|
|
606
|
|
607 while (going) {
|
|
608
|
|
609 if (!http_used() && !mpg123_ip.output->buffer_playing())
|
|
610 prebuffering = TRUE;
|
|
611 if (http_free() > 0 && !eof) {
|
|
612 if (http_check_for_data()) {
|
|
613 cnt = min(http_free(), buffer_length - wr_index);
|
|
614 if (cnt > 1024)
|
|
615 cnt = 1024;
|
|
616 written = read(sock, buffer + wr_index, cnt);
|
|
617 if (written <= 0) {
|
|
618 eof = TRUE;
|
|
619 if (prebuffering) {
|
|
620 prebuffering = FALSE;
|
|
621
|
|
622 mpg123_ip.set_info_text(NULL);
|
|
623 }
|
|
624
|
|
625 }
|
|
626 else
|
|
627 wr_index = (wr_index + written) % buffer_length;
|
|
628 }
|
|
629
|
|
630 if (prebuffering) {
|
|
631 if (http_used() > prebuffer_length) {
|
|
632 prebuffering = FALSE;
|
|
633 mpg123_ip.set_info_text(NULL);
|
|
634 }
|
|
635 else {
|
|
636 status =
|
|
637 g_strdup_printf(_("PRE-BUFFERING: %dKB/%dKB"),
|
|
638 http_used() / 1024,
|
|
639 prebuffer_length / 1024);
|
|
640 mpg123_ip.set_info_text(status);
|
|
641 g_free(status);
|
|
642 }
|
|
643
|
|
644 }
|
|
645 }
|
|
646 else
|
|
647 xmms_usleep(10000);
|
|
648
|
|
649 if (mpg123_cfg.use_udp_channel && udp_sock != 0)
|
|
650 if (udp_check_for_data(udp_sock) < 0) {
|
|
651 close(udp_sock);
|
|
652 udp_sock = 0;
|
|
653 }
|
|
654 }
|
|
655 if (output_file) {
|
|
656 vfs_fclose(output_file);
|
|
657 output_file = NULL;
|
|
658 }
|
|
659 close(sock);
|
|
660 if (udp_sock != 0)
|
|
661 close(udp_sock);
|
|
662
|
|
663 g_free(user);
|
|
664 g_free(pass);
|
|
665 g_free(host);
|
|
666 g_free(filename);
|
|
667 g_free(buffer);
|
|
668 g_free(url);
|
|
669
|
|
670 return NULL;
|
|
671 }
|
|
672
|
|
673 int
|
|
674 mpg123_http_open(gchar * _url)
|
|
675 {
|
|
676 gchar *url;
|
|
677
|
|
678 url = g_strdup(_url);
|
|
679
|
|
680 rd_index = 0;
|
|
681 wr_index = 0;
|
|
682 buffer_length = mpg123_cfg.http_buffer_size * 1024;
|
|
683 prebuffer_length = (buffer_length * mpg123_cfg.http_prebuffer) / 100;
|
|
684 buffer_read = 0;
|
|
685 icy_metaint = 0;
|
|
686 prebuffering = TRUE;
|
|
687 going = TRUE;
|
|
688 eof = FALSE;
|
|
689 buffer = g_malloc(buffer_length);
|
|
690
|
|
691 thread = g_thread_create(http_buffer_loop, url, TRUE, NULL);
|
|
692
|
|
693 return 0;
|
|
694 }
|
|
695
|
|
696 char *
|
|
697 mpg123_http_get_title(gchar * url)
|
|
698 {
|
|
699 if (icy_name)
|
|
700 return g_strdup(icy_name);
|
|
701 if (g_basename(url) && strlen(g_basename(url)) > 0)
|
|
702 return g_strdup(g_basename(url));
|
|
703 return g_strdup(url);
|
|
704 }
|
|
705
|
|
706 /* Start UDP Channel specific stuff */
|
|
707
|
|
708 /* Find a good local udp port and bind udp_sock to it, return the port */
|
|
709 static gint
|
|
710 udp_establish_listener(gint * sock)
|
|
711 {
|
|
712 struct sockaddr_in sin;
|
|
713 socklen_t sinlen = sizeof(struct sockaddr_in);
|
|
714
|
|
715 #ifdef DEBUG_UDP
|
|
716 fprintf(stderr, "Establishing udp listener\n");
|
|
717 #endif
|
|
718
|
|
719 if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
720 g_log(NULL, G_LOG_LEVEL_CRITICAL,
|
|
721 "udp_establish_listener(): unable to create socket");
|
|
722 return -1;
|
|
723 }
|
|
724
|
|
725 memset(&sin, 0, sinlen);
|
|
726 sin.sin_family = AF_INET;
|
|
727 sin.sin_addr.s_addr = g_htonl(INADDR_ANY);
|
|
728
|
|
729 if (bind(*sock, (struct sockaddr *) &sin, sinlen) < 0) {
|
|
730 g_log(NULL, G_LOG_LEVEL_CRITICAL,
|
|
731 "udp_establish_listener(): Failed to bind socket to localhost: %s",
|
|
732 strerror(errno));
|
|
733 close(*sock);
|
|
734 return -1;
|
|
735 }
|
|
736 if (fcntl(*sock, F_SETFL, O_NONBLOCK) < 0) {
|
|
737 g_log(NULL, G_LOG_LEVEL_CRITICAL,
|
|
738 "udp_establish_listener(): Failed to set flags: %s",
|
|
739 strerror(errno));
|
|
740 close(*sock);
|
|
741 return -1;
|
|
742 }
|
|
743
|
|
744 memset(&sin, 0, sinlen);
|
|
745 if (getsockname(*sock, (struct sockaddr *) &sin, &sinlen) < 0) {
|
|
746 g_log(NULL, G_LOG_LEVEL_CRITICAL,
|
|
747 "udp_establish_listener(): Failed to retrieve socket info: %s",
|
|
748 strerror(errno));
|
|
749 close(*sock);
|
|
750 return -1;
|
|
751 }
|
|
752 #ifdef DEBUG_UDP
|
|
753 fprintf(stderr, "Listening on local %s:%d\n", inet_ntoa(sin.sin_addr),
|
|
754 g_ntohs(sin.sin_port));
|
|
755 #endif
|
|
756
|
|
757 return g_ntohs(sin.sin_port);
|
|
758 }
|
|
759
|
|
760 static int
|
|
761 udp_check_for_data(int sock)
|
|
762 {
|
|
763 char buf[1025], **lines;
|
|
764 char *valptr;
|
|
765 gchar *title;
|
|
766 gint len, i;
|
|
767 struct sockaddr_in from;
|
|
768 socklen_t fromlen;
|
|
769
|
|
770 fromlen = sizeof(struct sockaddr_in);
|
|
771
|
|
772 if ((len =
|
|
773 recvfrom(sock, buf, 1024, 0, (struct sockaddr *) &from,
|
|
774 &fromlen)) < 0) {
|
|
775 if (errno != EAGAIN) {
|
|
776 g_log(NULL, G_LOG_LEVEL_CRITICAL,
|
|
777 "udp_read_data(): Error reading from socket: %s",
|
|
778 strerror(errno));
|
|
779 return -1;
|
|
780 }
|
|
781 return 0;
|
|
782 }
|
|
783 buf[len] = '\0';
|
|
784 #ifdef DEBUG_UDP
|
|
785 fprintf(stderr, "Received: [%s]\n", buf);
|
|
786 #endif
|
|
787 lines = g_strsplit(buf, "\n", 0);
|
|
788 if (!lines)
|
|
789 return 0;
|
|
790
|
|
791 for (i = 0; lines[i]; i++) {
|
|
792 while ((lines[i][strlen(lines[i]) - 1] == '\n') ||
|
|
793 (lines[i][strlen(lines[i]) - 1] == '\r'))
|
|
794 lines[i][strlen(lines[i]) - 1] = '\0';
|
|
795
|
|
796 valptr = strchr(lines[i], ':');
|
|
797
|
|
798 if (!valptr)
|
|
799 continue;
|
|
800 else
|
|
801 valptr++;
|
|
802
|
|
803 g_strstrip(valptr);
|
|
804 if (!strlen(valptr))
|
|
805 continue;
|
|
806
|
|
807 if (strstr(lines[i], "x-audiocast-streamtitle") != NULL) {
|
|
808 title = g_strdup_printf("%s (%s)", valptr, icy_name);
|
|
809 if (going)
|
|
810 mpg123_ip.set_info(title, -1, mpg123_bitrate * 1000,
|
|
811 mpg123_frequency, mpg123_stereo);
|
|
812 g_free(title);
|
|
813 }
|
|
814 #if 0
|
|
815 else if (strstr(lines[i], "x-audiocast-streamlength") != NULL) {
|
|
816 if (atoi(valptr) != -1)
|
|
817 mpg123_ip.set_info(NULL, atoi(valptr),
|
|
818 mpg123_bitrate * 1000, mpg123_frequency,
|
|
819 mpg123_stereo);
|
|
820 }
|
|
821 #endif
|
|
822
|
|
823 else if (strstr(lines[i], "x-audiocast-streammsg") != NULL) {
|
|
824 /* mpg123_ip.set_info(title, -1, mpg123_bitrate * 1000, mpg123_frequency, mpg123_stereo); */
|
|
825 /* xmms_show_message(_("Message"), valptr, _("Ok"), */
|
|
826 /* FALSE, NULL, NULL); */
|
|
827 g_message("Stream_message: %s", valptr);
|
|
828 }
|
|
829 #if 0
|
|
830 /* Use this to direct your webbrowser.. yeah right.. */
|
|
831 else if (strstr(lines[i], "x-audiocast-streamurl") != NULL) {
|
|
832 if (lasturl && g_strcmp(valptr, lasturl)) {
|
|
833 c_message(stderr, "Song URL: %s\n", valptr);
|
|
834 g_free(lasturl);
|
|
835 lasturl = g_strdup(valptr);
|
|
836 }
|
|
837 }
|
|
838 #endif
|
|
839 else if (strstr(lines[i], "x-audiocast-udpseqnr:") != NULL) {
|
|
840 gchar obuf[60];
|
|
841 sprintf(obuf, "x-audiocast-ack: %ld \r\n", atol(valptr));
|
|
842 if (sendto
|
|
843 (sock, obuf, strlen(obuf), 0, (struct sockaddr *) &from,
|
|
844 fromlen) < 0) {
|
|
845 g_log(NULL, G_LOG_LEVEL_WARNING,
|
|
846 "udp_check_for_data(): Unable to send ack to server: %s",
|
|
847 strerror(errno));
|
|
848 }
|
|
849 #ifdef DEBUG_UDP
|
|
850 else
|
|
851 fprintf(stderr, "Sent ack: %s", obuf);
|
|
852 fprintf(stderr, "Remote: %s:%d\n", inet_ntoa(from.sin_addr),
|
|
853 g_ntohs(from.sin_port));
|
|
854 #endif
|
|
855 }
|
|
856 }
|
|
857 g_strfreev(lines);
|
|
858 return 0;
|
|
859 }
|