2 * Copyright 2004-2005 Timo Hirvonen
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
31 #include "ui_curses.h"
32 #include "config/libdir.h"
38 #include <sys/types.h>
45 const struct input_plugin_ops
*ops
;
46 struct input_plugin_data data
;
47 unsigned int open
: 1;
52 /* cached duration, -1 = unset */
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);
63 * 2 if 8-bit stereo or 16-bit mono
66 int pcm_convert_scale
;
70 struct list_head node
;
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
);
83 static int http_connection_timeout
= 5e3
;
84 static int http_read_timeout
= 5e3
;
86 static const char *pl_mime_types
[] = {
92 static const char *get_extension(const char *filename
)
96 ext
= filename
+ strlen(filename
) - 1;
97 while (ext
>= filename
&& *ext
!= '/') {
107 static const struct input_plugin_ops
*get_ops_by_filename(const char *filename
)
112 ext
= get_extension(filename
);
115 list_for_each_entry(ip
, &ip_head
, node
) {
116 const char * const *exts
= ip
->extensions
;
119 for (i
= 0; exts
[i
]; i
++) {
120 if (strcasecmp(ext
, exts
[i
]) == 0)
127 static const struct input_plugin_ops
*get_ops_by_mime_type(const char *mime_type
)
131 list_for_each_entry(ip
, &ip_head
, node
) {
132 const char * const *types
= ip
->mime_types
;
135 for (i
= 0; types
[i
]; i
++) {
136 if (strcasecmp(mime_type
, types
[i
]) == 0)
143 static int do_http_get(struct http_get
*hg
, const char *uri
, int redirections
)
150 d_print("%s\n", uri
);
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
) {
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
);
174 snprintf(buf
, sizeof(buf
), "Basic %s", 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
);
185 return -IP_ERROR_ERRNO
;
187 return -IP_ERROR_HTTP_RESPONSE
;
191 * FIXME: Use information from the headers, we read.
193 * especially interesting:
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
);
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");
214 return -IP_ERROR_HTTP_RESPONSE
;
215 redirloc
= xstrdup(val
);
220 if (redirections
> 2)
221 return -IP_ERROR_HTTP_REDIRECT_LIMIT
;
223 rc
= do_http_get(hg
, redirloc
, redirections
);
227 return -IP_ERROR_HTTP_STATUS
;
231 static int setup_remote(struct input_plugin
*ip
, const struct keyval
*headers
, int sock
)
235 val
= keyvals_get_val(headers
, "Content-Type");
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
);
242 return -IP_ERROR_FILE_FORMAT
;
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
);
252 return -IP_ERROR_FILE_FORMAT
;
257 ip
->data
.metadata
= xmalloc(16 * 255 + 1);
259 val
= keyvals_get_val(headers
, "icy-metaint");
263 if (str_to_int(val
, &lint
) == 0 && lint
>= 0) {
264 ip
->data
.metaint
= lint
;
265 d_print("metaint: %d\n", ip
->data
.metaint
);
271 struct read_playlist_data
{
272 struct input_plugin
*ip
;
277 static int handle_line(void *data
, const char *uri
)
279 struct read_playlist_data
*rpd
= data
;
283 rpd
->rc
= do_http_get(&hg
, uri
, 0);
285 rpd
->ip
->http_code
= hg
.code
;
286 rpd
->ip
->http_reason
= hg
.reason
;
295 rpd
->rc
= setup_remote(rpd
->ip
, hg
.headers
, hg
.fd
);
300 static int read_playlist(struct input_plugin
*ip
, int sock
)
302 struct read_playlist_data rpd
= { ip
, 0, 0 };
306 body
= http_read_body(sock
, &size
, http_read_timeout
);
309 return -IP_ERROR_ERRNO
;
311 cmus_playlist_for_each(body
, size
, 0, handle_line
, &rpd
);
314 d_print("empty playlist\n");
315 rpd
.rc
= -IP_ERROR_HTTP_RESPONSE
;
320 static int open_remote(struct input_plugin
*ip
)
322 struct input_plugin_data
*d
= &ip
->data
;
327 rc
= do_http_get(&hg
, d
->filename
, 0);
329 ip
->http_code
= hg
.code
;
330 ip
->http_reason
= hg
.reason
;
336 val
= keyvals_get_val(hg
.headers
, "Content-Type");
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
);
344 return read_playlist(ip
, hg
.fd
);
349 rc
= setup_remote(ip
, hg
.headers
, hg
.fd
);
354 static int open_file(struct input_plugin
*ip
)
356 ip
->ops
= get_ops_by_filename(ip
->data
.filename
);
358 return -IP_ERROR_UNRECOGNIZED_FILE_TYPE
;
359 ip
->data
.fd
= open(ip
->data
.filename
, O_RDONLY
);
360 if (ip
->data
.fd
== -1) {
362 return -IP_ERROR_ERRNO
;
367 void ip_load_plugins(void)
372 dir
= opendir(plugin_dir
);
374 error_msg("couldn't open directory `%s': %s", plugin_dir
, strerror(errno
));
377 while ((d
= readdir(dir
)) != NULL
) {
383 if (d
->d_name
[0] == '.')
385 ext
= strrchr(d
->d_name
, '.');
388 if (strcmp(ext
, ".so"))
391 snprintf(filename
, sizeof(filename
), "%s/%s", plugin_dir
, d
->d_name
);
393 so
= dlopen(filename
, RTLD_NOW
);
395 error_msg("%s", dlerror());
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
);
411 ip
->name
= xstrndup(d
->d_name
, ext
- d
->d_name
);
414 list_add_tail(&ip
->node
, &ip_head
);
419 static void ip_init(struct input_plugin
*ip
, char *filename
)
421 memset(ip
, 0, sizeof(*ip
));
423 ip
->pcm_convert_scale
= -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
));
438 void ip_delete(struct input_plugin
*ip
)
442 free(ip
->data
.filename
);
446 int ip_open(struct input_plugin
*ip
)
453 if (ip
->data
.remote
) {
454 rc
= open_remote(ip
);
460 d_print("opening `%s' failed: %d %s\n", ip
->data
.filename
, rc
,
461 rc
== -1 ? strerror(errno
) : "");
465 rc
= ip
->ops
->open(&ip
->data
);
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)
471 free(ip
->data
.metadata
);
472 ip_init(ip
, ip
->data
.filename
);
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
)
511 rc
= ip
->ops
->close(&ip
->data
);
512 BUG_ON(ip
->data
.private);
513 if (ip
->data
.fd
!= -1)
515 free(ip
->data
.metadata
);
516 free(ip
->http_reason
);
518 ip_init(ip
, ip
->data
.filename
);
522 int ip_read(struct input_plugin
*ip
, char *buffer
, int count
)
526 /* 4608 seems to be optimal for mp3s, 4096 for oggs */
535 FD_SET(ip
->data
.fd
, &readfds
);
536 /* zero timeout -> return immediately */
539 rc
= select(ip
->data
.fd
+ 1, &readfds
, NULL
, NULL
, &tv
);
551 if (ip
->pcm_convert_scale
> 1) {
552 /* use tmp buffer for 16-bit mono and 8-bit */
554 count
/= ip
->pcm_convert_scale
;
555 if (count
> sizeof(tmp
))
559 rc
= ip
->ops
->read(&ip
->data
, buf
, count
);
560 if (rc
== -1 && (errno
== EAGAIN
|| errno
== EINTR
)) {
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
)
584 return -IP_ERROR_FUNCTION_NOT_SUPPORTED
;
585 rc
= ip
->ops
->seek(&ip
->data
, offset
);
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
)
600 if (ip
->duration
== -1)
601 ip
->duration
= ip
->ops
->duration(&ip
->data
);
602 if (ip
->duration
< 0)
607 sample_format_t
ip_get_sf(struct input_plugin
*ip
)
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
)
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
;
634 ip
->data
.metadata_changed
= 0;
638 int ip_eof(struct input_plugin
*ip
)
644 char *ip_get_error_msg(struct input_plugin
*ip
, int rc
, const char *arg
)
650 snprintf(buffer
, sizeof(buffer
), "%s: %s", arg
, strerror(errno
));
652 case IP_ERROR_UNRECOGNIZED_FILE_TYPE
:
653 snprintf(buffer
, sizeof(buffer
),
654 "%s: unrecognized filename extension", arg
);
656 case IP_ERROR_FUNCTION_NOT_SUPPORTED
:
657 snprintf(buffer
, sizeof(buffer
),
658 "%s: function not supported", arg
);
660 case IP_ERROR_FILE_FORMAT
:
661 snprintf(buffer
, sizeof(buffer
),
662 "%s: file format not supported or corrupted file",
665 case IP_ERROR_INVALID_URI
:
666 snprintf(buffer
, sizeof(buffer
), "%s: invalid URI", arg
);
668 case IP_ERROR_SAMPLE_FORMAT
:
669 snprintf(buffer
, sizeof(buffer
),
670 "%s: input plugin doesn't support the sample format",
673 case IP_ERROR_HTTP_RESPONSE
:
674 snprintf(buffer
, sizeof(buffer
), "%s: invalid HTTP response", arg
);
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
;
682 case IP_ERROR_HTTP_REDIRECT_LIMIT
:
683 snprintf(buffer
, sizeof(buffer
), "%s: too many HTTP redirections", arg
);
685 case IP_ERROR_INTERNAL
:
686 snprintf(buffer
, sizeof(buffer
), "%s: internal error", arg
);
688 case IP_ERROR_SUCCESS
:
690 snprintf(buffer
, sizeof(buffer
),
691 "%s: this is not an error (%d), this is a bug",
695 return xstrdup(buffer
);
698 char **ip_get_supported_extensions(void)
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) {
713 exts
= xrenew(char *, exts
, size
);
715 exts
[count
++] = xstrdup(e
[i
]);
719 qsort(exts
, count
, sizeof(char *), strptrcmp
);
723 void ip_dump_plugins(void)
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
]);