Handle streams separately in tree_add_track()
[cmus.git] / input.c
blobd7bbf7129247cdd53ecb7a9f6c27a29f8038957a
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include "input.h"
21 #include "ip.h"
22 #include "pcm.h"
23 #include "http.h"
24 #include "xmalloc.h"
25 #include "file.h"
26 #include "utils.h"
27 #include "cmus.h"
28 #include "list.h"
29 #include "misc.h"
30 #include "debug.h"
31 #include "ui_curses.h"
32 #include "config/libdir.h"
34 #include <unistd.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <stdarg.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <dirent.h>
42 #include <dlfcn.h>
44 struct input_plugin {
45 const struct input_plugin_ops *ops;
46 struct input_plugin_data data;
47 unsigned int open : 1;
48 unsigned int eof : 1;
49 int http_code;
50 char *http_reason;
52 /* cached duration, -1 = unset */
53 int duration;
56 * pcm is converted to 16-bit signed little-endian stereo
57 * NOTE: no conversion is done if channels > 2 or bits > 16
59 void (*pcm_convert)(char *, const char *, int);
60 void (*pcm_convert_in_place)(char *, int);
62 * 4 if 8-bit mono
63 * 2 if 8-bit stereo or 16-bit mono
64 * 1 otherwise
66 int pcm_convert_scale;
69 struct ip {
70 struct list_head node;
71 char *name;
72 void *handle;
74 const char * const *extensions;
75 const char * const *mime_types;
76 const struct input_plugin_ops *ops;
79 static const char * const plugin_dir = LIBDIR "/cmus/ip";
80 static LIST_HEAD(ip_head);
82 /* timeouts (ms) */
83 static int http_connection_timeout = 5e3;
84 static int http_read_timeout = 5e3;
86 static const char *pl_mime_types[] = {
87 "audio/m3u",
88 "audio/x-scpls",
89 "audio/x-mpegurl"
92 static const char *get_extension(const char *filename)
94 const char *ext;
96 ext = filename + strlen(filename) - 1;
97 while (ext >= filename && *ext != '/') {
98 if (*ext == '.') {
99 ext++;
100 return ext;
102 ext--;
104 return NULL;
107 static const struct input_plugin_ops *get_ops_by_filename(const char *filename)
109 struct ip *ip;
110 const char *ext;
112 ext = get_extension(filename);
113 if (ext == NULL)
114 return NULL;
115 list_for_each_entry(ip, &ip_head, node) {
116 const char * const *exts = ip->extensions;
117 int i;
119 for (i = 0; exts[i]; i++) {
120 if (strcasecmp(ext, exts[i]) == 0)
121 return ip->ops;
124 return NULL;
127 static const struct input_plugin_ops *get_ops_by_mime_type(const char *mime_type)
129 struct ip *ip;
131 list_for_each_entry(ip, &ip_head, node) {
132 const char * const *types = ip->mime_types;
133 int i;
135 for (i = 0; types[i]; i++) {
136 if (strcasecmp(mime_type, types[i]) == 0)
137 return ip->ops;
140 return NULL;
143 static int do_http_get(struct http_get *hg, const char *uri, int redirections)
145 GROWING_KEYVALS(h);
146 int i, rc;
147 const char *val;
148 char *redirloc;
150 d_print("%s\n", uri);
152 hg->headers = NULL;
153 hg->reason = NULL;
154 hg->code = -1;
155 hg->fd = -1;
156 if (http_parse_uri(uri, &hg->uri))
157 return -IP_ERROR_INVALID_URI;
159 if (http_open(hg, http_connection_timeout))
160 return -IP_ERROR_ERRNO;
162 keyvals_add(&h, "Host", xstrdup(hg->uri.host));
163 keyvals_add(&h, "User-Agent", xstrdup("cmus/" VERSION));
164 keyvals_add(&h, "Icy-MetaData", xstrdup("1"));
165 if (hg->uri.user && hg->uri.pass) {
166 char buf[256];
167 char *encoded;
169 snprintf(buf, sizeof(buf), "%s:%s", hg->uri.user, hg->uri.pass);
170 encoded = base64_encode(buf);
171 if (encoded == NULL) {
172 d_print("couldn't base64 encode '%s'\n", buf);
173 } else {
174 snprintf(buf, sizeof(buf), "Basic %s", encoded);
175 free(encoded);
176 keyvals_add(&h, "Authorization", xstrdup(buf));
179 keyvals_terminate(&h);
181 rc = http_get(hg, h.keyvals, http_read_timeout);
182 keyvals_free(h.keyvals);
183 switch (rc) {
184 case -1:
185 return -IP_ERROR_ERRNO;
186 case -2:
187 return -IP_ERROR_HTTP_RESPONSE;
191 * FIXME: Use information from the headers, we read.
193 * especially interesting:
194 * + icy-name
195 * + icy-url
197 d_print("HTTP response: %d %s\n", hg->code, hg->reason);
198 for (i = 0; hg->headers[i].key != NULL; i++)
199 d_print(" %s: %s\n", hg->headers[i].key, hg->headers[i].val);
201 switch (hg->code) {
202 case 200: /* OK */
203 return 0;
205 * 3xx Codes (Redirections)
206 * unhandled: 300 Multiple Choices
208 case 301: /* Moved Permanently */
209 case 302: /* Found */
210 case 303: /* See Other */
211 case 307: /* Temporary Redirect */
212 val = keyvals_get_val(hg->headers, "location");
213 if (!val)
214 return -IP_ERROR_HTTP_RESPONSE;
215 redirloc = xstrdup(val);
216 http_get_free(hg);
217 close(hg->fd);
219 redirections++;
220 if (redirections > 2)
221 return -IP_ERROR_HTTP_REDIRECT_LIMIT;
223 rc = do_http_get(hg, redirloc, redirections);
224 free(redirloc);
225 return rc;
226 default:
227 return -IP_ERROR_HTTP_STATUS;
231 static int setup_remote(struct input_plugin *ip, const struct keyval *headers, int sock)
233 const char *val;
235 val = keyvals_get_val(headers, "Content-Type");
236 if (val) {
237 d_print("Content-Type: %s\n", val);
238 ip->ops = get_ops_by_mime_type(val);
239 if (ip->ops == NULL) {
240 d_print("unsupported content type: %s\n", val);
241 close(sock);
242 return -IP_ERROR_FILE_FORMAT;
244 } else {
245 const char *type = "audio/mpeg";
247 d_print("assuming %s content type\n", type);
248 ip->ops = get_ops_by_mime_type(type);
249 if (ip->ops == NULL) {
250 d_print("unsupported content type: %s\n", type);
251 close(sock);
252 return -IP_ERROR_FILE_FORMAT;
256 ip->data.fd = sock;
257 ip->data.metadata = xmalloc(16 * 255 + 1);
259 val = keyvals_get_val(headers, "icy-metaint");
260 if (val) {
261 long int lint;
263 if (str_to_int(val, &lint) == 0 && lint >= 0) {
264 ip->data.metaint = lint;
265 d_print("metaint: %d\n", ip->data.metaint);
268 return 0;
271 struct read_playlist_data {
272 struct input_plugin *ip;
273 int rc;
274 int count;
277 static int handle_line(void *data, const char *uri)
279 struct read_playlist_data *rpd = data;
280 struct http_get hg;
282 rpd->count++;
283 rpd->rc = do_http_get(&hg, uri, 0);
284 if (rpd->rc) {
285 rpd->ip->http_code = hg.code;
286 rpd->ip->http_reason = hg.reason;
287 if (hg.fd >= 0)
288 close(hg.fd);
290 hg.reason = NULL;
291 http_get_free(&hg);
292 return 0;
295 rpd->rc = setup_remote(rpd->ip, hg.headers, hg.fd);
296 http_get_free(&hg);
297 return 1;
300 static int read_playlist(struct input_plugin *ip, int sock)
302 struct read_playlist_data rpd = { ip, 0, 0 };
303 char *body;
304 size_t size;
306 body = http_read_body(sock, &size, http_read_timeout);
307 close(sock);
308 if (!body)
309 return -IP_ERROR_ERRNO;
311 cmus_playlist_for_each(body, size, 0, handle_line, &rpd);
312 free(body);
313 if (!rpd.count) {
314 d_print("empty playlist\n");
315 rpd.rc = -IP_ERROR_HTTP_RESPONSE;
317 return rpd.rc;
320 static int open_remote(struct input_plugin *ip)
322 struct input_plugin_data *d = &ip->data;
323 struct http_get hg;
324 const char *val;
325 int rc;
327 rc = do_http_get(&hg, d->filename, 0);
328 if (rc) {
329 ip->http_code = hg.code;
330 ip->http_reason = hg.reason;
331 hg.reason = NULL;
332 http_get_free(&hg);
333 return rc;
336 val = keyvals_get_val(hg.headers, "Content-Type");
337 if (val) {
338 int i;
340 for (i = 0; i < sizeof(pl_mime_types) / sizeof(pl_mime_types[0]); i++) {
341 if (!strcasecmp(val, pl_mime_types[i])) {
342 d_print("Content-Type: %s\n", val);
343 http_get_free(&hg);
344 return read_playlist(ip, hg.fd);
349 rc = setup_remote(ip, hg.headers, hg.fd);
350 http_get_free(&hg);
351 return rc;
354 static int open_file(struct input_plugin *ip)
356 ip->ops = get_ops_by_filename(ip->data.filename);
357 if (ip->ops == NULL)
358 return -IP_ERROR_UNRECOGNIZED_FILE_TYPE;
359 ip->data.fd = open(ip->data.filename, O_RDONLY);
360 if (ip->data.fd == -1) {
361 ip->ops = NULL;
362 return -IP_ERROR_ERRNO;
364 return 0;
367 void ip_load_plugins(void)
369 DIR *dir;
370 struct dirent *d;
372 dir = opendir(plugin_dir);
373 if (dir == NULL) {
374 error_msg("couldn't open directory `%s': %s", plugin_dir, strerror(errno));
375 return;
377 while ((d = readdir(dir)) != NULL) {
378 char filename[256];
379 struct ip *ip;
380 void *so;
381 char *ext;
383 if (d->d_name[0] == '.')
384 continue;
385 ext = strrchr(d->d_name, '.');
386 if (ext == NULL)
387 continue;
388 if (strcmp(ext, ".so"))
389 continue;
391 snprintf(filename, sizeof(filename), "%s/%s", plugin_dir, d->d_name);
393 so = dlopen(filename, RTLD_NOW);
394 if (so == NULL) {
395 error_msg("%s", dlerror());
396 continue;
399 ip = xnew(struct ip, 1);
401 ip->extensions = dlsym(so, "ip_extensions");
402 ip->mime_types = dlsym(so, "ip_mime_types");
403 ip->ops = dlsym(so, "ip_ops");
404 if (!ip->extensions || !ip->mime_types || !ip->ops) {
405 error_msg("%s: missing symbol", filename);
406 free(ip);
407 dlclose(so);
408 continue;
411 ip->name = xstrndup(d->d_name, ext - d->d_name);
412 ip->handle = so;
414 list_add_tail(&ip->node, &ip_head);
416 closedir(dir);
419 static void ip_init(struct input_plugin *ip, char *filename)
421 memset(ip, 0, sizeof(*ip));
422 ip->http_code = -1;
423 ip->pcm_convert_scale = -1;
424 ip->duration = -1;
425 ip->data.fd = -1;
426 ip->data.filename = filename;
427 ip->data.remote = is_url(filename);
430 struct input_plugin *ip_new(const char *filename)
432 struct input_plugin *ip = xnew(struct input_plugin, 1);
434 ip_init(ip, xstrdup(filename));
435 return ip;
438 void ip_delete(struct input_plugin *ip)
440 if (ip->open)
441 ip_close(ip);
442 free(ip->data.filename);
443 free(ip);
446 int ip_open(struct input_plugin *ip)
448 int rc;
450 BUG_ON(ip->open);
452 /* set fd and ops */
453 if (ip->data.remote) {
454 rc = open_remote(ip);
455 } else {
456 rc = open_file(ip);
459 if (rc) {
460 d_print("opening `%s' failed: %d %s\n", ip->data.filename, rc,
461 rc == -1 ? strerror(errno) : "");
462 return rc;
465 rc = ip->ops->open(&ip->data);
466 if (rc) {
467 d_print("opening file `%s' failed: %d %s\n", ip->data.filename, rc,
468 rc == -1 ? strerror(errno) : "");
469 if (ip->data.fd != -1)
470 close(ip->data.fd);
471 free(ip->data.metadata);
472 ip_init(ip, ip->data.filename);
473 return rc;
475 ip->open = 1;
476 return 0;
479 void ip_setup(struct input_plugin *ip)
481 unsigned int bits, is_signed, channels;
482 sample_format_t sf = ip->data.sf;
484 bits = sf_get_bits(sf);
485 is_signed = sf_get_signed(sf);
486 channels = sf_get_channels(sf);
488 ip->pcm_convert_scale = 1;
489 ip->pcm_convert = NULL;
490 ip->pcm_convert_in_place = NULL;
492 if (bits <= 16 && channels <= 2) {
493 unsigned int mask = ((bits >> 2) & 4) | (is_signed << 1);
495 ip->pcm_convert = pcm_conv[mask | (channels - 1)];
496 ip->pcm_convert_in_place = pcm_conv_in_place[mask | sf_get_bigendian(sf)];
498 ip->pcm_convert_scale = (3 - channels) * (3 - bits / 8);
501 d_print("pcm convert: scale=%d convert=%d convert_in_place=%d\n",
502 ip->pcm_convert_scale,
503 ip->pcm_convert != NULL,
504 ip->pcm_convert_in_place != NULL);
507 int ip_close(struct input_plugin *ip)
509 int rc;
511 rc = ip->ops->close(&ip->data);
512 BUG_ON(ip->data.private);
513 if (ip->data.fd != -1)
514 close(ip->data.fd);
515 free(ip->data.metadata);
516 free(ip->http_reason);
518 ip_init(ip, ip->data.filename);
519 return rc;
522 int ip_read(struct input_plugin *ip, char *buffer, int count)
524 struct timeval tv;
525 fd_set readfds;
526 /* 4608 seems to be optimal for mp3s, 4096 for oggs */
527 char tmp[8 * 1024];
528 char *buf;
529 int sample_size;
530 int rc;
532 BUG_ON(count <= 0);
534 FD_ZERO(&readfds);
535 FD_SET(ip->data.fd, &readfds);
536 /* zero timeout -> return immediately */
537 tv.tv_sec = 0;
538 tv.tv_usec = 50e3;
539 rc = select(ip->data.fd + 1, &readfds, NULL, NULL, &tv);
540 if (rc == -1) {
541 if (errno == EINTR)
542 errno = EAGAIN;
543 return -1;
545 if (rc == 0) {
546 errno = EAGAIN;
547 return -1;
550 buf = buffer;
551 if (ip->pcm_convert_scale > 1) {
552 /* use tmp buffer for 16-bit mono and 8-bit */
553 buf = tmp;
554 count /= ip->pcm_convert_scale;
555 if (count > sizeof(tmp))
556 count = sizeof(tmp);
559 rc = ip->ops->read(&ip->data, buf, count);
560 if (rc == -1 && (errno == EAGAIN || errno == EINTR)) {
561 errno = EAGAIN;
562 return -1;
564 if (rc <= 0) {
565 ip->eof = 1;
566 return rc;
569 BUG_ON((rc & ~((unsigned int)sf_get_frame_size(ip->data.sf) - 1U)) != rc);
571 sample_size = sf_get_sample_size(ip->data.sf);
572 if (ip->pcm_convert_in_place != NULL)
573 ip->pcm_convert_in_place(buf, rc / sample_size);
574 if (ip->pcm_convert != NULL)
575 ip->pcm_convert(buffer, tmp, rc / sample_size);
576 return rc * ip->pcm_convert_scale;
579 int ip_seek(struct input_plugin *ip, double offset)
581 int rc;
583 if (ip->data.remote)
584 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
585 rc = ip->ops->seek(&ip->data, offset);
586 if (rc == 0)
587 ip->eof = 0;
588 return rc;
591 int ip_read_comments(struct input_plugin *ip, struct keyval **comments)
593 return ip->ops->read_comments(&ip->data, comments);
596 int ip_duration(struct input_plugin *ip)
598 if (ip->data.remote)
599 return -1;
600 if (ip->duration == -1)
601 ip->duration = ip->ops->duration(&ip->data);
602 if (ip->duration < 0)
603 return -1;
604 return ip->duration;
607 sample_format_t ip_get_sf(struct input_plugin *ip)
609 BUG_ON(!ip->open);
610 return ip->data.sf;
613 const char *ip_get_filename(struct input_plugin *ip)
615 return ip->data.filename;
618 const char *ip_get_metadata(struct input_plugin *ip)
620 BUG_ON(!ip->open);
621 return ip->data.metadata;
624 int ip_is_remote(struct input_plugin *ip)
626 return ip->data.remote;
629 int ip_metadata_changed(struct input_plugin *ip)
631 int ret = ip->data.metadata_changed;
633 BUG_ON(!ip->open);
634 ip->data.metadata_changed = 0;
635 return ret;
638 int ip_eof(struct input_plugin *ip)
640 BUG_ON(!ip->open);
641 return ip->eof;
644 char *ip_get_error_msg(struct input_plugin *ip, int rc, const char *arg)
646 char buffer[1024];
648 switch (-rc) {
649 case IP_ERROR_ERRNO:
650 snprintf(buffer, sizeof(buffer), "%s: %s", arg, strerror(errno));
651 break;
652 case IP_ERROR_UNRECOGNIZED_FILE_TYPE:
653 snprintf(buffer, sizeof(buffer),
654 "%s: unrecognized filename extension", arg);
655 break;
656 case IP_ERROR_FUNCTION_NOT_SUPPORTED:
657 snprintf(buffer, sizeof(buffer),
658 "%s: function not supported", arg);
659 break;
660 case IP_ERROR_FILE_FORMAT:
661 snprintf(buffer, sizeof(buffer),
662 "%s: file format not supported or corrupted file",
663 arg);
664 break;
665 case IP_ERROR_INVALID_URI:
666 snprintf(buffer, sizeof(buffer), "%s: invalid URI", arg);
667 break;
668 case IP_ERROR_SAMPLE_FORMAT:
669 snprintf(buffer, sizeof(buffer),
670 "%s: input plugin doesn't support the sample format",
671 arg);
672 break;
673 case IP_ERROR_HTTP_RESPONSE:
674 snprintf(buffer, sizeof(buffer), "%s: invalid HTTP response", arg);
675 break;
676 case IP_ERROR_HTTP_STATUS:
677 snprintf(buffer, sizeof(buffer), "%s: %d %s", arg, ip->http_code, ip->http_reason);
678 free(ip->http_reason);
679 ip->http_reason = NULL;
680 ip->http_code = -1;
681 break;
682 case IP_ERROR_HTTP_REDIRECT_LIMIT:
683 snprintf(buffer, sizeof(buffer), "%s: too many HTTP redirections", arg);
684 break;
685 case IP_ERROR_INTERNAL:
686 snprintf(buffer, sizeof(buffer), "%s: internal error", arg);
687 break;
688 case IP_ERROR_SUCCESS:
689 default:
690 snprintf(buffer, sizeof(buffer),
691 "%s: this is not an error (%d), this is a bug",
692 arg, rc);
693 break;
695 return xstrdup(buffer);
698 char **ip_get_supported_extensions(void)
700 struct ip *ip;
701 char **exts;
702 int i, size;
703 int count = 0;
705 size = 8;
706 exts = xnew(char *, size);
707 list_for_each_entry(ip, &ip_head, node) {
708 const char * const *e = ip->extensions;
710 for (i = 0; e[i]; i++) {
711 if (count == size - 1) {
712 size *= 2;
713 exts = xrenew(char *, exts, size);
715 exts[count++] = xstrdup(e[i]);
718 exts[count] = NULL;
719 qsort(exts, count, sizeof(char *), strptrcmp);
720 return exts;
723 void ip_dump_plugins(void)
725 struct ip *ip;
726 int i;
728 printf("Input Plugins: %s\n", plugin_dir);
729 list_for_each_entry(ip, &ip_head, node) {
730 printf(" %s:\n File Types:", ip->name);
731 for (i = 0; ip->extensions[i]; i++)
732 printf(" %s", ip->extensions[i]);
733 printf("\n MIME Types:");
734 for (i = 0; ip->mime_types[i]; i++)
735 printf(" %s", ip->mime_types[i]);
736 printf("\n");