summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deadbeef.h2
-rw-r--r--playlist.c191
-rw-r--r--plugins.c1
-rw-r--r--plugins/vfs_curl/vfs_curl.c56
-rw-r--r--streamer.c92
-rw-r--r--vfs.c5
-rw-r--r--vfs.h1
-rw-r--r--vfs_stdio.c7
8 files changed, 173 insertions, 182 deletions
diff --git a/deadbeef.h b/deadbeef.h
index e10029d5..3b8c1552 100644
--- a/deadbeef.h
+++ b/deadbeef.h
@@ -235,6 +235,7 @@ typedef struct {
int64_t (*ftell) (DB_FILE *stream);
void (*rewind) (DB_FILE *stream);
int64_t (*fgetlength) (DB_FILE *stream);
+ const char *(*fget_content_type) (DB_FILE *stream);
// message passing
int (*sendmessage) (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2);
// configuration access
@@ -388,6 +389,7 @@ typedef struct DB_vfs_s {
int64_t (*tell) (DB_FILE *stream);
void (*rewind) (DB_FILE *stream);
int64_t (*getlength)(DB_FILE *stream);
+ const char * (*get_content_type) (DB_FILE *stream);
const char **scheme_names; // NULL-terminated list of supported schemes, e.g. {"http", "ftp", NULL}
unsigned streaming : 1;
} DB_vfs_t;
diff --git a/playlist.c b/playlist.c
index bb21a10b..e437adc2 100644
--- a/playlist.c
+++ b/playlist.c
@@ -354,8 +354,35 @@ pl_insert_file (playItem_t *after, const char *fname, int *pabort, int (*cb)(pla
if (!fname) {
return NULL;
}
+
+ // add all posible streams as special-case:
+ // set decoder to NULL, and filetype to "content"
+ // streamer is responsible to determine content type on 1st access and
+ // update decoder and filetype fields
+ if (strcasecmp (fname, "file://")) {
+ const char *p = fname;
+ int detect_on_access = 1;
+ for (p = fname; *p; p++) {
+ if (!strncmp (p, "://", 3)) {
+ break;
+ }
+ if (!isalpha (*p)) {
+ detect_on_access = 0;
+ break;
+ }
+ }
+ if (detect_on_access && *fname != ':') {
+ playItem_t *it = pl_item_alloc ();
+ it->decoder = NULL;
+ it->fname = strdup (fname);
+ it->filetype = "content";
+ pl_set_item_duration (it, -1);
+ pl_add_meta (it, "title", NULL);
+ return pl_insert_item (after, it);
+ }
+ }
+
// detect decoder
-// DB_decoder_t *decoder = NULL;
const char *eol = fname + strlen (fname) - 1;
while (eol > fname && *eol != '.') {
eol--;
@@ -367,18 +394,16 @@ pl_insert_file (playItem_t *after, const char *fname, int *pabort, int (*cb)(pla
for (int i = 0; decoders[i]; i++) {
if (decoders[i]->exts && decoders[i]->insert) {
const char **exts = decoders[i]->exts;
- if (exts) {
- for (int e = 0; exts[e]; e++) {
- if (!strcasecmp (exts[e], eol)) {
- playItem_t *inserted = NULL;
- if ((inserted = (playItem_t *)decoders[i]->insert (DB_PLAYITEM (after), fname)) != NULL) {
- if (cb) {
- if (cb (inserted, user_data) < 0) {
- *pabort = 1;
- }
+ for (int e = 0; exts[e]; e++) {
+ if (!strcasecmp (exts[e], eol)) {
+ playItem_t *inserted = NULL;
+ if ((inserted = (playItem_t *)decoders[i]->insert (DB_PLAYITEM (after), fname)) != NULL) {
+ if (cb) {
+ if (cb (inserted, user_data) < 0) {
+ *pabort = 1;
}
- return inserted;
}
+ return inserted;
}
}
}
@@ -454,46 +479,6 @@ pl_add_dir (const char *dirname, int (*cb)(playItem_t *it, void *data), void *us
return 0;
}
return -1;
-// {{{ original pl_add_dir code
-#if 0
- struct stat buf;
- lstat (dirname, &buf);
- if (S_ISLNK(buf.st_mode)) {
- return -1;
- }
- struct dirent **namelist = NULL;
- int n;
-
- n = scandir (dirname, &namelist, NULL, alphasort);
- if (n < 0)
- {
- if (namelist)
- free (namelist);
- return -1; // not a dir or no read access
- }
- else
- {
- int i;
- for (i = 0; i < n; i++)
- {
- // no hidden files
- if (namelist[i]->d_name[0] != '.')
- {
- char fullname[1024];
- strcpy (fullname, dirname);
- strncat (fullname, "/", 1024);
- strncat (fullname, namelist[i]->d_name, 1024);
- if (pl_add_dir (fullname)) {
- pl_add_file (fullname);
- }
- }
- free (namelist[i]);
- }
- free (namelist);
- }
- return 0;
-#endif
-// }}}
}
int
@@ -905,8 +890,6 @@ pl_add_meta (playItem_t *it, const char *key, const char *value) {
m = malloc (sizeof (metaInfo_t));
m->key = key;
m->value = strdup (value);
-// strncpy (m->value, value, META_FIELD_SIZE-1);
-// m->value[META_FIELD_SIZE-1] = 0;
m->next = it->meta;
it->meta = m;
}
@@ -922,50 +905,6 @@ pl_format_item_display_name (playItem_t *it, char *str, int len) {
title = "Unknown title";
}
snprintf (str, len, "%s - %s", artist, title);
-#if 0
- // artist - title
- const char *track = pl_find_meta (it, "track");
- const char *artist = pl_find_meta (it, "artist");
- const char *album = pl_find_meta (it, "album");
- const char *title = pl_find_meta (it, "title");
- if (*track == '?' && *album == '?' && *artist != '?' && *title != '?') {
- snprintf (str, len, "%s - %s", artist, title);
- }
- else if (*artist != '?' && *track != '?' && *title != '?') {
- snprintf (str, len, "%s. %s - %s", track, artist, title);
- }
- else if (*artist == '?' && *track != '?' && *album != '?') {
- snprintf (str, len, "%s. %s", track, album);
- }
- else if (*artist != '?' && *track != '?' && *album != '?') {
- snprintf (str, len, "%s. %s - %s", track, artist, album);
- }
- else if (*artist != '?' && *title != '?') {
- snprintf (str, len, "%s - %s", artist, title);
- }
- else if (*artist != '?') {
- snprintf (str, len, "%s", artist);
- }
- else if (*title != '?') {
- snprintf (str, len, "%s", title);
- }
- else {
- // cut filename without path and extension
- char *pext = it->fname + strlen (it->fname) - 1;
- while (pext >= it->fname && *pext != '.') {
- pext--;
- }
- char *pname = pext;
- while (pname >= it->fname && *pname != '/') {
- pname--;
- }
- if (*pname == '/') {
- pname++;
- }
- strncpy (str, pname, pext-pname);
- str[pext-pname] = 0;
- }
-#endif
}
const char *
@@ -1050,12 +989,20 @@ pl_save (const char *fname) {
if (fwrite (it->fname, 1, l, fp) != l) {
goto save_fail;
}
- ll = strlen (it->decoder->id);
- if (fwrite (&ll, 1, 1, fp) != 1) {
- goto save_fail;
+ if (it->decoder) {
+ ll = strlen (it->decoder->id);
+ if (fwrite (&ll, 1, 1, fp) != 1) {
+ goto save_fail;
+ }
+ if (fwrite (it->decoder->id, 1, ll, fp) != ll) {
+ goto save_fail;
+ }
}
- if (fwrite (it->decoder->id, 1, ll, fp) != ll) {
- goto save_fail;
+ else {
+ ll = 0;
+ if (fwrite (&ll, 1, 1, fp) != 1) {
+ goto save_fail;
+ }
}
l = it->tracknum;
if (fwrite (&l, 1, 2, fp) != 2) {
@@ -1187,18 +1134,23 @@ pl_load (const char *fname) {
if (ll >= 20) {
goto load_fail;
}
- char decoder[20];
- if (fread (decoder, 1, ll, fp) != ll) {
- goto load_fail;
- }
- decoder[ll] = 0;
- for (int c = 0; decoders[c]; c++) {
- if (!strcmp (decoder, decoders[c]->id)) {
- it->decoder = decoders[c];
+ if (ll) {
+ char decoder[20];
+ if (fread (decoder, 1, ll, fp) != ll) {
+ goto load_fail;
}
+ decoder[ll] = 0;
+ for (int c = 0; decoders[c]; c++) {
+ if (!strcmp (decoder, decoders[c]->id)) {
+ it->decoder = decoders[c];
+ }
+ }
+// if (!it->decoder) {
+// goto load_fail;
+// }
}
- if (!it->decoder) {
- goto load_fail;
+ else {
+ it->decoder = NULL;
}
// tracknum
if (fread (&l, 1, 2, fp) != 2) {
@@ -1230,11 +1182,16 @@ pl_load (const char *fname) {
goto load_fail;
}
ftype[ft] = 0;
- if (it->decoder && it->decoder->filetypes) {
- for (int i = 0; it->decoder->filetypes[i]; i++) {
- if (!strcasecmp (it->decoder->filetypes[i], ftype)) {
- it->filetype = it->decoder->filetypes[i];
- break;
+ if (!strcmp (ftype, "content")) {
+ it->filetype = "content";
+ }
+ else {
+ if (it->decoder && it->decoder->filetypes) {
+ for (int i = 0; it->decoder->filetypes[i]; i++) {
+ if (!strcasecmp (it->decoder->filetypes[i], ftype)) {
+ it->filetype = it->decoder->filetypes[i];
+ break;
+ }
}
}
}
diff --git a/plugins.c b/plugins.c
index 9e08c1d8..f2ad578e 100644
--- a/plugins.c
+++ b/plugins.c
@@ -110,6 +110,7 @@ static DB_functions_t deadbeef_api = {
.ftell = vfs_ftell,
.rewind = vfs_rewind,
.fgetlength = vfs_fgetlength,
+ .fget_content_type = vfs_get_content_type,
// message passing
.sendmessage = messagepump_push,
// configuration access
diff --git a/plugins/vfs_curl/vfs_curl.c b/plugins/vfs_curl/vfs_curl.c
index f257600b..0a449824 100644
--- a/plugins/vfs_curl/vfs_curl.c
+++ b/plugins/vfs_curl/vfs_curl.c
@@ -52,6 +52,8 @@ typedef struct {
int32_t skipbytes;
intptr_t tid; // thread id which does http requests
intptr_t mutex;
+ int gotheader;
+ char *content_type;
uint8_t status;
} HTTP_FILE;
@@ -59,7 +61,6 @@ static DB_vfs_t plugin;
static char http_err[CURL_ERROR_SIZE];
-
static size_t
http_curl_write (void *ptr, size_t size, size_t nmemb, void *stream) {
// trace ("http_curl_write %d bytes\n", size * nmemb);
@@ -109,7 +110,7 @@ http_curl_write (void *ptr, size_t size, size_t nmemb, void *stream) {
}
static size_t
-http_size_request_handler (void *ptr, size_t size, size_t nmemb, void *stream) {
+http_size_header_handler (void *ptr, size_t size, size_t nmemb, void *stream) {
assert (stream);
HTTP_FILE *fp = (HTTP_FILE *)stream;
// don't copy/allocate mem, just grep for "Content-Length: "
@@ -122,6 +123,28 @@ http_size_request_handler (void *ptr, size_t size, size_t nmemb, void *stream) {
return size * nmemb;
}
+static size_t
+http_content_header_handler (void *ptr, size_t size, size_t nmemb, void *stream) {
+ assert (stream);
+ HTTP_FILE *fp = (HTTP_FILE *)stream;
+ const char c_type_str[] ="Content-Type: ";
+ const char *cl = strstr (ptr, c_type_str);
+ if (cl) {
+ cl += sizeof (c_type_str)-1;
+ const uint8_t *p = (const uint8_t *)cl;
+ while (*p >= 0x20) {
+ p++;
+ }
+ int len = (const char *)p-cl;
+ char str[len+1];
+ strncpy (str, cl, len);
+ str[len] = 0;
+ fp->content_type = strdup (str);
+ }
+ fp->gotheader = 1;
+ return size * nmemb;
+}
+
static void
http_thread_func (uintptr_t ctx) {
HTTP_FILE *fp = (HTTP_FILE *)ctx;
@@ -136,15 +159,12 @@ http_thread_func (uintptr_t ctx) {
curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10);
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, http_size_request_handler);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, http_size_header_handler);
curl_easy_setopt (curl, CURLOPT_HEADERDATA, ctx);
status = curl_easy_perform(curl);
if (status != 0) {
fp->length = -1;
trace ("vfs_curl: curl_easy_perform failed while getting filesize, status %d\n", status);
-// fp->status = STATUS_FINISHED;
-// fp->tid = 0;
-// return;
}
fp->status = STATUS_STARTING;
@@ -157,6 +177,8 @@ http_thread_func (uintptr_t ctx) {
curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, http_err);
curl_easy_setopt (curl, CURLOPT_BUFFERSIZE, BUFFER_SIZE/2);
curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+ curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, http_content_header_handler);
+ curl_easy_setopt (curl, CURLOPT_HEADERDATA, ctx);
// enable up to 10 redirects
curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 10);
@@ -216,7 +238,12 @@ http_close (DB_FILE *stream) {
deadbeef->thread_join (fp->tid);
deadbeef->mutex_free (fp->mutex);
}
- free (fp->url);
+ if (fp->content_type) {
+ free (fp->content_type);
+ }
+ if (fp->url) {
+ free (fp->url);
+ }
free (stream);
}
@@ -372,6 +399,20 @@ http_getlength (DB_FILE *stream) {
return fp->length;
}
+static const char *
+http_get_content_type (DB_FILE *stream) {
+ trace ("http_get_content_type\n");
+ assert (stream);
+ HTTP_FILE *fp = (HTTP_FILE *)stream;
+ if (!fp->tid) {
+ http_start_streamer (fp);
+ }
+ while (fp->status != STATUS_FINISHED && fp->status != STATUS_ABORTED && !fp->gotheader) {
+ usleep (3000);
+ }
+ return fp->content_type;
+}
+
static const char *scheme_names[] = { "http://", "ftp://", NULL };
// standard stdio vfs
@@ -392,6 +433,7 @@ static DB_vfs_t plugin = {
.tell = http_tell,
.rewind = http_rewind,
.getlength = http_getlength,
+ .get_content_type = http_get_content_type,
.scheme_names = scheme_names,
.streaming = 1
};
diff --git a/streamer.c b/streamer.c
index 74b4104a..70abb550 100644
--- a/streamer.c
+++ b/streamer.c
@@ -34,6 +34,7 @@
#include "plugins.h"
#include "optmath.h"
#include "volume.h"
+#include "vfs.h"
//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
#define trace(fmt,...)
@@ -118,6 +119,39 @@ streamer_set_current (playItem_t *it) {
if (!it) {
return 0;
}
+ if (!it->decoder && it->filetype && !strcmp (it->filetype, "content")) {
+ // try to get content-type
+ DB_FILE *fp = vfs_fopen (it->fname);
+ const char *ext = NULL;
+ if (fp) {
+ const char *ct = vfs_get_content_type (fp);
+ if (ct) {
+ fprintf (stderr, "got content-type: %s\n", ct);
+ if (!strcmp (ct, "audio/mpeg")) {
+ ext = "mp3";
+ }
+ else if (!strcmp (ct, "application/ogg")) {
+ ext = "ogg";
+ }
+ }
+ vfs_fclose (fp);
+ }
+ if (ext) {
+ DB_decoder_t **decoders = plug_get_decoder_list ();
+ // match by decoder
+ for (int i = 0; decoders[i]; i++) {
+ if (decoders[i]->exts) {
+ const char **exts = decoders[i]->exts;
+ for (int e = 0; exts[e]; e++) {
+ if (!strcasecmp (exts[e], ext)) {
+ it->decoder = decoders[i];
+ it->filetype = decoders[i]->filetypes[0];
+ }
+ }
+ }
+ }
+ }
+ }
if (it->decoder) {
int ret = it->decoder->init (DB_PLAYITEM (it));
// trace ("input samplerate: %d\n", it->decoder->info.samplerate);
@@ -142,44 +176,6 @@ streamer_set_current (playItem_t *it) {
return 0;
}
-#if 0
-static int
-str_set_current (playItem_t *it) {
- int ret = 0;
- int from = pl_get_idx_of (playlist_current_ptr);
- int to = it ? pl_get_idx_of (it) : -1;
- if (str_playing_song.decoder) {
- plug_trigger_event (DB_EV_SONGFINISHED);
- }
- codec_lock ();
- if (str_playing_song.decoder) {
- str_playing_song.decoder->free ();
- }
- pl_item_free (&str_playing_song);
- playlist_current_ptr = it;
- if (it && it->decoder) {
- // don't do anything on fail, streamer will take care
- ret = it->decoder->init (DB_PLAYITEM (it));
-// if (ret < 0) {
-// }
- }
- if (playlist_current_ptr) {
- streamer_reset (0);
- }
- if (it) {
- it->played = 1;
- it->started_timestamp = time (NULL);
- pl_item_copy (&str_playing_song, it);
- }
- codec_unlock ();
- if (it) {
- plug_trigger_event (DB_EV_SONGSTARTED);
- }
- messagepump_push (M_SONGCHANGED, 0, from, to);
- return ret;
-}
-#endif
-
float
streamer_get_playpos (void) {
return playpos;
@@ -374,26 +370,6 @@ streamer_thread (uintptr_t ctx) {
streamer_lock ();
memcpy (streambuffer+streambuffer_fill, buf, sz);
streambuffer_fill += bytesread;
-
-
-#if 0
- int writepos = (streambuffer_pos + streambuffer_fill) & STREAM_BUFFER_MASK;
- int part1 = STREAM_BUFFER_SIZE-writepos;
- part1 = min (part1, sz);
- if (part1 > 0) {
- streamer_unlock ();
- int bytesread = streamer_read_async (streambuffer + writepos, part1);
- streamer_lock ();
- streambuffer_fill += bytesread;
- }
- sz -= part1;
- if (sz > 0) {
- streamer_unlock ();
- int bytesread = streamer_read_async (streambuffer, sz);
- streamer_lock ();
- streambuffer_fill += bytesread;
- }
-#endif
}
streamer_unlock ();
struct timeval tm2;
diff --git a/vfs.c b/vfs.c
index 53266b5c..7e6f64ee 100644
--- a/vfs.c
+++ b/vfs.c
@@ -79,3 +79,8 @@ int64_t
vfs_fgetlength (DB_FILE *stream) {
return stream->vfs->getlength (stream);
}
+
+const char *
+vfs_get_content_type (DB_FILE *stream) {
+ return stream->vfs->get_content_type (stream);
+}
diff --git a/vfs.h b/vfs.h
index 3090ba1d..6bb7afd7 100644
--- a/vfs.h
+++ b/vfs.h
@@ -29,5 +29,6 @@ int vfs_fseek (DB_FILE *stream, int64_t offset, int whence);
int64_t vfs_ftell (DB_FILE *stream);
void vfs_rewind (DB_FILE *stream);
int64_t vfs_fgetlength (DB_FILE *stream);
+const char *vfs_get_content_type (DB_FILE *stream);
#endif // __VFS_H
diff --git a/vfs_stdio.c b/vfs_stdio.c
index 62fb1bee..d98002bd 100644
--- a/vfs_stdio.c
+++ b/vfs_stdio.c
@@ -88,6 +88,11 @@ stdio_getlength (DB_FILE *stream) {
return sz;
}
+const char *
+stdio_get_content_type (DB_FILE *stream) {
+ return NULL;
+}
+
// standard stdio vfs
static DB_vfs_t plugin = {
DB_PLUGIN_SET_API_VERSION
@@ -106,6 +111,7 @@ static DB_vfs_t plugin = {
.tell = stdio_tell,
.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
};
@@ -114,3 +120,4 @@ stdio_load (DB_functions_t *api) {
deadbeef = api;
return DB_PLUGIN (&plugin);
}
+