# HG changeset patch # User iabervon # Date 1169358049 28800 # Node ID ffa6858550037f3be24ed8ae9ed6394745fd7e8a # Parent 65408c59b42480f9f9d45a84a7822ec0f11e3990 [svn] Provide "name" metadata when the url is a shoutcast stream. We read the stream headers ourselves, because curl doesn't deal with ICY headers. diff -r 65408c59b424 -r ffa685855003 ChangeLog --- a/ChangeLog Fri Jan 19 11:05:40 2007 -0800 +++ b/ChangeLog Sat Jan 20 21:40:49 2007 -0800 @@ -1,3 +1,12 @@ +2007-01-19 19:05:40 +0000 Giacomo Lozito + revision [1012] + - sid plugin: removed plugin option to check files by content, that kind of option is handled by the player + trunk/src/sid/xs_config.c | 5 ----- + trunk/src/sid/xs_config.h | 1 - + trunk/src/sid/xs_interface.c | 30 ------------------------------ + 3 files changed, 36 deletions(-) + + 2007-01-19 18:55:44 +0000 Giacomo Lozito revision [1010] - added is_our_file_from_vfs function to sid, for both libsidplay v1 and v2 diff -r 65408c59b424 -r ffa685855003 src/curl/curl.c --- a/src/curl/curl.c Fri Jan 19 11:05:40 2007 -0800 +++ b/src/curl/curl.c Sat Jan 20 21:40:49 2007 -0800 @@ -30,6 +30,7 @@ #define DEBUG_OPEN_CLOSE 0 #define DEBUG_SEEK 0 #define DEBUG_READ 0 +#define DEBUG_HEADERS 0 typedef struct _CurlHandle CurlHandle; @@ -46,10 +47,15 @@ gsize rd_index; gsize wr_index; + gsize hdr_index; + + gboolean header; // true if we haven't finished the header yet gboolean no_data; // true if we're only looking for length currently gboolean cancel; // true if the thread should be cancelled gboolean failed; // true if we've tried and failed already GThread *thread; // the thread that's reading from the connection + + gchar *name; }; /* TODO: @@ -77,8 +83,12 @@ static size_t buf_available(CurlHandle *handle) { - size_t buffer_limit = handle->wr_abs - handle->rd_abs; - size_t cont_limit = handle->buffer_length - handle->rd_index; + size_t buffer_limit; + size_t cont_limit; + if (handle->header) + return 0; + buffer_limit = handle->wr_abs - handle->rd_abs; + cont_limit = handle->buffer_length - handle->rd_index; if (buffer_limit <= 0) return 0; return buffer_limit < cont_limit ? buffer_limit : cont_limit; @@ -118,6 +128,44 @@ #define PROBE 262140 +#define ICY_NAME "icy-name:" +#define ICY_METAINT "icy-metaint:" + +static gboolean match_header(CurlHandle *handle, size_t size, + const char *header) +{ + if (strlen(header) > size) + return FALSE; + // XXXX wrapped headers + return !(strncmp(handle->buffer + handle->hdr_index, + header, strlen(header))); +} + +static gchar *get_value(CurlHandle *handle, size_t size, const char *header) +{ + // XXXX wrapped headers + return strdup(handle->buffer + + (handle->hdr_index + strlen(header)) % handle->buffer_length); +} + +static void got_header(CurlHandle *handle, size_t size) +{ + if (DEBUG_HEADERS) + g_print("Got header %d bytes\n", size); + if (match_header(handle, size, ICY_NAME)) + { + handle->name = get_value(handle, size, ICY_NAME); + if (DEBUG_HEADERS) + { + g_print("Stream name: %s\n", handle->name); + } + } + if (match_header(handle, size, ICY_METAINT)) + { + + } +} + static size_t curl_writecb(void *ptr, size_t size, size_t nmemb, void *stream) { CurlHandle *handle = stream; @@ -125,7 +173,8 @@ gint ret = 0; gint trans; - update_length(handle); + if (!handle->header) + update_length(handle); while (ret < sz) { @@ -140,9 +189,44 @@ trans = sz - ret; memcpy(handle->buffer + handle->wr_index, ptr + ret, trans); - handle->wr_abs += trans; + if (!handle->header) + handle->wr_abs += trans; handle->wr_index = (handle->wr_index + trans) % handle->buffer_length; ret += trans; + + if (handle->header) + { + gsize i = handle->hdr_index; + while (1) + { + if ((i + 1) % handle->buffer_length == handle->wr_index) + break; + if (handle->buffer[i] == '\r' && + handle->buffer[(i + 1) % handle->buffer_length] == '\n') + { + gsize size = (handle->buffer_length + i - + handle->hdr_index) % handle->buffer_length; + handle->buffer[i] = '\0'; + got_header(handle, size); + if (i == handle->hdr_index) + { + // Empty header means the end of the headers + handle->header = 0; + handle->hdr_index = (i + 2) % handle->buffer_length; + // We read from the start of the data in the request + handle->rd_index = handle->hdr_index; + // We've already written the amount that's after + // the header. + handle->wr_abs += + (handle->wr_index - handle->hdr_index + handle->buffer_length) % handle->buffer_length; + break; + } + handle->hdr_index = (i + 2) % handle->buffer_length; + } + i = (i + 1) % handle->buffer_length; + } + } + } return ret; } @@ -167,13 +251,17 @@ curl_easy_setopt(handle->curl, CURLOPT_HTTPGET, 1); } + handle->header = 1; + handle->hdr_index = 0; + result = curl_easy_perform(handle->curl); if (result == CURLE_OK) update_length(handle); // We expect to get CURLE_WRITE_ERROR if we cancel. // We get CURLE_GOT_NOTHING if we send a HEAD request to a shoutcast server. + // We get CURLE_HTTP_RANGE_ERROR if we try to use range with shoutcast. if (result != CURLE_OK && result != CURLE_WRITE_ERROR && - result != CURLE_GOT_NOTHING) + result != CURLE_GOT_NOTHING && CURLE_HTTP_RANGE_ERROR) { g_print("Got curl error %d\n", result); handle->failed = 1; @@ -248,11 +336,17 @@ handle->cancel = 0; handle->failed = 0; handle->no_data = 0; + curl_easy_setopt(handle->curl, CURLOPT_URL, url); curl_easy_setopt(handle->curl, CURLOPT_WRITEFUNCTION, curl_writecb); curl_easy_setopt(handle->curl, CURLOPT_WRITEDATA, handle); + curl_easy_setopt(handle->curl, CURLOPT_HEADERDATA, handle); + curl_easy_setopt(handle->curl, CURLOPT_CONNECTTIMEOUT, 10); + //add header icy-metadata:1 (when we're ready for it) + //curl_easy_setopt(handle->curl, CURLOPT_HTTPHEADER, "icy-metadata:1"); + file->handle = handle; if (DEBUG_OPEN_CLOSE) @@ -279,6 +373,8 @@ g_print("Okay\n"); g_free(handle->buffer); + if (handle->name) + g_free(handle->name); curl_easy_cleanup(handle->curl); g_free(handle); } @@ -302,7 +398,8 @@ curl_req_xfer(handle); - check(handle); + if (DEBUG_SEEK) + check(handle); while (ret < sz) { @@ -384,7 +481,8 @@ if (whence == SEEK_END && handle->length < 0) { - //g_print("Tried to seek to the end of a file with unknown length\n"); + if (DEBUG_SEEK) + g_print("Tried to seek to the end of a file with unknown length\n"); // don't know how long it is... return -1; } @@ -452,6 +550,15 @@ return -1; } +gchar * +curl_vfs_metadata_impl(VFSFile * file, const gchar * field) +{ + CurlHandle *handle = file->handle; + if (!strcmp(field, "name")) + return strdup(handle->name); + return NULL; +} + VFSConstructor curl_const = { "http://", curl_vfs_fopen_impl, @@ -464,7 +571,8 @@ curl_vfs_rewind_impl, curl_vfs_ftell_impl, curl_vfs_feof_impl, - curl_vfs_truncate_impl + curl_vfs_truncate_impl, + curl_vfs_metadata_impl }; static void init(void)