summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deadbeef.h30
-rw-r--r--playlist.c24
-rw-r--r--plugins.c11
-rw-r--r--plugins/aac/aac.c16
-rw-r--r--plugins/ffmpeg/ffmpeg.c6
-rw-r--r--plugins/mms/mmsplug.c14
-rw-r--r--plugins/mpgmad/mpgmad.c10
-rw-r--r--plugins/vfs_curl/vfs_curl.c14
-rw-r--r--plugins/vorbis/vorbis.c10
-rw-r--r--vfs.c9
-rw-r--r--vfs_stdio.c1
11 files changed, 103 insertions, 42 deletions
diff --git a/deadbeef.h b/deadbeef.h
index 5efd7156..49abfdee 100644
--- a/deadbeef.h
+++ b/deadbeef.h
@@ -30,6 +30,7 @@
#include <stdint.h>
#include <time.h>
#include <stdio.h>
+#include <dirent.h>
#ifdef __cplusplus
extern "C" {
@@ -524,6 +525,7 @@ typedef struct {
int (*conf_save) (void);
// plugin communication
struct DB_decoder_s **(*plug_get_decoder_list) (void);
+ struct DB_vfs_s **(*plug_get_vfs_list) (void);
struct DB_output_s **(*plug_get_output_list) (void);
struct DB_dsp_s **(*plug_get_dsp_list) (void);
struct DB_playlist_s **(*plug_get_playlist_list) (void);
@@ -818,18 +820,38 @@ typedef struct {
// api is based on stdio
typedef struct DB_vfs_s {
DB_plugin_t plugin;
+
+// capabilities
+ const char **(*get_schemes) (void); // NULL-terminated list of supported schemes, e.g. {"http", "ftp", NULL}; can be NULL
+
+ const char **(*get_container_extensions) (void); // NULL-terminated list of supported container files, e.g. { "zip", NULL }; can be NULL
+
+ int (*is_streaming) (void); // return 1 if the plugin streaming data over slow connection, e.g. http; plugins will avoid scanning entire files if this is the case
+
+ const char * (*is_container) (const char *fname); // should return scheme name, if the file is supported container format. "zip" is an example
+
+// this is an evil hack to interrupt frozen vfs_curl streams
+// FIXME: pass it through command API
+ void (*abort) (DB_FILE *stream);
+
+// file access, follows stdio API with few extension
DB_FILE* (*open) (const char *fname);
- void (*set_track) (DB_FILE *f, DB_playItem_t *it);
void (*close) (DB_FILE *f);
size_t (*read) (void *ptr, size_t size, size_t nmemb, DB_FILE *stream);
int (*seek) (DB_FILE *stream, int64_t offset, int whence);
int64_t (*tell) (DB_FILE *stream);
void (*rewind) (DB_FILE *stream);
int64_t (*getlength)(DB_FILE *stream);
+
+ // should return mime-type of a stream, if known; can be NULL
const char * (*get_content_type) (DB_FILE *stream);
- void (*abort) (DB_FILE *stream);
- const char **scheme_names; // NULL-terminated list of supported schemes, e.g. {"http", "ftp", NULL}
- unsigned streaming : 1;
+
+ // associates stream with a track, to allow dynamic metadata updating, like
+ // in icy protocol
+ void (*set_track) (DB_FILE *f, DB_playItem_t *it);
+
+// folder access, follows dirent API, and uses dirent data structures
+ int (*scandir) (const char *dir, struct dirent ***namelist, int (*selector) (const struct dirent *), int (*cmp) (const struct dirent **, const struct dirent **));
} DB_vfs_t;
// gui plugin
diff --git a/playlist.c b/playlist.c
index fb170f98..a6ff3633 100644
--- a/playlist.c
+++ b/playlist.c
@@ -1305,6 +1305,9 @@ pl_insert_pls (playItem_t *after, const char *fname, int *pabort, int (*cb)(play
}
playItem_t *
+pl_insert_dir_int (DB_vfs_t *vfs, playItem_t *after, const char *dirname, int *pabort, int (*cb)(playItem_t *it, void *data), void *user_data);
+
+playItem_t *
pl_insert_file (playItem_t *after, const char *fname, int *pabort, int (*cb)(playItem_t *it, void *data), void *user_data) {
trace ("count: %d\n", playlist->count[PL_MAIN]);
trace ("pl_insert_file %s\n", fname);
@@ -1312,6 +1315,21 @@ pl_insert_file (playItem_t *after, const char *fname, int *pabort, int (*cb)(pla
return NULL;
}
+ // check if that is supported container format
+ DB_vfs_t **vfsplugs = plug_get_vfs_list ();
+ for (int i = 0; vfsplugs[i]; i++) {
+ if (vfsplugs[i]->is_container) {
+ const char *scheme = vfsplugs[i]->is_container (fname);
+ if (scheme) {
+ playItem_t *it = pl_insert_dir_int (vfsplugs[i], after, fname, pabort, cb, user_data);
+ if (it) {
+ return it;
+ }
+ }
+ }
+ }
+
+
// detect decoder
const char *eol = strrchr (fname, '.');
if (!eol) {
@@ -1428,7 +1446,7 @@ static int dirent_alphasort (const struct dirent **a, const struct dirent **b) {
static int follow_symlinks = 0;
playItem_t *
-pl_insert_dir_int (playItem_t *after, const char *dirname, int *pabort, int (*cb)(playItem_t *it, void *data), void *user_data) {
+pl_insert_dir_int (DB_vfs_t *vfs, playItem_t *after, const char *dirname, int *pabort, int (*cb)(playItem_t *it, void *data), void *user_data) {
if (!memcmp (dirname, "file://", 7)) {
dirname += 7;
}
@@ -1459,7 +1477,7 @@ pl_insert_dir_int (playItem_t *after, const char *dirname, int *pabort, int (*cb
{
char fullname[PATH_MAX];
snprintf (fullname, sizeof (fullname), "%s/%s", dirname, namelist[i]->d_name);
- playItem_t *inserted = pl_insert_dir_int (after, fullname, pabort, cb, user_data);
+ playItem_t *inserted = pl_insert_dir_int (vfs, after, fullname, pabort, cb, user_data);
if (!inserted) {
inserted = pl_insert_file (after, fullname, pabort, cb, user_data);
}
@@ -1480,7 +1498,7 @@ pl_insert_dir_int (playItem_t *after, const char *dirname, int *pabort, int (*cb
playItem_t *
pl_insert_dir (playItem_t *after, const char *dirname, int *pabort, int (*cb)(playItem_t *it, void *data), void *user_data) {
follow_symlinks = conf_get_int ("add_folders_follow_symlinks", 0);
- return pl_insert_dir_int (after, dirname, pabort, cb, user_data);
+ return pl_insert_dir_int (NULL, after, dirname, pabort, cb, user_data);
}
int
diff --git a/plugins.c b/plugins.c
index 4d6c15ac..adc1ded2 100644
--- a/plugins.c
+++ b/plugins.c
@@ -249,6 +249,7 @@ static DB_functions_t deadbeef_api = {
.conf_save = conf_save,
// plugin communication
.plug_get_decoder_list = plug_get_decoder_list,
+ .plug_get_vfs_list = plug_get_vfs_list,
.plug_get_output_list = plug_get_output_list,
.plug_get_dsp_list = plug_get_dsp_list,
.plug_get_playlist_list = plug_get_playlist_list,
@@ -952,16 +953,16 @@ plug_get_decoder_list (void) {
return g_decoder_plugins;
}
-struct DB_output_s **
-plug_get_output_list (void) {
- return g_output_plugins;
-}
-
struct DB_vfs_s **
plug_get_vfs_list (void) {
return g_vfs_plugins;
}
+struct DB_output_s **
+plug_get_output_list (void) {
+ return g_output_plugins;
+}
+
struct DB_dsp_s **
plug_get_dsp_list (void) {
return g_dsp_plugins;
diff --git a/plugins/aac/aac.c b/plugins/aac/aac.c
index 25dc10e8..2623d30a 100644
--- a/plugins/aac/aac.c
+++ b/plugins/aac/aac.c
@@ -136,7 +136,7 @@ parse_aac_stream(DB_FILE *fp, int *psamplerate, int *pchannels, float *pduration
int firstframepos = -1;
int fsize = -1;
int offs = 0;
- if (!fp->vfs->streaming) {
+ if (!fp->vfs->is_streaming ()) {
int skip = deadbeef->junk_get_leading_size (fp);
if (skip >= 0) {
deadbeef->fseek (fp, skip, SEEK_SET);
@@ -160,7 +160,7 @@ parse_aac_stream(DB_FILE *fp, int *psamplerate, int *pchannels, float *pduration
int frame = 0;
int scanframes = 1000;
- if (fp->vfs->streaming) {
+ if (fp->vfs->is_streaming ()) {
scanframes = 1;
}
@@ -382,7 +382,7 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
int totalsamples = -1;
int offs = -1;
- if (!info->file->vfs->streaming) {
+ if (!info->file->vfs->is_streaming ()) {
int skip = deadbeef->junk_get_leading_size (info->file);
if (skip >= 0) {
deadbeef->fseek (info->file, skip, SEEK_SET);
@@ -408,7 +408,7 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
info->mp4reader.close = aac_fs_close;
#endif
- if (!info->file->vfs->streaming) {
+ if (!info->file->vfs->is_streaming ()) {
#ifdef USE_MP4FF
trace ("aac_init: mp4ff_open_read %s\n", it->fname);
info->mp4file = mp4ff_open_read (&info->mp4reader);
@@ -611,7 +611,7 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
_info->fmt.channels = ch;
}
- if (!info->file->vfs->streaming) {
+ if (!info->file->vfs->is_streaming ()) {
if (it->endsample > 0) {
info->startsample = it->startsample;
info->endsample = it->endsample;
@@ -658,7 +658,7 @@ static int
aac_read (DB_fileinfo_t *_info, char *bytes, int size) {
aac_info_t *info = (aac_info_t *)_info;
int samplesize = _info->fmt.channels * _info->fmt.bps / 8;
- if (!info->file->vfs->streaming) {
+ if (!info->file->vfs->is_streaming ()) {
if (info->currentsample + size / samplesize > info->endsample) {
size = (info->endsample - info->currentsample + 1) * samplesize;
if (size <= 0) {
@@ -1011,7 +1011,7 @@ aac_read_metadata (DB_playItem_t *it) {
return -1;
}
- if (fp->vfs->streaming) {
+ if (fp->vfs->is_streaming ()) {
deadbeef->fclose (fp);
return -1;
}
@@ -1058,7 +1058,7 @@ aac_insert (DB_playItem_t *after, const char *fname) {
int mp4track = -1;
MP4FILE mp4 = NULL;
- if (fp->vfs->streaming) {
+ if (fp->vfs->is_streaming ()) {
trace ("streaming aac (%s)\n", fname);
ftype = plugin.filetypes[0];
}
diff --git a/plugins/ffmpeg/ffmpeg.c b/plugins/ffmpeg/ffmpeg.c
index 888a4a84..cda37555 100644
--- a/plugins/ffmpeg/ffmpeg.c
+++ b/plugins/ffmpeg/ffmpeg.c
@@ -577,7 +577,7 @@ ffmpeg_vfs_open(URLContext *h, const char *filename, int flags)
if (f == NULL)
return -ENOENT;
- if (f->vfs->streaming) {
+ if (f->vfs->is_streaming ()) {
deadbeef->fset_track (f, current_track);
if (current_info) {
current_info->file = f;
@@ -609,9 +609,9 @@ ffmpeg_vfs_seek(URLContext *h, int64_t pos, int whence)
DB_FILE *f = h->priv_data;
if (whence == AVSEEK_SIZE) {
- return f->vfs->streaming ? -1 : deadbeef->fgetlength (h->priv_data);
+ return f->vfs->is_streaming () ? -1 : deadbeef->fgetlength (h->priv_data);
}
- else if (f->vfs->streaming) {
+ else if (f->vfs->is_streaming ()) {
return -1;
}
else {
diff --git a/plugins/mms/mmsplug.c b/plugins/mms/mmsplug.c
index 28d3f513..41f60ba5 100644
--- a/plugins/mms/mmsplug.c
+++ b/plugins/mms/mmsplug.c
@@ -108,6 +108,16 @@ mms_get_content_type (DB_FILE *stream) {
static const char *scheme_names[] = { "mms://", "mmsh://", NULL };
+const char **
+mms_get_schemes (void) {
+ return scheme_names;
+}
+
+int
+mms_is_streaming (void) {
+ return 1;
+}
+
static DB_vfs_t plugin = {
DB_PLUGIN_SET_API_VERSION
.plugin.version_major = 1,
@@ -126,8 +136,8 @@ static DB_vfs_t plugin = {
.rewind = mms_rewind,
.getlength = mms_getlength,
.get_content_type = mms_get_content_type,
- .scheme_names = scheme_names,
- .streaming = 1
+ .get_schemes = mms_get_schemes,
+ .is_streaming = mms_is_streaming,
};
DB_plugin_t *
diff --git a/plugins/mpgmad/mpgmad.c b/plugins/mpgmad/mpgmad.c
index 1803ece6..d718198e 100644
--- a/plugins/mpgmad/mpgmad.c
+++ b/plugins/mpgmad/mpgmad.c
@@ -379,7 +379,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
if (sample <= 0 && !buffer->have_xing_header)
{
size_t framepos = deadbeef->ftell (buffer->file);
- if (!buffer->file->vfs->streaming) {
+ if (!buffer->file->vfs->is_streaming ()) {
// trace ("trying to read xing header at pos %d\n", framepos);
if (ver == 1) {
deadbeef->fseek (buffer->file, 32, SEEK_CUR);
@@ -489,7 +489,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
// xing header failed, calculate based on file size
// trace ("xing header failed\n");
buffer->samplerate = samplerate;
- if (buffer->file->vfs->streaming) {
+ if (buffer->file->vfs->is_streaming ()) {
// only suitable for cbr files, used if streaming
int sz = deadbeef->fgetlength (buffer->file) - buffer->startoffset - buffer->endoffset;
if (sz < 0) {
@@ -621,7 +621,7 @@ cmp3_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
deadbeef->pl_item_ref (it);
info->buffer.it = it;
info->info.readpos = 0;
- if (!info->buffer.file->vfs->streaming) {
+ if (!info->buffer.file->vfs->is_streaming ()) {
int skip = deadbeef->junk_get_leading_size (info->buffer.file);
if (skip > 0) {
trace ("mpgmad: skipping %d(%xH) bytes of junk\n", skip, skip);
@@ -1057,7 +1057,7 @@ cmp3_seek_sample (DB_fileinfo_t *_info, int sample) {
return -1;
}
- if (info->buffer.file->vfs->streaming) {
+ if (info->buffer.file->vfs->is_streaming ()) {
if (info->buffer.totalsamples > 0 && info->buffer.avg_samples_per_frame && info->buffer.avg_packetlength) { // that means seekable remote stream, like podcast
trace ("seeking is possible!\n");
// get length excluding id3v2
@@ -1156,7 +1156,7 @@ cmp3_insert (DB_playItem_t *after, const char *fname) {
trace ("failed to open file %s\n", fname);
return NULL;
}
- if (fp->vfs->streaming) {
+ if (fp->vfs->is_streaming ()) {
DB_playItem_t *it = deadbeef->pl_item_alloc ();
it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id);
it->fname = strdup (fname);
diff --git a/plugins/vfs_curl/vfs_curl.c b/plugins/vfs_curl/vfs_curl.c
index 28b1dae7..607b1b6c 100644
--- a/plugins/vfs_curl/vfs_curl.c
+++ b/plugins/vfs_curl/vfs_curl.c
@@ -908,6 +908,16 @@ vfs_curl_stop (void) {
static const char *scheme_names[] = { "http://", "ftp://", NULL };
+const char **
+http_get_schemes (void) {
+ return scheme_names;
+}
+
+int
+http_is_streaming (void) {
+ return 1;
+}
+
// standard stdio vfs
static DB_vfs_t plugin = {
DB_PLUGIN_SET_API_VERSION
@@ -932,8 +942,8 @@ static DB_vfs_t plugin = {
.rewind = http_rewind,
.getlength = http_getlength,
.get_content_type = http_get_content_type,
- .scheme_names = scheme_names,
- .streaming = 1
+ .get_schemes = http_get_schemes,
+ .is_streaming = http_is_streaming,
};
DB_plugin_t *
diff --git a/plugins/vorbis/vorbis.c b/plugins/vorbis/vorbis.c
index dc3e3a72..c4958d39 100644
--- a/plugins/vorbis/vorbis.c
+++ b/plugins/vorbis/vorbis.c
@@ -184,7 +184,7 @@ cvorbis_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
return -1;
}
int ln = deadbeef->fgetlength (info->info.file);
- if (info->info.file->vfs->streaming && ln == -1) {
+ if (info->info.file->vfs->is_streaming () && ln == -1) {
ov_callbacks ovcb = {
.read_func = cvorbis_fread,
.seek_func = NULL,
@@ -238,7 +238,7 @@ cvorbis_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
_info->readpos = 0;
info->currentsample = 0;
- if (!info->info.file->vfs->streaming) {
+ if (!info->info.file->vfs->is_streaming ()) {
if (it->endsample > 0) {
info->startsample = it->startsample;
info->endsample = it->endsample;
@@ -285,7 +285,7 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) {
int samplesize = _info->fmt.channels * _info->fmt.bps / 8;
- if (!info->info.file->vfs->streaming) {
+ if (!info->info.file->vfs->is_streaming ()) {
if (info->currentsample + size / samplesize > info->endsample) {
size = (info->endsample - info->currentsample + 1) * samplesize;
trace ("size truncated to %d bytes, cursample=%d, info->endsample=%d, totalsamples=%d\n", size, info->currentsample, info->endsample, ov_pcm_total (&info->vorbis_file, -1));
@@ -427,7 +427,7 @@ cvorbis_insert (DB_playItem_t *after, const char *fname) {
trace ("vorbis: failed to fopen %s\n", fname);
return NULL;
}
- if (fp->vfs->streaming) {
+ if (fp->vfs->is_streaming ()) {
DB_playItem_t *it = deadbeef->pl_item_alloc ();
it->fname = strdup (fname);
it->filetype = "OggVorbis";
@@ -535,7 +535,7 @@ cvorbis_read_metadata (DB_playItem_t *it) {
trace ("cvorbis_read_metadata: failed to fopen %s\n", it->fname);
return -1;
}
- if (fp->vfs->streaming) {
+ if (fp->vfs->is_streaming ()) {
trace ("cvorbis_read_metadata: failed to fopen %s\n", it->fname);
goto error;
}
diff --git a/vfs.c b/vfs.c
index d91b59ab..08badec1 100644
--- a/vfs.c
+++ b/vfs.c
@@ -32,14 +32,15 @@ vfs_fopen (const char *fname) {
int i;
for (i = 0; plugs[i]; i++) {
DB_vfs_t *p = plugs[i];
- if (!p->scheme_names) {
+ if (!p->get_schemes) {
fallback = p;
continue;
}
int n;
- for (n = 0; p->scheme_names[n]; n++) {
- size_t l = strlen (p->scheme_names[n]);
- if (!strncasecmp (p->scheme_names[n], fname, l)) {
+ const char **scheme_names = p->get_schemes ();
+ for (n = 0; scheme_names[n]; n++) {
+ size_t l = strlen (scheme_names[n]);
+ if (!strncasecmp (scheme_names[n], fname, l)) {
return p->open (fname);
}
}
diff --git a/vfs_stdio.c b/vfs_stdio.c
index 6918c26d..68d225cd 100644
--- a/vfs_stdio.c
+++ b/vfs_stdio.c
@@ -112,7 +112,6 @@ static DB_vfs_t plugin = {
.rewind = stdio_rewind,
.getlength = stdio_getlength,
.get_content_type = stdio_get_content_type,
- .scheme_names = NULL // this is NULL because that's a fallback vfs, used when no other matching vfs plugin found
};
DB_plugin_t *