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
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 *get_extension(const char *filename
)
90 ext
= filename
+ strlen(filename
) - 1;
91 while (ext
>= filename
&& *ext
!= '/') {
101 static const struct input_plugin_ops
*get_ops_by_filename(const char *filename
)
106 ext
= get_extension(filename
);
109 list_for_each_entry(ip
, &ip_head
, node
) {
110 const char * const *exts
= ip
->extensions
;
113 for (i
= 0; exts
[i
]; i
++) {
114 if (strcasecmp(ext
, exts
[i
]) == 0)
121 static const struct input_plugin_ops
*get_ops_by_mime_type(const char *mime_type
)
125 list_for_each_entry(ip
, &ip_head
, node
) {
126 const char * const *types
= ip
->mime_types
;
129 for (i
= 0; types
[i
]; i
++) {
130 if (strcasecmp(mime_type
, types
[i
]) == 0)
137 static int do_http_get(const char *uri
, struct http_header
**headersp
, int *codep
, char **reasonp
)
140 struct http_header
*h
;
141 int sock
, i
, rc
, code
;
148 if (http_parse_uri(uri
, &u
))
149 return -IP_ERROR_INVALID_URI
;
151 /* d_print("%s -> '%s':'%s'@'%s':%d'%s'\n", uri, user, pass, host, port, path); */
153 sock
= http_open(u
.host
, u
.port
, http_connection_timeout
);
156 return -IP_ERROR_ERRNO
;
159 h
= xnew(struct http_header
, 5);
161 h
[i
].key
= xstrdup("Host");
162 h
[i
].val
= xstrdup(u
.host
);
164 h
[i
].key
= xstrdup("User-Agent");
165 h
[i
].val
= xstrdup("cmus/" VERSION
);
167 h
[i
].key
= xstrdup("Icy-MetaData");
168 h
[i
].val
= xstrdup("1");
170 if (u
.user
&& u
.pass
) {
174 snprintf(buf
, sizeof(buf
), "%s:%s", u
.user
, u
.pass
);
175 encoded
= base64_encode(buf
);
176 if (encoded
== NULL
) {
177 d_print("couldn't base64 encode '%s'\n", buf
);
179 snprintf(buf
, sizeof(buf
), "Basic %s", encoded
);
181 h
[i
].key
= xstrdup("Authorization");
182 h
[i
].val
= xstrdup(buf
);
190 rc
= http_get(sock
, u
.path
, h
, &code
, &reason
, headersp
, http_read_timeout
);
191 http_headers_free(h
);
195 d_print("error: %s\n", strerror(errno
));
197 return -IP_ERROR_ERRNO
;
199 d_print("error parsing HTTP response\n");
201 return -IP_ERROR_HTTP_RESPONSE
;
203 d_print("HTTP response: %d %s\n", code
, reason
);
208 return -IP_ERROR_HTTP_STATUS
;
214 static int setup_remote(struct input_plugin
*ip
, const struct http_header
*headers
, int sock
)
218 val
= http_headers_get_value(headers
, "Content-Type");
220 ip
->ops
= get_ops_by_mime_type(val
);
221 if (ip
->ops
== NULL
) {
222 d_print("unsupported content type: %s\n", val
);
224 return -IP_ERROR_FILE_FORMAT
;
227 const char *type
= "audio/mpeg";
229 d_print("assuming %s content type\n", type
);
230 ip
->ops
= get_ops_by_mime_type(type
);
231 if (ip
->ops
== NULL
) {
232 d_print("unsupported content type: %s\n", type
);
234 return -IP_ERROR_FILE_FORMAT
;
239 ip
->data
.metadata
= (char *)xmalloc(16 * 255 + 1);
241 val
= http_headers_get_value(headers
, "icy-metaint");
245 if (str_to_int(val
, &lint
) == 0 && lint
>= 0) {
246 ip
->data
.metaint
= lint
;
247 d_print("metaint: %d\n", ip
->data
.metaint
);
253 static int handle_line(void *data
, const char *line
)
255 char **firstp
= data
;
257 *firstp
= xstrdup(line
);
258 /* ignore other lines */
262 static int read_playlist(struct input_plugin
*ip
, int sock
)
264 struct http_header
*headers
;
265 char *body
, *reason
, *first
;
268 rc
= http_read_body(sock
, &body
, http_read_timeout
);
271 return -IP_ERROR_ERRNO
;
273 /* get only first URL from the playlist */
275 cmus_playlist_for_each(body
, strlen(body
), 0, handle_line
, &first
);
279 d_print("empty playlist\n");
280 return -IP_ERROR_HTTP_RESPONSE
;
283 sock
= do_http_get(first
, &headers
, &code
, &reason
);
286 ip
->http_code
= code
;
287 ip
->http_reason
= reason
;
288 /* URI in the _playlist_ is invalid, not our fault */
289 if (sock
== -IP_ERROR_INVALID_URI
)
290 sock
= -IP_ERROR_HTTP_RESPONSE
;
294 rc
= setup_remote(ip
, headers
, sock
);
295 http_headers_free(headers
);
299 static int open_remote(struct input_plugin
*ip
)
301 struct input_plugin_data
*d
= &ip
->data
;
304 struct http_header
*headers
;
307 sock
= do_http_get(d
->filename
, &headers
, &code
, &reason
);
309 ip
->http_code
= code
;
310 ip
->http_reason
= reason
;
314 val
= http_headers_get_value(headers
, "Content-Type");
316 d_print("Content-Type: %s\n", val
);
317 if (!strcasecmp(val
, "audio/x-scpls") || !strcasecmp(val
, "audio/m3u")) {
318 http_headers_free(headers
);
319 return read_playlist(ip
, sock
);
323 rc
= setup_remote(ip
, headers
, sock
);
324 http_headers_free(headers
);
328 static int open_file(struct input_plugin
*ip
)
330 ip
->ops
= get_ops_by_filename(ip
->data
.filename
);
332 return -IP_ERROR_UNRECOGNIZED_FILE_TYPE
;
333 ip
->data
.fd
= open(ip
->data
.filename
, O_RDONLY
);
334 if (ip
->data
.fd
== -1) {
336 return -IP_ERROR_ERRNO
;
341 void ip_load_plugins(void)
346 dir
= opendir(plugin_dir
);
348 warn_errno("couldn't open directory `%s'", plugin_dir
);
351 while ((d
= readdir(dir
)) != NULL
) {
358 if (d
->d_name
[0] == '.')
360 ext
= strrchr(d
->d_name
, '.');
363 if (strcmp(ext
, ".so"))
366 snprintf(filename
, sizeof(filename
), "%s/%s", plugin_dir
, d
->d_name
);
368 so
= dlopen(filename
, RTLD_NOW
);
370 warn("%s\n", dlerror());
374 ip
= xnew(struct ip
, 1);
376 sym
= "ip_extensions";
377 if (!(ip
->extensions
= dlsym(so
, sym
)))
380 sym
= "ip_mime_types";
381 if (!(ip
->mime_types
= dlsym(so
, sym
)))
385 if (!(ip
->ops
= dlsym(so
, sym
)))
388 ip
->name
= xstrndup(d
->d_name
, ext
- d
->d_name
);
391 list_add_tail(&ip
->node
, &ip_head
);
394 warn("%s: symbol %s not found\n", filename
, sym
);
401 static void ip_init(struct input_plugin
*ip
, char *filename
)
403 memset(ip
, 0, sizeof(*ip
));
405 ip
->pcm_convert_scale
= -1;
408 ip
->data
.filename
= filename
;
409 ip
->data
.remote
= is_url(filename
);
412 struct input_plugin
*ip_new(const char *filename
)
414 struct input_plugin
*ip
= xnew(struct input_plugin
, 1);
416 ip_init(ip
, xstrdup(filename
));
420 void ip_delete(struct input_plugin
*ip
)
424 free(ip
->data
.filename
);
428 int ip_open(struct input_plugin
*ip
)
435 if (ip
->data
.remote
) {
436 rc
= open_remote(ip
);
442 d_print("opening `%s' failed: %d %s\n", ip
->data
.filename
, rc
,
443 rc
== -1 ? strerror(errno
) : "");
447 rc
= ip
->ops
->open(&ip
->data
);
449 d_print("opening file `%s' failed: %d %s\n", ip
->data
.filename
, rc
,
450 rc
== -1 ? strerror(errno
) : "");
451 if (ip
->data
.fd
!= -1)
453 free(ip
->data
.metadata
);
454 ip_init(ip
, ip
->data
.filename
);
461 void ip_setup(struct input_plugin
*ip
)
463 unsigned int bits
, is_signed
, channels
;
464 sample_format_t sf
= ip
->data
.sf
;
466 bits
= sf_get_bits(sf
);
467 is_signed
= sf_get_signed(sf
);
468 channels
= sf_get_channels(sf
);
470 ip
->pcm_convert_scale
= 1;
471 ip
->pcm_convert
= NULL
;
472 ip
->pcm_convert_in_place
= NULL
;
474 if (bits
<= 16 && channels
<= 2) {
475 unsigned int mask
= ((bits
>> 2) & 4) | (is_signed
<< 1);
477 ip
->pcm_convert
= pcm_conv
[mask
| (channels
- 1)];
478 ip
->pcm_convert_in_place
= pcm_conv_in_place
[mask
| sf_get_bigendian(sf
)];
480 ip
->pcm_convert_scale
= (3 - channels
) * (3 - bits
/ 8);
483 d_print("pcm convert: scale=%d convert=%d convert_in_place=%d\n",
484 ip
->pcm_convert_scale
,
485 ip
->pcm_convert
!= NULL
,
486 ip
->pcm_convert_in_place
!= NULL
);
489 int ip_close(struct input_plugin
*ip
)
493 rc
= ip
->ops
->close(&ip
->data
);
494 BUG_ON(ip
->data
.private);
495 if (ip
->data
.fd
!= -1)
497 free(ip
->data
.metadata
);
498 free(ip
->http_reason
);
500 ip_init(ip
, ip
->data
.filename
);
504 int ip_read(struct input_plugin
*ip
, char *buffer
, int count
)
508 /* 4608 seems to be optimal for mp3s, 4096 for oggs */
516 FD_SET(ip
->data
.fd
, &readfds
);
517 /* zero timeout -> return immediately */
520 rc
= select(ip
->data
.fd
+ 1, &readfds
, NULL
, NULL
, &tv
);
522 d_print("select: error: %s\n", strerror(errno
));
533 if (ip
->pcm_convert_scale
> 1) {
534 /* use tmp buffer for 16-bit mono and 8-bit */
536 count
/= ip
->pcm_convert_scale
;
537 if (count
> sizeof(tmp
))
541 rc
= ip
->ops
->read(&ip
->data
, buf
, count
);
545 d_print("error: %s\n", strerror(errno
));
548 int sample_size
= sf_get_sample_size(ip
->data
.sf
);
550 if (ip
->pcm_convert_in_place
!= NULL
)
551 ip
->pcm_convert_in_place(buf
, rc
/ sample_size
);
552 if (ip
->pcm_convert
!= NULL
)
553 ip
->pcm_convert(buffer
, tmp
, rc
/ sample_size
);
554 rc
*= ip
->pcm_convert_scale
;
559 int ip_seek(struct input_plugin
*ip
, double offset
)
564 return -IP_ERROR_FUNCTION_NOT_SUPPORTED
;
565 rc
= ip
->ops
->seek(&ip
->data
, offset
);
571 int ip_read_comments(struct input_plugin
*ip
, struct keyval
**comments
)
573 return ip
->ops
->read_comments(&ip
->data
, comments
);
576 int ip_duration(struct input_plugin
*ip
)
580 if (ip
->duration
== -1)
581 ip
->duration
= ip
->ops
->duration(&ip
->data
);
582 if (ip
->duration
< 0)
587 sample_format_t
ip_get_sf(struct input_plugin
*ip
)
593 const char *ip_get_filename(struct input_plugin
*ip
)
595 return ip
->data
.filename
;
598 const char *ip_get_metadata(struct input_plugin
*ip
)
601 return ip
->data
.metadata
;
604 int ip_is_remote(struct input_plugin
*ip
)
606 return ip
->data
.remote
;
609 int ip_metadata_changed(struct input_plugin
*ip
)
611 int ret
= ip
->data
.metadata_changed
;
614 ip
->data
.metadata_changed
= 0;
618 int ip_eof(struct input_plugin
*ip
)
624 char *ip_get_error_msg(struct input_plugin
*ip
, int rc
, const char *arg
)
630 snprintf(buffer
, sizeof(buffer
), "%s: %s", arg
, strerror(errno
));
632 case IP_ERROR_UNRECOGNIZED_FILE_TYPE
:
633 snprintf(buffer
, sizeof(buffer
),
634 "%s: unrecognized filename extension", arg
);
636 case IP_ERROR_FUNCTION_NOT_SUPPORTED
:
637 snprintf(buffer
, sizeof(buffer
),
638 "%s: function not supported", arg
);
640 case IP_ERROR_FILE_FORMAT
:
641 snprintf(buffer
, sizeof(buffer
),
642 "%s: file format not supported or corrupted file",
645 case IP_ERROR_INVALID_URI
:
646 snprintf(buffer
, sizeof(buffer
), "%s: invalid URI", arg
);
648 case IP_ERROR_SAMPLE_FORMAT
:
649 snprintf(buffer
, sizeof(buffer
),
650 "%s: input plugin doesn't support the sample format",
653 case IP_ERROR_HTTP_RESPONSE
:
654 snprintf(buffer
, sizeof(buffer
), "%s: invalid HTTP response", arg
);
656 case IP_ERROR_HTTP_STATUS
:
657 snprintf(buffer
, sizeof(buffer
), "%s: %d %s", arg
, ip
->http_code
, ip
->http_reason
);
658 free(ip
->http_reason
);
659 ip
->http_reason
= NULL
;
662 case IP_ERROR_INTERNAL
:
663 snprintf(buffer
, sizeof(buffer
), "%s: internal error", arg
);
665 case IP_ERROR_SUCCESS
:
667 snprintf(buffer
, sizeof(buffer
),
668 "%s: this is not an error (%d), this is a bug",
672 return xstrdup(buffer
);
675 char **ip_get_supported_extensions(void)
683 exts
= xnew(char *, size
);
684 list_for_each_entry(ip
, &ip_head
, node
) {
685 const char * const *e
= ip
->extensions
;
687 for (i
= 0; e
[i
]; i
++) {
688 if (count
== size
- 1) {
690 exts
= xrenew(char *, exts
, size
);
692 exts
[count
++] = xstrdup(e
[i
]);
696 qsort(exts
, count
, sizeof(char *), strptrcmp
);
700 void ip_dump_plugins(void)
705 printf("Input Plugins: %s\n", plugin_dir
);
706 list_for_each_entry(ip
, &ip_head
, node
) {
707 printf(" %s:\n File Types:", ip
->name
);
708 for (i
= 0; ip
->extensions
[i
]; i
++)
709 printf(" %s", ip
->extensions
[i
]);
710 printf("\n MIME Types:");
711 for (i
= 0; ip
->mime_types
[i
]; i
++)
712 printf(" %s", ip
->mime_types
[i
]);