changeset 465:ffa685855003 trunk

[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.
author iabervon
date Sat, 20 Jan 2007 21:40:49 -0800
parents 65408c59b424
children 20657d994de8
files ChangeLog src/curl/curl.c
diffstat 2 files changed, 125 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- 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 <james@develia.org>
+  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 <james@develia.org>
   revision [1010]
   - added is_our_file_from_vfs function to sid, for both libsidplay v1 and v2
--- 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)