Johannes Weißl
2011-03-14 05:17:50 UTC
cmus decides the input plugin for local files based on their filename
extension (which is fast and usually works). Some input plugins however
can't handle all their file types, and it would be very difficult to
implement that. An example is mp4.c, which can only decode AAC, but not
ALAC (Apple Lossless) files, which have also .m4a extension. FFmpeg
contains a decoder, but mp4.c can't call ffmpeg.c.
This patch implements a fallback system:
If the input plugin fails because it doesn't support an otherwise valid
file, it can return with a new error message (IP_ERROR_UNSUPPORTED_FILE_TYPE).
cmus will then try the next input plugin which supports the given
filename extension. The order is given by new ip_priorities.
The priorities also solve the problem when two input plugins have a
common subset of file extensions (e.g. mikmod<->modplug).
New file types supported:
* ALAC (Apple Lossless), .m4a
* non-PCM .wav files (a-law, μ-law, gsm, ...)
---
aac.c | 1 +
ffmpeg.c | 12 ++++-
flac.c | 1 +
input.c | 125 ++++++++++++++++++++++++++++++++++++++++---------------------
ip.h | 5 ++-
mad.c | 1 +
mikmod.c | 2 +-
modplug.c | 1 +
mp4.c | 6 ++-
mpc.c | 1 +
vorbis.c | 1 +
wav.c | 5 +-
wavpack.c | 1 +
13 files changed, 111 insertions(+), 51 deletions(-)
diff --git a/aac.c b/aac.c
index ec723b8..0ff0b7c 100644
--- a/aac.c
+++ b/aac.c
@@ -428,5 +428,6 @@ const struct input_plugin_ops ip_ops = {
.duration = aac_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "aac", NULL };
const char * const ip_mime_types[] = { "audio/aac", "audio/aacp", NULL };
diff --git a/ffmpeg.c b/ffmpeg.c
index 28a2796..14196f6 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -187,7 +187,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
codec = avcodec_find_decoder(cc->codec_id);
if (!codec) {
d_print("codec not found: %d, %s\n", cc->codec_id, cc->codec_name);
- err = -IP_ERROR_FILE_FORMAT;
+ err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
break;
}
@@ -196,7 +196,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
if (avcodec_open(cc, codec) < 0) {
d_print("could not open codec: %d, %s\n", cc->codec_id, cc->codec_name);
- err = -IP_ERROR_FILE_FORMAT;
+ err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
break;
}
@@ -435,9 +435,15 @@ const struct input_plugin_ops ip_ops = {
.duration = ffmpeg_duration
};
+const int ip_priority = 30;
#ifdef USE_FALLBACK_IP
const char *const ip_extensions[] = { "any", NULL };
#else
-const char *const ip_extensions[] = { "ape", "wma", "mka", NULL };
+const char *const ip_extensions[] = { "ape", "wma", "mka",
+ /* also supported by other plugins */
+ "aac", "fla", "flac", "m4a", "m4b", "mp+", "mp2", "mp3", "mp4", "mpc",
+ "mpp", "ogg", "wav", "wv",
+ NULL
+};
#endif
const char *const ip_mime_types[] = { NULL };
diff --git a/flac.c b/flac.c
index 7bdf0ec..6ca55bb 100644
--- a/flac.c
+++ b/flac.c
@@ -522,5 +522,6 @@ const struct input_plugin_ops ip_ops = {
.duration = flac_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "flac", "fla", NULL };
const char * const ip_mime_types[] = { NULL };
diff --git a/input.c b/input.c
index dcff162..69dbf2a 100644
--- a/input.c
+++ b/input.c
@@ -26,6 +26,7 @@
#include "utils.h"
#include "cmus.h"
#include "list.h"
+#include "mergesort.h"
#include "misc.h"
#include "debug.h"
#include "ui_curses.h"
@@ -75,6 +76,7 @@ struct ip {
char *name;
void *handle;
+ int priority;
const char * const *extensions;
const char * const *mime_types;
const struct input_plugin_ops *ops;
@@ -108,30 +110,22 @@ static const char *get_extension(const char *filename)
return NULL;
}
-static const struct input_plugin_ops *get_ops_by_filename(const char *filename)
+static const struct input_plugin_ops *get_ops_by_extension(const char *ext, struct list_head **headp)
{
- struct ip *ip;
- const char *ext;
- struct ip *fallback_ip = NULL;
+ struct list_head *node = *headp;
- ext = get_extension(filename);
- if (ext == NULL)
- return NULL;
- list_for_each_entry(ip, &ip_head, node) {
+ for (node = node->next; node != &ip_head; node = node->next) {
+ struct ip *ip = list_entry(node, struct ip, node);
const char * const *exts = ip->extensions;
int i;
for (i = 0; exts[i]; i++) {
- if (strcasecmp("any", exts[i]) == 0)
- fallback_ip = ip;
- if (strcasecmp(ext, exts[i]) == 0){
+ if (strcasecmp(ext, exts[i]) == 0 || strcmp("any", exts[i]) == 0) {
+ *headp = node;
return ip->ops;
}
}
}
- if (fallback_ip)
- return fallback_ip->ops;
-
return NULL;
}
@@ -374,20 +368,78 @@ static int open_remote(struct input_plugin *ip)
rc = setup_remote(ip, hg.headers, hg.fd);
http_get_free(&hg);
+ if (rc == 0)
+ rc = ip->ops->open(&ip->data);
return rc;
}
+static void ip_init(struct input_plugin *ip, char *filename)
+{
+ memset(ip, 0, sizeof(*ip));
+ ip->http_code = -1;
+ ip->pcm_convert_scale = -1;
+ ip->duration = -1;
+ ip->data.fd = -1;
+ ip->data.filename = filename;
+ ip->data.remote = is_url(filename);
+}
+
+static void ip_reset(struct input_plugin *ip, int close_fd)
+{
+ int fd = ip->data.fd;
+ free(ip->data.metadata);
+ ip_init(ip, ip->data.filename);
+ if (fd != -1) {
+ if (close_fd)
+ close(fd);
+ else {
+ lseek(fd, 0, SEEK_SET);
+ ip->data.fd = fd;
+ }
+ }
+}
+
static int open_file(struct input_plugin *ip)
{
- ip->ops = get_ops_by_filename(ip->data.filename);
- if (ip->ops == NULL)
+ const struct input_plugin_ops *ops;
+ struct list_head *head = &ip_head;
+ const char *ext;
+ int rc = 0;
+
+ ext = get_extension(ip->data.filename);
+ if (!ext)
+ return -IP_ERROR_UNRECOGNIZED_FILE_TYPE;
+
+ ops = get_ops_by_extension(ext, &head);
+ if (!ops)
return -IP_ERROR_UNRECOGNIZED_FILE_TYPE;
+
ip->data.fd = open(ip->data.filename, O_RDONLY);
- if (ip->data.fd == -1) {
- ip->ops = NULL;
+ if (ip->data.fd == -1)
return -IP_ERROR_ERRNO;
+
+ while (1) {
+ ip->ops = ops;
+ rc = ip->ops->open(&ip->data);
+ if (rc != -IP_ERROR_UNSUPPORTED_FILE_TYPE)
+ break;
+
+ ops = get_ops_by_extension(ext, &head);
+ if (!ops)
+ break;
+
+ ip_reset(ip, 0);
+ d_print("fallback: try next plugin for `%s'\n", ip->data.filename);
}
- return 0;
+
+ return rc;
+}
+
+static int sort_ip(const struct list_head *a_, const struct list_head *b_)
+{
+ const struct ip *a = list_entry(a_, struct ip, node);
+ const struct ip *b = list_entry(b_, struct ip, node);
+ return b->priority - a->priority;
}
void ip_load_plugins(void)
@@ -405,6 +457,7 @@ void ip_load_plugins(void)
struct ip *ip;
void *so;
char *ext;
+ const int *priority_ptr;
if (d->d_name[0] == '.')
continue;
@@ -424,35 +477,27 @@ void ip_load_plugins(void)
ip = xnew(struct ip, 1);
+ priority_ptr = dlsym(so, "ip_priority");
ip->extensions = dlsym(so, "ip_extensions");
ip->mime_types = dlsym(so, "ip_mime_types");
ip->ops = dlsym(so, "ip_ops");
- if (!ip->extensions || !ip->mime_types || !ip->ops) {
+ if (!priority_ptr || !ip->extensions || !ip->mime_types || !ip->ops) {
error_msg("%s: missing symbol", filename);
free(ip);
dlclose(so);
continue;
}
+ ip->priority = *priority_ptr;
ip->name = xstrndup(d->d_name, ext - d->d_name);
ip->handle = so;
list_add_tail(&ip->node, &ip_head);
}
+ list_mergesort(&ip_head, sort_ip);
closedir(dir);
}
-static void ip_init(struct input_plugin *ip, char *filename)
-{
- memset(ip, 0, sizeof(*ip));
- ip->http_code = -1;
- ip->pcm_convert_scale = -1;
- ip->duration = -1;
- ip->data.fd = -1;
- ip->data.filename = filename;
- ip->data.remote = is_url(filename);
-}
-
struct input_plugin *ip_new(const char *filename)
{
struct input_plugin *ip = xnew(struct input_plugin, 1);
@@ -475,7 +520,7 @@ int ip_open(struct input_plugin *ip)
BUG_ON(ip->open);
- /* set fd and ops */
+ /* set fd and ops, call ops->open */
if (ip->data.remote) {
rc = open_remote(ip);
} else {
@@ -485,17 +530,7 @@ int ip_open(struct input_plugin *ip)
if (rc) {
d_print("opening `%s' failed: %d %s\n", ip->data.filename, rc,
rc == -1 ? strerror(errno) : "");
- return rc;
- }
-
- rc = ip->ops->open(&ip->data);
- if (rc) {
- d_print("opening file `%s' failed: %d %s\n", ip->data.filename, rc,
- rc == -1 ? strerror(errno) : "");
- if (ip->data.fd != -1)
- close(ip->data.fd);
- free(ip->data.metadata);
- ip_init(ip, ip->data.filename);
+ ip_reset(ip, 1);
return rc;
}
ip->open = 1;
@@ -711,6 +746,10 @@ char *ip_get_error_msg(struct input_plugin *ip, int rc, const char *arg)
snprintf(buffer, sizeof(buffer),
"%s: unrecognized filename extension", arg);
break;
+ case IP_ERROR_UNSUPPORTED_FILE_TYPE:
+ snprintf(buffer, sizeof(buffer),
+ "%s: unsupported file format", arg);
+ break;
case IP_ERROR_FUNCTION_NOT_SUPPORTED:
snprintf(buffer, sizeof(buffer),
"%s: function not supported", arg);
diff --git a/ip.h b/ip.h
index f44cf29..0919b26 100644
--- a/ip.h
+++ b/ip.h
@@ -33,8 +33,10 @@ enum {
IP_ERROR_SUCCESS,
/* system error (error code in errno) */
IP_ERROR_ERRNO,
- /* file type not supported */
+ /* file type not recognized */
IP_ERROR_UNRECOGNIZED_FILE_TYPE,
+ /* file type recognized, but not supported */
+ IP_ERROR_UNSUPPORTED_FILE_TYPE,
/* function not supported (usually seek) */
IP_ERROR_FUNCTION_NOT_SUPPORTED,
/* input plugin detected corrupted file */
@@ -86,6 +88,7 @@ struct input_plugin_ops {
/* symbols exported by plugin */
extern const struct input_plugin_ops ip_ops;
+extern const int ip_priority;
extern const char * const ip_extensions[];
extern const char * const ip_mime_types[];
diff --git a/mad.c b/mad.c
index 0ae0199..6b3b9c1 100644
--- a/mad.c
+++ b/mad.c
@@ -190,6 +190,7 @@ const struct input_plugin_ops ip_ops = {
.duration = mad_duration
};
+const int ip_priority = 55;
const char * const ip_extensions[] = { "mp3", "mp2", NULL };
const char * const ip_mime_types[] = {
"audio/mpeg", "audio/x-mp3", "audio/x-mpeg", NULL
diff --git a/mikmod.c b/mikmod.c
index 86acaf2..32cd727 100644
--- a/mikmod.c
+++ b/mikmod.c
@@ -151,10 +151,10 @@ const struct input_plugin_ops ip_ops = {
.duration = mik_duration
};
+const int ip_priority = 40;
const char * const ip_extensions[] = {
"mod", "s3m", "xm", "it", "669", "amf", "dsm",
"far", "med", "mtm", "stm", "ult",
NULL
};
-
const char * const ip_mime_types[] = { NULL };
diff --git a/modplug.c b/modplug.c
index b4a5d05..61364e4 100644
--- a/modplug.c
+++ b/modplug.c
@@ -159,6 +159,7 @@ const struct input_plugin_ops ip_ops = {
.duration = mod_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = {
"mod", "s3m", "xm", "it", "669", "amf", "ams", "dbm", "dmf", "dsm",
"far", "mdl", "med", "mtm", "okt", "ptm", "stm", "ult", "umx", "mt2",
diff --git a/mp4.c b/mp4.c
index 04814ad..e75d907 100644
--- a/mp4.c
+++ b/mp4.c
@@ -98,6 +98,7 @@ static int mp4_open(struct input_plugin_data *ip_data)
NeAACDecConfigurationPtr neaac_cfg;
unsigned char *buf;
unsigned int buf_size;
+ int rc = -IP_ERROR_FILE_FORMAT;
/* http://sourceforge.net/forum/message.php?msg_id=3578887 */
@@ -127,6 +128,8 @@ static int mp4_open(struct input_plugin_data *ip_data)
priv->mp4.track = mp4_get_track(priv->mp4.handle);
if (priv->mp4.track == MP4_INVALID_TRACK_ID) {
d_print("MP4FindTrackId failed\n");
+ if (MP4GetNumberOfTracks(priv->mp4.handle, MP4_AUDIO_TRACK_TYPE, 0) > 0)
+ rc = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
goto out;
}
@@ -167,7 +170,7 @@ out:
if (priv->decoder)
NeAACDecClose(priv->decoder);
free(priv);
- return -IP_ERROR_FILE_FORMAT;
+ return rc;
}
static int mp4_close(struct input_plugin_data *ip_data)
@@ -468,5 +471,6 @@ const struct input_plugin_ops ip_ops = {
.duration = mp4_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "mp4", "m4a", "m4b", NULL };
const char * const ip_mime_types[] = { /*"audio/mp4", "audio/mp4a-latm",*/ NULL };
diff --git a/mpc.c b/mpc.c
index 0eec85c..50d7e5d 100644
--- a/mpc.c
+++ b/mpc.c
@@ -365,5 +365,6 @@ const struct input_plugin_ops ip_ops = {
.duration = mpc_duration
};
+const int ip_priority = 50;
const char *const ip_extensions[] = { "mpc", "mpp", "mp+", NULL };
const char *const ip_mime_types[] = { "audio/x-musepack", NULL };
diff --git a/vorbis.c b/vorbis.c
index 8932986..584d636 100644
--- a/vorbis.c
+++ b/vorbis.c
@@ -288,5 +288,6 @@ const struct input_plugin_ops ip_ops = {
.duration = vorbis_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "ogg", NULL };
const char * const ip_mime_types[] = { "application/ogg", "audio/x-ogg", NULL };
diff --git a/wav.c b/wav.c
index 9a7ce0c..a2f6ef1 100644
--- a/wav.c
+++ b/wav.c
@@ -177,8 +177,8 @@ static int wav_open(struct input_plugin_data *ip_data)
free(fmt);
if (format_tag != WAVE_FORMAT_PCM) {
- d_print("invalid format tag %u, should be 1\n", format_tag);
- rc = -IP_ERROR_FILE_FORMAT;
+ d_print("unsupported format tag %u, should be 1\n", format_tag);
+ rc = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
goto error_exit;
}
if ((bits != 8 && bits != 16 && bits != 24 && bits != 32) || channels < 1 || channels > 2) {
@@ -381,5 +381,6 @@ const struct input_plugin_ops ip_ops = {
.duration = wav_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "wav", NULL };
const char * const ip_mime_types[] = { NULL };
diff --git a/wavpack.c b/wavpack.c
index b1fb8c7..d9d666f 100644
--- a/wavpack.c
+++ b/wavpack.c
@@ -318,5 +318,6 @@ const struct input_plugin_ops ip_ops = {
.duration = wavpack_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "wv", NULL };
const char * const ip_mime_types[] = { "audio/x-wavpack", NULL };
extension (which is fast and usually works). Some input plugins however
can't handle all their file types, and it would be very difficult to
implement that. An example is mp4.c, which can only decode AAC, but not
ALAC (Apple Lossless) files, which have also .m4a extension. FFmpeg
contains a decoder, but mp4.c can't call ffmpeg.c.
This patch implements a fallback system:
If the input plugin fails because it doesn't support an otherwise valid
file, it can return with a new error message (IP_ERROR_UNSUPPORTED_FILE_TYPE).
cmus will then try the next input plugin which supports the given
filename extension. The order is given by new ip_priorities.
The priorities also solve the problem when two input plugins have a
common subset of file extensions (e.g. mikmod<->modplug).
New file types supported:
* ALAC (Apple Lossless), .m4a
* non-PCM .wav files (a-law, μ-law, gsm, ...)
---
aac.c | 1 +
ffmpeg.c | 12 ++++-
flac.c | 1 +
input.c | 125 ++++++++++++++++++++++++++++++++++++++++---------------------
ip.h | 5 ++-
mad.c | 1 +
mikmod.c | 2 +-
modplug.c | 1 +
mp4.c | 6 ++-
mpc.c | 1 +
vorbis.c | 1 +
wav.c | 5 +-
wavpack.c | 1 +
13 files changed, 111 insertions(+), 51 deletions(-)
diff --git a/aac.c b/aac.c
index ec723b8..0ff0b7c 100644
--- a/aac.c
+++ b/aac.c
@@ -428,5 +428,6 @@ const struct input_plugin_ops ip_ops = {
.duration = aac_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "aac", NULL };
const char * const ip_mime_types[] = { "audio/aac", "audio/aacp", NULL };
diff --git a/ffmpeg.c b/ffmpeg.c
index 28a2796..14196f6 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -187,7 +187,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
codec = avcodec_find_decoder(cc->codec_id);
if (!codec) {
d_print("codec not found: %d, %s\n", cc->codec_id, cc->codec_name);
- err = -IP_ERROR_FILE_FORMAT;
+ err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
break;
}
@@ -196,7 +196,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
if (avcodec_open(cc, codec) < 0) {
d_print("could not open codec: %d, %s\n", cc->codec_id, cc->codec_name);
- err = -IP_ERROR_FILE_FORMAT;
+ err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
break;
}
@@ -435,9 +435,15 @@ const struct input_plugin_ops ip_ops = {
.duration = ffmpeg_duration
};
+const int ip_priority = 30;
#ifdef USE_FALLBACK_IP
const char *const ip_extensions[] = { "any", NULL };
#else
-const char *const ip_extensions[] = { "ape", "wma", "mka", NULL };
+const char *const ip_extensions[] = { "ape", "wma", "mka",
+ /* also supported by other plugins */
+ "aac", "fla", "flac", "m4a", "m4b", "mp+", "mp2", "mp3", "mp4", "mpc",
+ "mpp", "ogg", "wav", "wv",
+ NULL
+};
#endif
const char *const ip_mime_types[] = { NULL };
diff --git a/flac.c b/flac.c
index 7bdf0ec..6ca55bb 100644
--- a/flac.c
+++ b/flac.c
@@ -522,5 +522,6 @@ const struct input_plugin_ops ip_ops = {
.duration = flac_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "flac", "fla", NULL };
const char * const ip_mime_types[] = { NULL };
diff --git a/input.c b/input.c
index dcff162..69dbf2a 100644
--- a/input.c
+++ b/input.c
@@ -26,6 +26,7 @@
#include "utils.h"
#include "cmus.h"
#include "list.h"
+#include "mergesort.h"
#include "misc.h"
#include "debug.h"
#include "ui_curses.h"
@@ -75,6 +76,7 @@ struct ip {
char *name;
void *handle;
+ int priority;
const char * const *extensions;
const char * const *mime_types;
const struct input_plugin_ops *ops;
@@ -108,30 +110,22 @@ static const char *get_extension(const char *filename)
return NULL;
}
-static const struct input_plugin_ops *get_ops_by_filename(const char *filename)
+static const struct input_plugin_ops *get_ops_by_extension(const char *ext, struct list_head **headp)
{
- struct ip *ip;
- const char *ext;
- struct ip *fallback_ip = NULL;
+ struct list_head *node = *headp;
- ext = get_extension(filename);
- if (ext == NULL)
- return NULL;
- list_for_each_entry(ip, &ip_head, node) {
+ for (node = node->next; node != &ip_head; node = node->next) {
+ struct ip *ip = list_entry(node, struct ip, node);
const char * const *exts = ip->extensions;
int i;
for (i = 0; exts[i]; i++) {
- if (strcasecmp("any", exts[i]) == 0)
- fallback_ip = ip;
- if (strcasecmp(ext, exts[i]) == 0){
+ if (strcasecmp(ext, exts[i]) == 0 || strcmp("any", exts[i]) == 0) {
+ *headp = node;
return ip->ops;
}
}
}
- if (fallback_ip)
- return fallback_ip->ops;
-
return NULL;
}
@@ -374,20 +368,78 @@ static int open_remote(struct input_plugin *ip)
rc = setup_remote(ip, hg.headers, hg.fd);
http_get_free(&hg);
+ if (rc == 0)
+ rc = ip->ops->open(&ip->data);
return rc;
}
+static void ip_init(struct input_plugin *ip, char *filename)
+{
+ memset(ip, 0, sizeof(*ip));
+ ip->http_code = -1;
+ ip->pcm_convert_scale = -1;
+ ip->duration = -1;
+ ip->data.fd = -1;
+ ip->data.filename = filename;
+ ip->data.remote = is_url(filename);
+}
+
+static void ip_reset(struct input_plugin *ip, int close_fd)
+{
+ int fd = ip->data.fd;
+ free(ip->data.metadata);
+ ip_init(ip, ip->data.filename);
+ if (fd != -1) {
+ if (close_fd)
+ close(fd);
+ else {
+ lseek(fd, 0, SEEK_SET);
+ ip->data.fd = fd;
+ }
+ }
+}
+
static int open_file(struct input_plugin *ip)
{
- ip->ops = get_ops_by_filename(ip->data.filename);
- if (ip->ops == NULL)
+ const struct input_plugin_ops *ops;
+ struct list_head *head = &ip_head;
+ const char *ext;
+ int rc = 0;
+
+ ext = get_extension(ip->data.filename);
+ if (!ext)
+ return -IP_ERROR_UNRECOGNIZED_FILE_TYPE;
+
+ ops = get_ops_by_extension(ext, &head);
+ if (!ops)
return -IP_ERROR_UNRECOGNIZED_FILE_TYPE;
+
ip->data.fd = open(ip->data.filename, O_RDONLY);
- if (ip->data.fd == -1) {
- ip->ops = NULL;
+ if (ip->data.fd == -1)
return -IP_ERROR_ERRNO;
+
+ while (1) {
+ ip->ops = ops;
+ rc = ip->ops->open(&ip->data);
+ if (rc != -IP_ERROR_UNSUPPORTED_FILE_TYPE)
+ break;
+
+ ops = get_ops_by_extension(ext, &head);
+ if (!ops)
+ break;
+
+ ip_reset(ip, 0);
+ d_print("fallback: try next plugin for `%s'\n", ip->data.filename);
}
- return 0;
+
+ return rc;
+}
+
+static int sort_ip(const struct list_head *a_, const struct list_head *b_)
+{
+ const struct ip *a = list_entry(a_, struct ip, node);
+ const struct ip *b = list_entry(b_, struct ip, node);
+ return b->priority - a->priority;
}
void ip_load_plugins(void)
@@ -405,6 +457,7 @@ void ip_load_plugins(void)
struct ip *ip;
void *so;
char *ext;
+ const int *priority_ptr;
if (d->d_name[0] == '.')
continue;
@@ -424,35 +477,27 @@ void ip_load_plugins(void)
ip = xnew(struct ip, 1);
+ priority_ptr = dlsym(so, "ip_priority");
ip->extensions = dlsym(so, "ip_extensions");
ip->mime_types = dlsym(so, "ip_mime_types");
ip->ops = dlsym(so, "ip_ops");
- if (!ip->extensions || !ip->mime_types || !ip->ops) {
+ if (!priority_ptr || !ip->extensions || !ip->mime_types || !ip->ops) {
error_msg("%s: missing symbol", filename);
free(ip);
dlclose(so);
continue;
}
+ ip->priority = *priority_ptr;
ip->name = xstrndup(d->d_name, ext - d->d_name);
ip->handle = so;
list_add_tail(&ip->node, &ip_head);
}
+ list_mergesort(&ip_head, sort_ip);
closedir(dir);
}
-static void ip_init(struct input_plugin *ip, char *filename)
-{
- memset(ip, 0, sizeof(*ip));
- ip->http_code = -1;
- ip->pcm_convert_scale = -1;
- ip->duration = -1;
- ip->data.fd = -1;
- ip->data.filename = filename;
- ip->data.remote = is_url(filename);
-}
-
struct input_plugin *ip_new(const char *filename)
{
struct input_plugin *ip = xnew(struct input_plugin, 1);
@@ -475,7 +520,7 @@ int ip_open(struct input_plugin *ip)
BUG_ON(ip->open);
- /* set fd and ops */
+ /* set fd and ops, call ops->open */
if (ip->data.remote) {
rc = open_remote(ip);
} else {
@@ -485,17 +530,7 @@ int ip_open(struct input_plugin *ip)
if (rc) {
d_print("opening `%s' failed: %d %s\n", ip->data.filename, rc,
rc == -1 ? strerror(errno) : "");
- return rc;
- }
-
- rc = ip->ops->open(&ip->data);
- if (rc) {
- d_print("opening file `%s' failed: %d %s\n", ip->data.filename, rc,
- rc == -1 ? strerror(errno) : "");
- if (ip->data.fd != -1)
- close(ip->data.fd);
- free(ip->data.metadata);
- ip_init(ip, ip->data.filename);
+ ip_reset(ip, 1);
return rc;
}
ip->open = 1;
@@ -711,6 +746,10 @@ char *ip_get_error_msg(struct input_plugin *ip, int rc, const char *arg)
snprintf(buffer, sizeof(buffer),
"%s: unrecognized filename extension", arg);
break;
+ case IP_ERROR_UNSUPPORTED_FILE_TYPE:
+ snprintf(buffer, sizeof(buffer),
+ "%s: unsupported file format", arg);
+ break;
case IP_ERROR_FUNCTION_NOT_SUPPORTED:
snprintf(buffer, sizeof(buffer),
"%s: function not supported", arg);
diff --git a/ip.h b/ip.h
index f44cf29..0919b26 100644
--- a/ip.h
+++ b/ip.h
@@ -33,8 +33,10 @@ enum {
IP_ERROR_SUCCESS,
/* system error (error code in errno) */
IP_ERROR_ERRNO,
- /* file type not supported */
+ /* file type not recognized */
IP_ERROR_UNRECOGNIZED_FILE_TYPE,
+ /* file type recognized, but not supported */
+ IP_ERROR_UNSUPPORTED_FILE_TYPE,
/* function not supported (usually seek) */
IP_ERROR_FUNCTION_NOT_SUPPORTED,
/* input plugin detected corrupted file */
@@ -86,6 +88,7 @@ struct input_plugin_ops {
/* symbols exported by plugin */
extern const struct input_plugin_ops ip_ops;
+extern const int ip_priority;
extern const char * const ip_extensions[];
extern const char * const ip_mime_types[];
diff --git a/mad.c b/mad.c
index 0ae0199..6b3b9c1 100644
--- a/mad.c
+++ b/mad.c
@@ -190,6 +190,7 @@ const struct input_plugin_ops ip_ops = {
.duration = mad_duration
};
+const int ip_priority = 55;
const char * const ip_extensions[] = { "mp3", "mp2", NULL };
const char * const ip_mime_types[] = {
"audio/mpeg", "audio/x-mp3", "audio/x-mpeg", NULL
diff --git a/mikmod.c b/mikmod.c
index 86acaf2..32cd727 100644
--- a/mikmod.c
+++ b/mikmod.c
@@ -151,10 +151,10 @@ const struct input_plugin_ops ip_ops = {
.duration = mik_duration
};
+const int ip_priority = 40;
const char * const ip_extensions[] = {
"mod", "s3m", "xm", "it", "669", "amf", "dsm",
"far", "med", "mtm", "stm", "ult",
NULL
};
-
const char * const ip_mime_types[] = { NULL };
diff --git a/modplug.c b/modplug.c
index b4a5d05..61364e4 100644
--- a/modplug.c
+++ b/modplug.c
@@ -159,6 +159,7 @@ const struct input_plugin_ops ip_ops = {
.duration = mod_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = {
"mod", "s3m", "xm", "it", "669", "amf", "ams", "dbm", "dmf", "dsm",
"far", "mdl", "med", "mtm", "okt", "ptm", "stm", "ult", "umx", "mt2",
diff --git a/mp4.c b/mp4.c
index 04814ad..e75d907 100644
--- a/mp4.c
+++ b/mp4.c
@@ -98,6 +98,7 @@ static int mp4_open(struct input_plugin_data *ip_data)
NeAACDecConfigurationPtr neaac_cfg;
unsigned char *buf;
unsigned int buf_size;
+ int rc = -IP_ERROR_FILE_FORMAT;
/* http://sourceforge.net/forum/message.php?msg_id=3578887 */
@@ -127,6 +128,8 @@ static int mp4_open(struct input_plugin_data *ip_data)
priv->mp4.track = mp4_get_track(priv->mp4.handle);
if (priv->mp4.track == MP4_INVALID_TRACK_ID) {
d_print("MP4FindTrackId failed\n");
+ if (MP4GetNumberOfTracks(priv->mp4.handle, MP4_AUDIO_TRACK_TYPE, 0) > 0)
+ rc = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
goto out;
}
@@ -167,7 +170,7 @@ out:
if (priv->decoder)
NeAACDecClose(priv->decoder);
free(priv);
- return -IP_ERROR_FILE_FORMAT;
+ return rc;
}
static int mp4_close(struct input_plugin_data *ip_data)
@@ -468,5 +471,6 @@ const struct input_plugin_ops ip_ops = {
.duration = mp4_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "mp4", "m4a", "m4b", NULL };
const char * const ip_mime_types[] = { /*"audio/mp4", "audio/mp4a-latm",*/ NULL };
diff --git a/mpc.c b/mpc.c
index 0eec85c..50d7e5d 100644
--- a/mpc.c
+++ b/mpc.c
@@ -365,5 +365,6 @@ const struct input_plugin_ops ip_ops = {
.duration = mpc_duration
};
+const int ip_priority = 50;
const char *const ip_extensions[] = { "mpc", "mpp", "mp+", NULL };
const char *const ip_mime_types[] = { "audio/x-musepack", NULL };
diff --git a/vorbis.c b/vorbis.c
index 8932986..584d636 100644
--- a/vorbis.c
+++ b/vorbis.c
@@ -288,5 +288,6 @@ const struct input_plugin_ops ip_ops = {
.duration = vorbis_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "ogg", NULL };
const char * const ip_mime_types[] = { "application/ogg", "audio/x-ogg", NULL };
diff --git a/wav.c b/wav.c
index 9a7ce0c..a2f6ef1 100644
--- a/wav.c
+++ b/wav.c
@@ -177,8 +177,8 @@ static int wav_open(struct input_plugin_data *ip_data)
free(fmt);
if (format_tag != WAVE_FORMAT_PCM) {
- d_print("invalid format tag %u, should be 1\n", format_tag);
- rc = -IP_ERROR_FILE_FORMAT;
+ d_print("unsupported format tag %u, should be 1\n", format_tag);
+ rc = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
goto error_exit;
}
if ((bits != 8 && bits != 16 && bits != 24 && bits != 32) || channels < 1 || channels > 2) {
@@ -381,5 +381,6 @@ const struct input_plugin_ops ip_ops = {
.duration = wav_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "wav", NULL };
const char * const ip_mime_types[] = { NULL };
diff --git a/wavpack.c b/wavpack.c
index b1fb8c7..d9d666f 100644
--- a/wavpack.c
+++ b/wavpack.c
@@ -318,5 +318,6 @@ const struct input_plugin_ops ip_ops = {
.duration = wavpack_duration
};
+const int ip_priority = 50;
const char * const ip_extensions[] = { "wv", NULL };
const char * const ip_mime_types[] = { "audio/x-wavpack", NULL };
--
1.7.4.1
1.7.4.1