diff options
author | Alexey Yakovenko <wakeroid@gmail.com> | 2009-09-29 22:47:50 +0200 |
---|---|---|
committer | Alexey Yakovenko <wakeroid@gmail.com> | 2009-09-29 22:47:50 +0200 |
commit | fc2689bbd0af9323a366d6fe6b37f613e28fc53f (patch) | |
tree | 32cef377a22338d1bde1e26d32e0313318651550 | |
parent | 7ad596b7eb25cc2ab18b439d6159b0e12205c2f5 (diff) |
vfs_curl WIP
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | callbacks.c | 5 | ||||
-rw-r--r-- | configure.ac | 17 | ||||
-rw-r--r-- | deadbeef.h | 1 | ||||
-rw-r--r-- | gtkplaylist.c | 4 | ||||
-rw-r--r-- | playlist.c | 4 | ||||
-rw-r--r-- | plugins/lastfm/Makefile.am | 4 | ||||
-rw-r--r-- | plugins/vfs_curl/Makefile.am | 7 | ||||
-rw-r--r-- | plugins/vfs_curl/vfs_curl.c | 246 | ||||
-rw-r--r-- | vfs.c | 5 | ||||
-rw-r--r-- | vfs_stdio.c | 17 |
11 files changed, 297 insertions, 16 deletions
diff --git a/Makefile.am b/Makefile.am index 1e0a07d2..2c23dcb8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,7 +14,8 @@ SUBDIRS = gme/Game_Music_Emu-0.5.2\ ${VORBIS_DIR}\ ${FLAC_DIR}\ ${WAVPACK_DIR}\ - ${SNDFILE_DIR} + ${SNDFILE_DIR}\ + ${VFS_CURL_DIR} dumbpath=@top_srcdir@/dumb sidpath=@top_srcdir@/sid/sidplay-libs-2.1.0 diff --git a/callbacks.c b/callbacks.c index f03c36c4..082715cb 100644 --- a/callbacks.c +++ b/callbacks.c @@ -597,9 +597,10 @@ on_playlist_drag_data_received (GtkWidget *widget, GTKPL_PROLOGUE; gchar *ptr=(char*)data->data; if (target_type == 0) { // uris - if (!strncmp(ptr,"file:///",8)) { + fprintf (stderr, "calling gtkpl_handle_fm_drag_drop\n"); +// if (!strncmp(ptr,"file:///",8)) { gtkpl_handle_fm_drag_drop (ps, y, ptr, data->length); - } +// } } else if (target_type == 1) { uint32_t *d= (uint32_t *)ptr; diff --git a/configure.ac b/configure.ac index 2f268ce4..062cdd28 100644 --- a/configure.ac +++ b/configure.ac @@ -47,12 +47,16 @@ if test ${HAVE_SSE2}; then fi AC_SUBST(SIMD_FLAGS) -dnl lastfm plugin +dnl curl lib AC_CHECK_LIB([curl], [main], [HAVE_CURL=1]) if test ${HAVE_CURL}; then - LFM_LIBS="-lcurl" + CURL_LIBS="-lcurl" + AC_SUBST(CURL_LIBS) +fi + +dnl lastfm plugin +if test ${HAVE_CURL}; then LFM_DIR="plugins/lastfm" - AC_SUBST(LFM_LIBS) AC_SUBST(LFM_DIR) fi @@ -106,6 +110,12 @@ if test ${HAVE_SNDFILE} ; then AC_SUBST(SNDFILE_DIR) fi +dnl lastfm plugin +if test ${HAVE_CURL}; then + VFS_CURL_DIR="plugins/vfs_curl" + AC_SUBST(VFS_CURL_DIR) +fi + AC_OUTPUT([ Makefile pixmaps/Makefile @@ -122,6 +132,7 @@ plugins/vorbis/Makefile plugins/flac/Makefile plugins/wavpack/Makefile plugins/sndfile/Makefile +plugins/vfs_curl/Makefile deadbeef.desktop ]) @@ -326,6 +326,7 @@ typedef struct DB_vfs_s { long (*tell) (DB_FILE *stream); void (*rewind) (DB_FILE *stream); const char **scheme_names; // NULL-terminated list of supported schemes, e.g. {"http", "ftp", NULL} + unsigned streaming : 1; } DB_vfs_t; #ifdef __cplusplus diff --git a/gtkplaylist.c b/gtkplaylist.c index 7307462a..2c97a3f5 100644 --- a/gtkplaylist.c +++ b/gtkplaylist.c @@ -1088,9 +1088,9 @@ gtkpl_add_fm_dropped_files (gtkplaylist_t *ps, char *ptr, int length, int drop_y //strncpy (fname, p, pe - p); //fname[pe - p] = 0; int abort = 0; - playItem_t *inserted = pl_insert_dir (after, fname + 7, &abort, gtkpl_add_file_info_cb, NULL); + playItem_t *inserted = pl_insert_dir (after, fname, &abort, gtkpl_add_file_info_cb, NULL); if (!inserted && !abort) { - inserted = pl_insert_file (after, fname + 7, &abort, gtkpl_add_file_info_cb, NULL); + inserted = pl_insert_file (after, fname, &abort, gtkpl_add_file_info_cb, NULL); } if (inserted) { after = inserted; @@ -372,11 +372,15 @@ pl_insert_file (playItem_t *after, const char *fname, int *pabort, int (*cb)(pla } } } + fprintf (stderr, "no decoder found for %s\n", fname); return NULL; } playItem_t * pl_insert_dir (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; + } struct stat buf; lstat (dirname, &buf); if (S_ISLNK(buf.st_mode)) { diff --git a/plugins/lastfm/Makefile.am b/plugins/lastfm/Makefile.am index c9e1e9b3..9aec3cf4 100644 --- a/plugins/lastfm/Makefile.am +++ b/plugins/lastfm/Makefile.am @@ -3,6 +3,6 @@ pkglib_LTLIBRARIES = lastfm.la lastfm_la_SOURCES = lastfm.c lastfm_la_LDFLAGS = -module -lastfm_la_LIBADD = $(LDADD) $(LFM_LIBS) -AM_CFLAGS = $(LASTFM_DEPS_CFLAGS) -std=c99 +lastfm_la_LIBADD = $(LDADD) $(CURL_LIBS) +AM_CFLAGS = -std=c99 diff --git a/plugins/vfs_curl/Makefile.am b/plugins/vfs_curl/Makefile.am new file mode 100644 index 00000000..189596a5 --- /dev/null +++ b/plugins/vfs_curl/Makefile.am @@ -0,0 +1,7 @@ +vfs_curldir = $(libdir)/$(PACKAGE) +pkglib_LTLIBRARIES = vfs_curl.la +vfs_curl_la_SOURCES = vfs_curl.c +vfs_curl_la_LDFLAGS = -module + +vfs_curl_la_LIBADD = $(LDADD) $(CURL_LIBS) +AM_CFLAGS = $(CFLAGS) -std=c99 diff --git a/plugins/vfs_curl/vfs_curl.c b/plugins/vfs_curl/vfs_curl.c new file mode 100644 index 00000000..8ff59d98 --- /dev/null +++ b/plugins/vfs_curl/vfs_curl.c @@ -0,0 +1,246 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009 Alexey Yakovenko + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <curl/curl.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include "../../deadbeef.h" + +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +static DB_functions_t *deadbeef; + +#define BUFFER_SIZE (0x10000/2) +#define BUFFER_MASK 0xffff + +#define STATUS_INITIAL 0 +#define STATUS_READING 1 +#define STATUS_FINISHED 2 +#define STATUS_ABORTED 3 +#define STATUS_REWIND 4 + +typedef struct { + DB_vfs_t *vfs; + // buffer must be 2x size of BUFFER_SIZE, to be able to seek back a bit + uint8_t buffer[BUFFER_SIZE*2]; + int pos; // position in stream; use "& BUFFER_MASK" to make it index into ringbuffer + int remaining; // remaining bytes in buffer read from stream + int status; + char *url; + intptr_t tid; // thread id which does http requests + intptr_t mutex; +} HTTP_FILE; + +static DB_vfs_t plugin; + +static char http_err[CURL_ERROR_SIZE]; + +static size_t +lastfm_curl_write (void *ptr, size_t size, size_t nmemb, void *stream) { + HTTP_FILE *fp = (HTTP_FILE *)stream; + int avail = size * nmemb; + for (;;) { + if (fp->status == STATUS_REWIND) { + return 0; + } + if (fp->status == STATUS_ABORTED) { + break; + } + deadbeef->mutex_lock (fp->mutex); + int sz = BUFFER_SIZE - fp->remaining; + int cp = min (avail, 10000); + if (sz >= cp) { + cp = min (avail, sz); + int writepos = (fp->pos + fp->remaining) & BUFFER_MASK; + // copy data + int part1 = BUFFER_SIZE * 2 - writepos; + part1 = min (part1, cp); + int part2 = sz - part1; + memcpy (fp->buffer+writepos, ptr, part1); + ptr += part1; + avail -= part1; + fp->remaining += part1; + if (part2 > 0) { + memcpy (fp->buffer, ptr, part2); + ptr += part2; + avail -= part2; + fp->remaining += part2; + } + } + deadbeef->mutex_unlock (fp->mutex); + if (avail >= size*nmemb) { + break; + } + usleep (3000); + } + + return nmemb * size - avail; +} + +static void +http_thread_func (uintptr_t ctx) { + HTTP_FILE *fp = (HTTP_FILE *)ctx; + CURL *curl; + curl = curl_easy_init (); + for (;;) { + fp->pos = 0; + fp->remaining = 0; + curl_easy_setopt (curl, CURLOPT_URL, fp->url); + curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, lastfm_curl_write); + curl_easy_setopt (curl, CURLOPT_WRITEDATA, ctx); + curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, http_err); + curl_easy_setopt (curl, CURLOPT_BUFFERSIZE, BUFFER_SIZE - fp->remaining); + curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + fp->status = STATUS_READING; + int status = curl_easy_perform (curl); + trace ("curl: curl_easy_perform status=%d\n", status); + if (fp->status != STATUS_REWIND) { + break; + } + } + curl_easy_cleanup (curl); + fp->status = STATUS_FINISHED; +} + +static DB_FILE * +http_open (const char *fname) { + HTTP_FILE *fp = malloc (sizeof (HTTP_FILE)); + memset (fp, 0, sizeof (HTTP_FILE)); + fp->vfs = &plugin; + fp->url = strdup (fname); + return (DB_FILE*)fp; +} + +static void +http_close (DB_FILE *stream) { + assert (stream); + HTTP_FILE *fp = (HTTP_FILE *)stream; + if (fp->tid) { + fp->status = STATUS_ABORTED; + deadbeef->thread_join (fp->tid); + deadbeef->mutex_free (fp->mutex); + } + free (fp->url); + free (stream); +} + +static size_t +http_read (void *ptr, size_t size, size_t nmemb, DB_FILE *stream) { + assert (stream); + assert (ptr); + assert (size * nmemb <= BUFFER_SIZE); + HTTP_FILE *fp = (HTTP_FILE *)stream; + if (!fp->tid) { + fp->mutex = deadbeef->mutex_create (); + fp->tid = deadbeef->thread_start (http_thread_func, (uintptr_t)fp); + } + // wait until data is available + while (fp->remaining < size * nmemb && fp->status != STATUS_FINISHED) { + sleep (3000); + } + deadbeef->mutex_lock (fp->mutex); + int sz = size * nmemb; + int readpos = fp->pos & BUFFER_MASK; + int part1 = BUFFER_SIZE*2-(fp->pos&BUFFER_MASK); + part1 = min (part1, sz); + part1 = min (part1, fp->remaining); + memcpy (ptr, fp->buffer+readpos, part1); + fp->remaining -= part1; + fp->pos += part1; + sz -= part1; + if (fp->remaining > 0) { + int part2 = min (fp->remaining, sz); + memcpy (((char *)ptr) + part1, fp->buffer, part2); + fp->remaining -= part2; + fp->pos += part2; + sz -= part2; + } + deadbeef->mutex_unlock (fp->mutex); + return size * nmemb - sz; +} + +static int +http_seek (DB_FILE *stream, long offset, int whence) { + assert (stream); + HTTP_FILE *fp = (HTTP_FILE *)stream; + if (!fp->tid) { + if (offset == 0 && (whence == SEEK_SET || whence == SEEK_CUR)) { + return 0; + } + else { + trace ("vfs_curl: cannot do seek(%d,%d)\n", offset, whence); + return -1; + } + } + // FIXME: can do some limited seeking + trace ("vfs_curl: seeking not implemented\n"); + return -1; +} + +static long +http_tell (DB_FILE *stream) { + assert (stream); + HTTP_FILE *fp = (HTTP_FILE *)stream; + return fp->pos; +} + +static void +http_rewind (DB_FILE *stream) { + assert (stream); + HTTP_FILE *fp = (HTTP_FILE *)stream; + if (fp->tid) { + deadbeef->mutex_lock (fp->mutex); + fp->status = STATUS_REWIND; + deadbeef->mutex_unlock (fp->mutex); + } +} + +static const char *scheme_names[] = { "http://", NULL }; + +// standard stdio vfs +static DB_vfs_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.type = DB_PLUGIN_VFS, + .plugin.name = "CURL VFS (streaming over http)", + .plugin.author = "Alexey Yakovenko", + .plugin.email = "waker@users.sourceforge.net", + .plugin.website = "http://deadbeef.sf.net", + .open = http_open, + .close = http_close, + .read = http_read, + .seek = http_seek, + .tell = http_tell, + .rewind = http_rewind, + .scheme_names = scheme_names, + .streaming = 1 +}; + +DB_plugin_t * +vfs_curl_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} @@ -17,11 +17,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <string.h> +#include <stdio.h> #include "vfs.h" #include "plugins.h" +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) + DB_FILE * vfs_fopen (const char *fname) { + trace ("vfs_open %s\n", fname); DB_vfs_t **plugs = plug_get_vfs_list (); DB_vfs_t *fallback = NULL; int i; diff --git a/vfs_stdio.c b/vfs_stdio.c index bc0f1183..9d63c141 100644 --- a/vfs_stdio.c +++ b/vfs_stdio.c @@ -20,6 +20,7 @@ #include <stdio.h> #include <stdlib.h> #include <assert.h> +#include <string.h> static DB_functions_t *deadbeef; typedef struct { @@ -29,8 +30,11 @@ typedef struct { static DB_vfs_t plugin; -DB_FILE * +static DB_FILE * stdio_open (const char *fname) { + if (!memcmp (fname, "file://", 7)) { + fname += 7; + } FILE *file = fopen (fname, "rb"); if (!file) { return NULL; @@ -41,32 +45,33 @@ stdio_open (const char *fname) { return (DB_FILE*)fp; } -void +static void stdio_close (DB_FILE *stream) { assert (stream); fclose (((STDIO_FILE *)stream)->stream); free (stream); } -size_t stdio_read (void *ptr, size_t size, size_t nmemb, DB_FILE *stream) { +static size_t +stdio_read (void *ptr, size_t size, size_t nmemb, DB_FILE *stream) { assert (stream); assert (ptr); return fread (ptr, size, nmemb, ((STDIO_FILE *)stream)->stream); } -int +static int stdio_seek (DB_FILE *stream, long offset, int whence) { assert (stream); return fseek (((STDIO_FILE *)stream)->stream, offset, whence); } -long +static long stdio_tell (DB_FILE *stream) { assert (stream); return ftell (((STDIO_FILE *)stream)->stream); } -void +static void stdio_rewind (DB_FILE *stream) { assert (stream); rewind (((STDIO_FILE *)stream)->stream); |