add :refresh command
[cmus.git] / input.c
blob151b93709a98833651aba6622d27e2edfea07281
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 <prog.h>
30 #include <debug.h>
31 #include <config.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <stdarg.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <dirent.h>
41 #include <dlfcn.h>
43 struct input_plugin {
44 const struct input_plugin_ops *ops;
45 struct input_plugin_data data;
46 unsigned int open : 1;
47 unsigned int eof : 1;
48 int http_code;
49 char *http_reason;
52 * pcm is converted to 16-bit signed little-endian stereo
53 * NOTE: no conversion is done if channels > 2 or bits > 16
55 void (*pcm_convert)(char *, const char *, int);
56 void (*pcm_convert_in_place)(char *, int);
58 * 4 if 8-bit mono
59 * 2 if 8-bit stereo or 16-bit mono
60 * 1 otherwise
62 int pcm_convert_scale;
65 struct ip {
66 struct list_head node;
67 char *name;
68 void *handle;
70 const char * const *extensions;
71 const char * const *mime_types;
72 const struct input_plugin_ops *ops;
75 static const char * const plugin_dir = LIBDIR "/" PACKAGE "/ip";
76 static LIST_HEAD(ip_head);
78 /* timeouts (ms) */
79 static int http_connection_timeout = 5e3;
80 static int http_read_timeout = 5e3;
82 static const char *get_extension(const char *filename)
84 const char *ext;
86 ext = filename + strlen(filename) - 1;
87 while (ext >= filename && *ext != '/') {
88 if (*ext == '.') {
89 ext++;
90 return ext;
92 ext--;
94 return NULL;
97 static const struct input_plugin_ops *get_ops_by_filename(const char *filename)
99 struct ip *ip;
100 const char *ext;
102 ext = get_extension(filename);
103 if (ext == NULL)
104 return NULL;
105 list_for_each_entry(ip, &ip_head, node) {
106 const char * const *exts = ip->extensions;
107 int i;
109 for (i = 0; exts[i]; i++) {
110 if (strcasecmp(ext, exts[i]) == 0)
111 return ip->ops;
114 return NULL;
117 static const struct input_plugin_ops *get_ops_by_mime_type(const char *mime_type)
119 struct ip *ip;
121 list_for_each_entry(ip, &ip_head, node) {
122 const char * const *types = ip->mime_types;
123 int i;
125 for (i = 0; types[i]; i++) {
126 if (strcasecmp(mime_type, types[i]) == 0)
127 return ip->ops;
130 return NULL;
133 static int do_http_get(const char *uri, struct http_header **headersp, int *codep, char **reasonp)
135 struct http_uri u;
136 struct http_header *h;
137 int sock, i, rc, code;
138 char *reason;
140 *headersp = NULL;
141 *codep = -1;
142 *reasonp = NULL;
144 if (http_parse_uri(uri, &u))
145 return -IP_ERROR_INVALID_URI;
147 /* d_print("%s -> '%s':'%s'@'%s':%d'%s'\n", uri, user, pass, host, port, path); */
149 sock = http_open(u.host, u.port, http_connection_timeout);
150 if (sock == -1) {
151 http_free_uri(&u);
152 return -IP_ERROR_ERRNO;
155 h = xnew(struct http_header, 5);
156 i = 0;
157 h[i].key = xstrdup("Host");
158 h[i].val = xstrdup(u.host);
159 i++;
160 h[i].key = xstrdup("User-Agent");
161 h[i].val = xstrdup(PACKAGE "/" VERSION);
162 i++;
163 h[i].key = xstrdup("Icy-MetaData");
164 h[i].val = xstrdup("1");
165 i++;
166 if (u.user && u.pass) {
167 char buf[256];
168 char *encoded;
170 snprintf(buf, sizeof(buf), "%s:%s", u.user, u.pass);
171 encoded = base64_encode(buf);
172 if (encoded == NULL) {
173 d_print("couldn't base64 encode '%s'\n", buf);
174 } else {
175 snprintf(buf, sizeof(buf), "Basic %s", encoded);
176 free(encoded);
177 h[i].key = xstrdup("Authorization");
178 h[i].val = xstrdup(buf);
179 i++;
182 h[i].key = NULL;
183 h[i].val = NULL;
184 i++;
186 rc = http_get(sock, u.path, h, &code, &reason, headersp, http_read_timeout);
187 http_headers_free(h);
188 http_free_uri(&u);
189 switch (rc) {
190 case -1:
191 d_print("error: %s\n", strerror(errno));
192 close(sock);
193 return -IP_ERROR_ERRNO;
194 case -2:
195 d_print("error parsing HTTP response\n");
196 close(sock);
197 return -IP_ERROR_HTTP_RESPONSE;
199 d_print("HTTP response: %d %s\n", code, reason);
200 if (code != 200) {
201 *codep = code;
202 *reasonp = reason;
203 close(sock);
204 return -IP_ERROR_HTTP_STATUS;
206 free(reason);
207 return sock;
210 static int setup_remote(struct input_plugin *ip, const struct http_header *headers, int sock)
212 const char *val;
214 val = http_headers_get_value(headers, "Content-Type");
215 if (val) {
216 ip->ops = get_ops_by_mime_type(val);
217 if (ip->ops == NULL) {
218 d_print("unsupported content type: %s\n", val);
219 close(sock);
220 return -IP_ERROR_FILE_FORMAT;
222 } else {
223 const char *type = "audio/mpeg";
225 d_print("assuming %s content type\n", type);
226 ip->ops = get_ops_by_mime_type(type);
227 if (ip->ops == NULL) {
228 d_print("unsupported content type: %s\n", type);
229 close(sock);
230 return -IP_ERROR_FILE_FORMAT;
234 ip->data.fd = sock;
235 ip->data.metadata = (char *)xmalloc(16 * 255 + 1);
237 val = http_headers_get_value(headers, "icy-metaint");
238 if (val) {
239 long int lint;
241 if (str_to_int(val, &lint) == 0 && lint >= 0) {
242 ip->data.metaint = lint;
243 d_print("metaint: %d\n", ip->data.metaint);
246 return 0;
249 static int handle_line(void *data, const char *line)
251 char **firstp = data;
253 *firstp = xstrdup(line);
254 /* ignore other lines */
255 return 1;
258 static int read_playlist(struct input_plugin *ip, int sock)
260 struct http_header *headers;
261 char *body, *reason, *first;
262 int rc, code;
264 rc = http_read_body(sock, &body, http_read_timeout);
265 close(sock);
266 if (rc)
267 return -IP_ERROR_ERRNO;
269 /* get only first URL from the playlist */
270 first = NULL;
271 cmus_playlist_for_each(body, strlen(body), 0, handle_line, &first);
272 free(body);
274 if (first == NULL) {
275 d_print("empty playlist\n");
276 return -IP_ERROR_HTTP_RESPONSE;
279 sock = do_http_get(first, &headers, &code, &reason);
280 free(first);
281 if (sock < 0) {
282 ip->http_code = code;
283 ip->http_reason = reason;
284 /* URI in the _playlist_ is invalid, not our fault */
285 if (sock == -IP_ERROR_INVALID_URI)
286 sock = -IP_ERROR_HTTP_RESPONSE;
287 return sock;
290 rc = setup_remote(ip, headers, sock);
291 http_headers_free(headers);
292 return rc;
295 static int open_remote(struct input_plugin *ip)
297 struct input_plugin_data *d = &ip->data;
298 char *reason;
299 int sock, rc, code;
300 struct http_header *headers;
301 const char *val;
303 sock = do_http_get(d->filename, &headers, &code, &reason);
304 if (sock < 0) {
305 ip->http_code = code;
306 ip->http_reason = reason;
307 return sock;
310 val = http_headers_get_value(headers, "Content-Type");
311 if (val) {
312 d_print("Content-Type: %s\n", val);
313 if (!strcasecmp(val, "audio/x-scpls") || !strcasecmp(val, "audio/m3u")) {
314 http_headers_free(headers);
315 return read_playlist(ip, sock);
319 rc = setup_remote(ip, headers, sock);
320 http_headers_free(headers);
321 return rc;
324 static int open_file(struct input_plugin *ip)
326 ip->ops = get_ops_by_filename(ip->data.filename);
327 if (ip->ops == NULL)
328 return -IP_ERROR_UNRECOGNIZED_FILE_TYPE;
329 ip->data.fd = open(ip->data.filename, O_RDONLY);
330 if (ip->data.fd == -1) {
331 ip->ops = NULL;
332 return -IP_ERROR_ERRNO;
334 return 0;
337 void ip_load_plugins(void)
339 DIR *dir;
340 struct dirent *d;
342 dir = opendir(plugin_dir);
343 if (dir == NULL) {
344 warn_errno("couldn't open directory `%s'", plugin_dir);
345 return;
347 while ((d = readdir(dir)) != NULL) {
348 char filename[256];
349 struct ip *ip;
350 void *so;
351 char *ext;
352 const char *sym;
354 if (d->d_name[0] == '.')
355 continue;
356 ext = strrchr(d->d_name, '.');
357 if (ext == NULL)
358 continue;
359 if (strcmp(ext, ".so"))
360 continue;
362 snprintf(filename, sizeof(filename), "%s/%s", plugin_dir, d->d_name);
364 so = dlopen(filename, RTLD_NOW);
365 if (so == NULL) {
366 warn("%s\n", dlerror());
367 continue;
370 ip = xnew(struct ip, 1);
372 sym = "ip_extensions";
373 if (!(ip->extensions = dlsym(so, sym)))
374 goto sym_err;
376 sym = "ip_mime_types";
377 if (!(ip->mime_types = dlsym(so, sym)))
378 goto sym_err;
380 sym = "ip_ops";
381 if (!(ip->ops = dlsym(so, sym)))
382 goto sym_err;
384 ip->name = xstrndup(d->d_name, ext - d->d_name);
385 ip->handle = so;
387 list_add_tail(&ip->node, &ip_head);
388 continue;
389 sym_err:
390 warn("%s: symbol %s not found\n", filename, sym);
391 free(ip);
392 dlclose(so);
394 closedir(dir);
397 /* init everything but ip->data.filename and ip->data.remote */
398 static void ip_init(struct input_plugin *ip)
400 ip->ops = NULL;
401 ip->open = 0;
402 ip->eof = 0;
403 ip->http_code = -1;
404 ip->http_reason = NULL;
405 ip->pcm_convert = NULL;
406 ip->pcm_convert_in_place = NULL;
407 ip->pcm_convert_scale = -1;
409 ip->data.fd = -1;
410 ip->data.metadata_changed = 0;
411 ip->data.counter = 0;
412 ip->data.metaint = 0;
413 ip->data.metadata = NULL;
414 ip->data.sf = 0;
415 ip->data.private = NULL;
418 struct input_plugin *ip_new(const char *filename)
420 struct input_plugin *ip = xnew(struct input_plugin, 1);
422 ip_init(ip);
423 ip->data.filename = xstrdup(filename);
424 ip->data.remote = is_url(filename);
425 return ip;
428 void ip_delete(struct input_plugin *ip)
430 if (ip->open)
431 ip_close(ip);
432 free(ip->data.filename);
433 free(ip);
436 int ip_open(struct input_plugin *ip)
438 int rc, bits, is_signed, channels;
440 BUG_ON(ip->open);
441 BUG_ON(ip->eof);
442 BUG_ON(ip->ops);
443 BUG_ON(ip->data.filename == NULL);
444 BUG_ON(ip->data.fd != -1);
445 BUG_ON(ip->data.sf);
446 BUG_ON(ip->pcm_convert_scale != -1);
448 /* set fd and ops */
449 if (ip->data.remote) {
450 rc = open_remote(ip);
451 } else {
452 rc = open_file(ip);
455 if (rc) {
456 d_print("opening `%s' failed: %d %s\n", ip->data.filename, rc,
457 rc == -1 ? strerror(errno) : "");
458 return rc;
461 BUG_ON(ip->data.fd == -1);
462 BUG_ON(ip->ops == NULL);
464 BUG_ON(ip->ops->open == NULL);
465 BUG_ON(ip->ops->close == NULL);
466 BUG_ON(ip->ops->read == NULL);
467 BUG_ON(ip->ops->seek == NULL);
468 BUG_ON(ip->ops->read_comments == NULL);
469 BUG_ON(ip->ops->duration == NULL);
471 rc = ip->ops->open(&ip->data);
472 if (rc) {
473 d_print("opening file `%s' failed: %d %s\n", ip->data.filename, rc,
474 rc == -1 ? strerror(errno) : "");
475 if (ip->data.fd != -1)
476 close(ip->data.fd);
477 ip->data.fd = -1;
478 ip->ops = NULL;
479 free(ip->data.metadata);
480 ip->data.metadata = NULL;
481 return rc;
484 ip->pcm_convert_scale = 1;
485 ip->pcm_convert = NULL;
486 ip->pcm_convert_in_place = NULL;
487 bits = sf_get_bits(ip->data.sf);
488 is_signed = sf_get_signed(ip->data.sf);
489 channels = sf_get_channels(ip->data.sf);
490 if (bits == 8) {
491 if (channels == 1) {
492 ip->pcm_convert_scale = 4;
493 if (is_signed) {
494 ip->pcm_convert = convert_s8_1ch_to_s16_2ch;
495 } else {
496 ip->pcm_convert = convert_u8_1ch_to_s16_2ch;
498 } else if (channels == 2) {
499 ip->pcm_convert_scale = 2;
500 if (is_signed) {
501 ip->pcm_convert = convert_s8_2ch_to_s16_2ch;
502 } else {
503 ip->pcm_convert = convert_u8_2ch_to_s16_2ch;
506 } else if (bits == 16) {
507 if (channels == 1) {
508 ip->pcm_convert_scale = 2;
509 ip->pcm_convert = convert_16_1ch_to_16_2ch;
511 if (channels <= 2) {
512 int bigendian = sf_get_bigendian(ip->data.sf);
514 if (is_signed) {
515 if (bigendian)
516 ip->pcm_convert_in_place = convert_s16_be_to_s16_le;
517 } else {
518 if (bigendian) {
519 ip->pcm_convert_in_place = convert_u16_be_to_s16_le;
520 } else {
521 ip->pcm_convert_in_place = convert_u16_le_to_s16_le;
526 d_print("pcm convert: scale=%d convert=%d convert_in_place=%d\n",
527 ip->pcm_convert_scale,
528 ip->pcm_convert != NULL,
529 ip->pcm_convert_in_place != NULL);
531 ip->open = 1;
532 return 0;
535 int ip_close(struct input_plugin *ip)
537 int rc;
539 BUG_ON(!ip->open);
541 rc = ip->ops->close(&ip->data);
542 BUG_ON(ip->data.private);
543 if (ip->data.fd != -1)
544 close(ip->data.fd);
545 free(ip->data.metadata);
546 free(ip->http_reason);
548 ip_init(ip);
549 return rc;
552 int ip_read(struct input_plugin *ip, char *buffer, int count)
554 struct timeval tv;
555 fd_set readfds;
556 /* 4608 seems to be optimal for mp3s, 4096 for oggs */
557 char tmp[8 * 1024];
558 char *buf;
559 int rc;
561 BUG_ON(!ip->open);
562 BUG_ON(count <= 0);
564 FD_ZERO(&readfds);
565 FD_SET(ip->data.fd, &readfds);
566 /* zero timeout -> return immediately */
567 tv.tv_sec = 0;
568 tv.tv_usec = 50e3;
569 rc = select(ip->data.fd + 1, &readfds, NULL, NULL, &tv);
570 if (rc == -1) {
571 d_print("select: error: %s\n", strerror(errno));
572 if (errno == EINTR)
573 errno = EAGAIN;
574 return -1;
576 if (rc == 0) {
577 errno = EAGAIN;
578 return -1;
581 buf = buffer;
582 if (ip->pcm_convert_scale > 1) {
583 /* use tmp buffer for 16-bit mono and 8-bit */
584 buf = tmp;
585 count /= ip->pcm_convert_scale;
586 if (count > sizeof(tmp))
587 count = sizeof(tmp);
590 rc = ip->ops->read(&ip->data, buf, count);
591 if (rc == 0)
592 ip->eof = 1;
593 if (rc == -1)
594 d_print("error: %s\n", strerror(errno));
596 if (rc > 0) {
597 int sample_size = sf_get_sample_size(ip->data.sf);
599 if (ip->pcm_convert_in_place != NULL)
600 ip->pcm_convert_in_place(buf, rc / sample_size);
601 if (ip->pcm_convert != NULL)
602 ip->pcm_convert(buffer, tmp, rc / sample_size);
603 rc *= ip->pcm_convert_scale;
605 return rc;
608 int ip_seek(struct input_plugin *ip, double offset)
610 int rc;
612 BUG_ON(!ip->open);
614 if (ip->data.remote)
615 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
616 rc = ip->ops->seek(&ip->data, offset);
617 if (rc == 0)
618 ip->eof = 0;
619 return rc;
622 int ip_read_comments(struct input_plugin *ip, struct keyval **comments)
624 int rc;
626 BUG_ON(!ip->open);
628 rc = ip->ops->read_comments(&ip->data, comments);
629 return rc;
632 int ip_duration(struct input_plugin *ip)
634 int rc;
636 BUG_ON(!ip->open);
638 rc = ip->ops->duration(&ip->data);
639 return rc;
642 sample_format_t ip_get_sf(struct input_plugin *ip)
644 BUG_ON(!ip->open);
645 return ip->data.sf;
648 const char *ip_get_filename(struct input_plugin *ip)
650 return ip->data.filename;
653 const char *ip_get_metadata(struct input_plugin *ip)
655 BUG_ON(!ip->open);
656 return ip->data.metadata;
659 int ip_is_remote(struct input_plugin *ip)
661 return ip->data.remote;
664 int ip_metadata_changed(struct input_plugin *ip)
666 int ret = ip->data.metadata_changed;
668 BUG_ON(!ip->open);
669 ip->data.metadata_changed = 0;
670 return ret;
673 int ip_eof(struct input_plugin *ip)
675 BUG_ON(!ip->open);
676 return ip->eof;
679 char *ip_get_error_msg(struct input_plugin *ip, int rc, const char *arg)
681 char buffer[1024];
683 switch (-rc) {
684 case IP_ERROR_ERRNO:
685 snprintf(buffer, sizeof(buffer), "%s: %s", arg, strerror(errno));
686 break;
687 case IP_ERROR_UNRECOGNIZED_FILE_TYPE:
688 snprintf(buffer, sizeof(buffer),
689 "%s: unrecognized filename extension", arg);
690 break;
691 case IP_ERROR_FUNCTION_NOT_SUPPORTED:
692 snprintf(buffer, sizeof(buffer),
693 "%s: function not supported", arg);
694 break;
695 case IP_ERROR_FILE_FORMAT:
696 snprintf(buffer, sizeof(buffer),
697 "%s: file format not supported or corrupted file",
698 arg);
699 break;
700 case IP_ERROR_INVALID_URI:
701 snprintf(buffer, sizeof(buffer), "%s: invalid URI", arg);
702 break;
703 case IP_ERROR_SAMPLE_FORMAT:
704 snprintf(buffer, sizeof(buffer),
705 "%s: input plugin doesn't support the sample format",
706 arg);
707 break;
708 case IP_ERROR_HTTP_RESPONSE:
709 snprintf(buffer, sizeof(buffer), "%s: invalid HTTP response", arg);
710 break;
711 case IP_ERROR_HTTP_STATUS:
712 snprintf(buffer, sizeof(buffer), "%s: %d %s", arg, ip->http_code, ip->http_reason);
713 free(ip->http_reason);
714 ip->http_reason = NULL;
715 ip->http_code = -1;
716 break;
717 case IP_ERROR_INTERNAL:
718 snprintf(buffer, sizeof(buffer), "%s: internal error", arg);
719 break;
720 case IP_ERROR_SUCCESS:
721 default:
722 snprintf(buffer, sizeof(buffer),
723 "%s: this is not an error (%d), this is a bug",
724 arg, rc);
725 break;
727 return xstrdup(buffer);
730 static int strptrcmp(const void *a, const void *b)
732 const char *as = *(char **)a;
733 const char *bs = *(char **)b;
735 return strcmp(as, bs);
738 char **ip_get_supported_extensions(void)
740 struct ip *ip;
741 char **exts;
742 int i, size;
743 int count = 0;
745 size = 8;
746 exts = xnew(char *, size);
747 list_for_each_entry(ip, &ip_head, node) {
748 const char * const *e = ip->extensions;
750 for (i = 0; e[i]; i++) {
751 if (count == size - 1) {
752 size *= 2;
753 exts = xrenew(char *, exts, size);
755 exts[count++] = xstrdup(e[i]);
758 exts[count] = NULL;
759 qsort(exts, count, sizeof(char *), strptrcmp);
760 return exts;
763 void ip_dump_plugins(void)
765 struct ip *ip;
766 int i;
768 printf("Input Plugins: %s\n", plugin_dir);
769 list_for_each_entry(ip, &ip_head, node) {
770 printf(" %s:\n File Types:", ip->name);
771 for (i = 0; ip->extensions[i]; i++)
772 printf(" %s", ip->extensions[i]);
773 printf("\n MIME Types:");
774 for (i = 0; ip->mime_types[i]; i++)
775 printf(" %s", ip->mime_types[i]);
776 printf("\n");