From 80e8e0f4a5a214de8884be245049b5fbc53cf4aa Mon Sep 17 00:00:00 2001 From: waker Date: Mon, 31 Jan 2011 19:29:57 +0100 Subject: added container support to vfs plugin api --- deadbeef.h | 30 ++++++++++++++++++++++++++---- playlist.c | 24 +++++++++++++++++++++--- plugins.c | 11 ++++++----- plugins/aac/aac.c | 16 ++++++++-------- plugins/ffmpeg/ffmpeg.c | 6 +++--- plugins/mms/mmsplug.c | 14 ++++++++++++-- plugins/mpgmad/mpgmad.c | 10 +++++----- plugins/vfs_curl/vfs_curl.c | 14 ++++++++++++-- plugins/vorbis/vorbis.c | 10 +++++----- vfs.c | 9 +++++---- vfs_stdio.c | 1 - 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 #include #include +#include #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 @@ -1304,6 +1304,9 @@ pl_insert_pls (playItem_t *after, const char *fname, int *pabort, int (*cb)(play return after; } +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]); @@ -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 * -- cgit v1.2.3