# HG changeset patch # User iabervon # Date 1169364672 28800 # Node ID 20657d994de8ce80b8aeddb39c87bf2349416964 # Parent ffa6858550037f3be24ed8ae9ed6394745fd7e8a [svn] Handle inline metadata. Parsing of tags needs work before it will not crash on occasion, so it's disabled for now. diff -r ffa685855003 -r 20657d994de8 ChangeLog --- a/ChangeLog Sat Jan 20 21:40:49 2007 -0800 +++ b/ChangeLog Sat Jan 20 23:31:12 2007 -0800 @@ -1,3 +1,14 @@ +2007-01-21 05:40:49 +0000 Daniel Barkalow + revision [1016] + Provide "name" metadata when the url is a shoutcast stream. + + We read the stream headers ourselves, because curl doesn't deal with + ICY headers. + + trunk/src/curl/curl.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 116 insertions(+), 8 deletions(-) + + 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 diff -r ffa685855003 -r 20657d994de8 src/curl/curl.c --- a/src/curl/curl.c Sat Jan 20 21:40:49 2007 -0800 +++ b/src/curl/curl.c Sat Jan 20 23:31:12 2007 -0800 @@ -31,6 +31,10 @@ #define DEBUG_SEEK 0 #define DEBUG_READ 0 #define DEBUG_HEADERS 0 +#define DEBUG_ICY 0 +#define DEBUG_ICY_VERBOSE 0 +#define XXXX_FIXED_METADATA 0 +#define DEBUG_METADATA_REPORT 0 typedef struct _CurlHandle CurlHandle; @@ -41,6 +45,11 @@ gsize rd_abs; // the absolute position for reading from the stream gsize wr_abs; // the absolute position where the input connection is + gsize icy_left; + gsize icy_interval; + gint in_icy_meta; // 0=no, 1=before size, 2=in data + gsize meta_abs; // the absolute position where the metadata changes + gsize buffer_length; gchar *buffer; @@ -56,6 +65,7 @@ GThread *thread; // the thread that's reading from the connection gchar *name; + gchar *title; }; /* TODO: @@ -73,12 +83,21 @@ static size_t buf_space(CurlHandle *handle) { size_t rd_edge = handle->rd_abs - REVERSE_SEEK_SIZE; + size_t buffer_limit; + size_t cont_limit; if (rd_edge < 0) rd_edge = 0; - size_t buffer_limit = handle->buffer_length - + buffer_limit = handle->buffer_length - (handle->wr_abs - rd_edge); - size_t cont_limit = handle->buffer_length - handle->wr_index; - return buffer_limit < cont_limit ? buffer_limit : cont_limit; + cont_limit = handle->buffer_length - handle->wr_index; + if (cont_limit < buffer_limit) + buffer_limit = cont_limit; + if (handle->icy_interval) + { + if (handle->icy_left < buffer_limit) + buffer_limit = handle->icy_left; + } + return buffer_limit; } static size_t buf_available(CurlHandle *handle) @@ -162,10 +181,53 @@ } if (match_header(handle, size, ICY_METAINT)) { - + gchar *value = get_value(handle, size, ICY_METAINT); + handle->icy_interval = atoi(value); + free(value); + if (DEBUG_HEADERS) + g_print("Metadata interval: %d\n", handle->icy_interval); } } +#define TITLE_INLINE "StreamTitle=" + +static gboolean match_inline(CurlHandle *handle, size_t posn, + const char *name) +{ + // XXXX Wrapped + return (!strncmp(handle->buffer + posn, name, strlen(name))); +} + +static gchar *get_inline_value(CurlHandle *handle, size_t posn, + const char *name) +{ + // XXXX Wrapped + size_t end; + size_t sz; + gchar *ret; + posn += strlen(name); + end = posn + 1; + while (handle->buffer[end] != ';') + end++; + sz = end - posn - 1; + ret = g_malloc(sz); + memcpy(ret, handle->buffer + posn + 1, sz); + ret[sz - 1] = '\0'; + return ret; +} + +static void got_inline_metadata(CurlHandle *handle) +{ + size_t i = (handle->hdr_index + 1) % handle->buffer_length; + if (match_inline(handle, i, TITLE_INLINE)) + { + handle->title = get_inline_value(handle, i, TITLE_INLINE); + if (DEBUG_ICY) + g_print("Title: '%s'\n", handle->title); + } + handle->meta_abs = handle->wr_abs; +} + static size_t curl_writecb(void *ptr, size_t size, size_t nmemb, void *stream) { CurlHandle *handle = stream; @@ -190,7 +252,76 @@ memcpy(handle->buffer + handle->wr_index, ptr + ret, trans); if (!handle->header) - handle->wr_abs += trans; + { + if (handle->icy_interval) + handle->icy_left -= trans; + if (!handle->in_icy_meta) + { + handle->wr_abs += trans; + // write download here + if (handle->icy_interval && !handle->icy_left) + { + if (DEBUG_ICY) + g_print("Metadata inline after %d\n", handle->wr_abs); + handle->in_icy_meta = 1; + handle->icy_left = 1; + } + } + else if (handle->in_icy_meta == 1) + { + // Track where the header insert starts + handle->hdr_index = handle->wr_index; + handle->icy_left = + ((unsigned char)(handle->buffer[handle->wr_index])) * 16; + if (DEBUG_ICY) + g_print("Metadata of size %d\n", handle->icy_left); + if (handle->icy_left) + { + handle->in_icy_meta = 2; + } + else + { + handle->in_icy_meta = 0; + handle->icy_left = handle->icy_interval; + handle->wr_index--; + } + } + else + { + if (!handle->icy_left) + { + handle->wr_index = (handle->wr_index + trans) % + handle->buffer_length; + if (DEBUG_ICY_VERBOSE) + { + if (handle->wr_index < handle->hdr_index) + { + // wrapped + fwrite(handle->buffer + handle->hdr_index + 1, + handle->buffer_length - handle->hdr_index - 1, + 1, stdout); + fwrite(handle->buffer, handle->wr_index, 1, stdout); + } + else + { + fwrite(handle->buffer + handle->hdr_index, + handle->wr_index - handle->hdr_index, 1, + stdout); + } + g_print("\n"); + } + if (XXXX_FIXED_METADATA) + got_inline_metadata(handle); + + // Rewind the buffer usage to write over the + // metadata with content. -trans because we're about + // to add it. + handle->wr_index = handle->hdr_index - trans; + handle->in_icy_meta = 0; + handle->icy_left = handle->icy_interval; + } + } + } handle->wr_index = (handle->wr_index + trans) % handle->buffer_length; ret += trans; @@ -219,6 +350,17 @@ // the header. handle->wr_abs += (handle->wr_index - handle->hdr_index + handle->buffer_length) % handle->buffer_length; + // write download here... + //handle->icy_interval = 0; + handle->icy_left = handle->icy_interval; + if (handle->icy_interval) + { + handle->icy_left -= + (handle->wr_index - handle->hdr_index + handle->buffer_length) % handle->buffer_length; + } + if (DEBUG_ICY) + g_print("Left %d\n", handle->icy_left); + handle->in_icy_meta = 0; break; } handle->hdr_index = (i + 2) % handle->buffer_length; @@ -226,7 +368,6 @@ i = (i + 1) % handle->buffer_length; } } - } return ret; } @@ -253,6 +394,7 @@ handle->header = 1; handle->hdr_index = 0; + handle->icy_interval = 0; result = curl_easy_perform(handle->curl); if (result == CURLE_OK) @@ -327,6 +469,7 @@ handle->curl = curl_easy_init(); handle->rd_index = 0; handle->wr_index = 0; + handle->meta_abs = 0; handle->rd_abs = 0; handle->wr_abs = 0; handle->buffer_length = BUFFER_SIZE; @@ -345,7 +488,11 @@ 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"); + { + struct curl_slist *hdr = NULL; + hdr = curl_slist_append(hdr, "icy-metadata:1"); + curl_easy_setopt(handle->curl, CURLOPT_HTTPHEADER, hdr); + } file->handle = handle; @@ -412,6 +559,12 @@ handle->rd_index = (handle->rd_index + available) % handle->buffer_length; + if (handle->rd_abs < handle->meta_abs && + handle->rd_abs + available >= handle->meta_abs) + { + if (DEBUG_METADATA_REPORT) + g_print("New song: '%s'\n", handle->title); + } handle->rd_abs += available; ret += available; if (!available) @@ -554,8 +707,10 @@ curl_vfs_metadata_impl(VFSFile * file, const gchar * field) { CurlHandle *handle = file->handle; - if (!strcmp(field, "name")) + if (!strcmp(field, "stream-name")) return strdup(handle->name); + if (!strcmp(field, "track-name")) + return strdup(handle->title); return NULL; }