From 1205684e301f998f26a1dc8828e1f628ef0160f6 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Sun, 25 Oct 2009 21:16:35 +0100 Subject: added vfs filetype detection based on content-type --- deadbeef.h | 2 + playlist.c | 191 +++++++++++++++++--------------------------- plugins.c | 1 + plugins/vfs_curl/vfs_curl.c | 56 +++++++++++-- streamer.c | 92 ++++++++------------- vfs.c | 5 ++ vfs.h | 1 + vfs_stdio.c | 7 ++ 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); } + -- cgit v1.2.3