[xiph-commits] r15372 - in icecast/branches/kh/icecast: . admin src web win32

karl at svn.xiph.org karl at svn.xiph.org
Fri Oct 3 18:09:17 PDT 2008


Author: karl
Date: 2008-10-03 18:09:17 -0700 (Fri, 03 Oct 2008)
New Revision: 15372

Removed:
   icecast/branches/kh/icecast/debian/
Modified:
   icecast/branches/kh/icecast/Makefile.am
   icecast/branches/kh/icecast/NEWS
   icecast/branches/kh/icecast/admin/showlog.xsl
   icecast/branches/kh/icecast/config.h.vc6
   icecast/branches/kh/icecast/configure.in
   icecast/branches/kh/icecast/src/admin.c
   icecast/branches/kh/icecast/src/auth_htpasswd.c
   icecast/branches/kh/icecast/src/cfgfile.h
   icecast/branches/kh/icecast/src/connection.c
   icecast/branches/kh/icecast/src/format.c
   icecast/branches/kh/icecast/src/format.h
   icecast/branches/kh/icecast/src/format_kate.c
   icecast/branches/kh/icecast/src/format_mp3.c
   icecast/branches/kh/icecast/src/format_ogg.c
   icecast/branches/kh/icecast/src/format_ogg.h
   icecast/branches/kh/icecast/src/format_theora.c
   icecast/branches/kh/icecast/src/fserve.c
   icecast/branches/kh/icecast/src/global.c
   icecast/branches/kh/icecast/src/refbuf.c
   icecast/branches/kh/icecast/src/refbuf.h
   icecast/branches/kh/icecast/src/slave.c
   icecast/branches/kh/icecast/src/source.c
   icecast/branches/kh/icecast/src/stats.c
   icecast/branches/kh/icecast/src/stats.h
   icecast/branches/kh/icecast/src/util.c
   icecast/branches/kh/icecast/src/util.h
   icecast/branches/kh/icecast/src/yp.c
   icecast/branches/kh/icecast/web/status.xsl
   icecast/branches/kh/icecast/win32/icecast.dsp
   icecast/branches/kh/icecast/win32/icecast2.iss
Log:
bump version, drop debian directory as it's unmaintained here. some more thread
removal work for stats, video preview merge (not built by default) and a couple
of bug fixes from reported feedback.


Modified: icecast/branches/kh/icecast/Makefile.am
===================================================================
--- icecast/branches/kh/icecast/Makefile.am	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/Makefile.am	2008-10-04 01:09:17 UTC (rev 15372)
@@ -3,7 +3,7 @@
 AUTOMAKE_OPTIONS = foreign
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = src conf debian doc web admin win32
+SUBDIRS = src conf doc web admin win32
 
 EXTRA_DIST = HACKING config.h.vc6 m4/acx_pthread.m4 m4/ogg.m4 \
     m4/theora.m4 m4/vorbis.m4 m4/speex.m4\

Modified: icecast/branches/kh/icecast/NEWS
===================================================================
--- icecast/branches/kh/icecast/NEWS	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/NEWS	2008-10-04 01:09:17 UTC (rev 15372)
@@ -15,6 +15,21 @@
 any extra tags are show in the conf/icecast.xml.dist file
 
 
+2.3.2-kh2
+. more stats work. Clients are now processed as needed instead of a dedicated
+  thread per stats client. 
+. don't allow raw metadata updates if not from the same IP address as the source
+  client unless it is from the admin user. For some reason some source client
+  issue updates even though they are rejected.
+. missing lock on streamlist master/slave update, could cause memory corruption.
+. update to average bitrate handling.
+. allow strftime expansion on dump filename, applies at the time the stream has
+  started
+. removed debian directory, it's not maintained really so leave it to the debian
+  people to deal with it themselves.
+. experimental theora keyframe as png patch added. not built by default
+. minor memory leak plugged in fserve
+
 2.3.2-kh1
 . remove stats thread. Stats clients still have their own thread and queue.
 . fix low bandwidth theora stream problem.

Modified: icecast/branches/kh/icecast/admin/showlog.xsl
===================================================================
--- icecast/branches/kh/icecast/admin/showlog.xsl	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/admin/showlog.xsl	2008-10-04 01:09:17 UTC (rev 15372)
@@ -7,7 +7,7 @@
 <head>
 <title>Icecast log files</title>
 </head>
-<body style="color: white; font-size: 90%">
+<body bgcolor="#656565" style="color: white; font-size: 90%">
 <table>
 <tr><td><pre> 
 <xsl:for-each select="/icestats"> <xsl:for-each select="log"> <xsl:value-of select="." /> </xsl:for-each></xsl:for-each>

Modified: icecast/branches/kh/icecast/config.h.vc6
===================================================================
--- icecast/branches/kh/icecast/config.h.vc6	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/config.h.vc6	2008-10-04 01:09:17 UTC (rev 15372)
@@ -13,7 +13,7 @@
 /* Define to 1 if you have the <curl/curl.h> header file. */
 #define HAVE_CURL_CURL_H 1
 
-/* Define t o1 if you have the 'curl_global_init' function */
+/* Define to 1 if you have the 'curl_global_init' function */
 #define HAVE_CURL_GLOBAL_INIT 1
 
 /* Define to 1 if you have the `inet_aton' function. */
@@ -95,7 +95,7 @@
 #define PACKAGE_NAME "Icecast"
 
 /* Version number of package */
-#define VERSION "2.3.2-kh1"
+#define VERSION "2.3.2-kh2"
 
 /* Define to the version of this package. */
 #define PACKAGE_VERSION VERSION
@@ -162,3 +162,4 @@
 /* time format for strftime */
 #define ICECAST_TIME_FMT "%a, %d %b %Y %H:%M:%S"
 
+#define PATH_MAX MAX_PATH

Modified: icecast/branches/kh/icecast/configure.in
===================================================================
--- icecast/branches/kh/icecast/configure.in	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/configure.in	2008-10-04 01:09:17 UTC (rev 15372)
@@ -1,4 +1,4 @@
-AC_INIT([Icecast], [2.3.2-kh1], [karl at xiph.org])
+AC_INIT([Icecast], [2.3.2-kh2], [karl at xiph.org])
 
 AC_PREREQ(2.59)
 AC_CONFIG_SRCDIR(src/main.c)
@@ -155,7 +155,7 @@
 AC_SUBST(HAVE_KATE)
 AC_SUBST(KATE_LIBS)
 
-AC_OUTPUT([Makefile conf/Makefile debian/Makefile src/Makefile src/avl/Makefile
+AC_OUTPUT([Makefile conf/Makefile src/Makefile src/avl/Makefile
 src/httpp/Makefile src/thread/Makefile src/log/Makefile
 src/net/Makefile src/timing/Makefile doc/Makefile web/Makefile web/images/Makefile
 admin/Makefile win32/Makefile win32/res/Makefile])

Modified: icecast/branches/kh/icecast/src/admin.c
===================================================================
--- icecast/branches/kh/icecast/src/admin.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/admin.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -58,6 +58,7 @@
 static void command_manageauth(client_t *client, source_t *source,
         int response);
 static void command_buildm3u(client_t *client, const char *mount);
+static void command_show_image (client_t *client, const char* mount);
 static void command_kill_source(client_t *client, source_t *source,
         int response);
 static void command_updatemetadata(client_t *client, source_t *source,
@@ -355,6 +356,11 @@
             command_buildm3u (client, mount);
             return 0;
         }
+        if (strcmp (uri, "showimage") == 0)
+        {
+            command_show_image (client, mount);
+            return 0;
+        }
 
         /* This is a mount request, but admin user is allowed */
         if (client->authenticated == 0)
@@ -763,6 +769,29 @@
     xmlFreeDoc(doc);
 }
 
+static void command_show_image (client_t *client, const char *mount)
+{
+    source_t *source;
+
+    avl_tree_rlock (global.source_tree);
+    source = source_find_mount_raw (mount);
+    if (source && source->format && source->format->get_image)
+    {
+        thread_mutex_lock (&source->lock);
+        avl_tree_unlock (global.source_tree);
+        if (source->format->get_image (client, source->format) == 0)
+        {
+            thread_mutex_unlock (&source->lock);
+            fserve_add_client (client, NULL);
+            return;
+        }
+        thread_mutex_unlock (&source->lock);
+    }
+    else
+        avl_tree_unlock (global.source_tree);
+    client_send_404 (client, "No image available");
+}
+
 static void command_buildm3u (client_t *client, const char *mount)
 {
     const char *username = NULL;
@@ -961,6 +990,7 @@
     format_plugin_t *plugin;
     xmlDocPtr doc;
     xmlNodePtr node;
+    int same_ip = 1;
 
     doc = xmlNewDoc(XMLSTR("1.0"));
     node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
@@ -977,10 +1007,13 @@
     thread_mutex_lock (&source->lock);
 
     plugin = source->format;
+    if (source->client && strcmp (client->con->ip, source->client->con->ip) != 0)
+        if (response == RAW && connection_check_admin_pass (client->parser) == 0)
+            same_ip = 0;
 
     do
     {
-        if (plugin == NULL)
+        if (same_ip == 0 && plugin == NULL)
             break;
         if (artwork)
             stats_event (source->mount, "artwork", artwork);
@@ -1026,6 +1059,7 @@
 {
     const char *action;
     const char *value;
+    int same_ip = 1;
 
     DEBUG0("Got shoutcast metadata update request");
 
@@ -1046,6 +1080,10 @@
         return;
     }
 
+    if (source->client && strcmp (client->con->ip, source->client->con->ip) != 0)
+        if (connection_check_admin_pass (client->parser) == 0)
+            same_ip = 0;
+
     if (source->format && source->format->set_tag)
     {
         httpp_set_query_param (client->parser, "mount", client->server_conn->shoutcast_mount);
@@ -1084,7 +1122,7 @@
 
     show_mount = httpp_get_query_param (client->parser, "mount");
 
-    stats_get_xml(&doc, 1, show_mount);
+    stats_get_xml(&doc, STATS_ALL, show_mount);
     admin_send_response (doc, client, response, filename);
     xmlFreeDoc(doc);
 }

Modified: icecast/branches/kh/icecast/src/auth_htpasswd.c
===================================================================
--- icecast/branches/kh/icecast/src/auth_htpasswd.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/auth_htpasswd.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -108,6 +108,8 @@
     char *sep;
     char line [MAX_LINE_LEN];
 
+    if (htpasswd->filename == NULL)
+        return;
     if (stat (htpasswd->filename, &file_stat) < 0)
     {
         WARN1 ("failed to check status of %s", htpasswd->filename);

Modified: icecast/branches/kh/icecast/src/cfgfile.h
===================================================================
--- icecast/branches/kh/icecast/src/cfgfile.h	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/cfgfile.h	2008-10-04 01:09:17 UTC (rev 15372)
@@ -132,6 +132,14 @@
     int ssl;
 };
 
+typedef struct
+{
+    char *hostname;
+    int  port;
+    char *username;
+    char *password;
+} ice_master_details;
+
 typedef struct ice_config_tag
 {
     char *config_filename;
@@ -169,6 +177,8 @@
     listener_t *listen_sock;
     unsigned int listen_sock_count;
 
+    ice_master_details *master;
+
     char *master_server;
     int master_server_port;
     int master_update_interval;

Modified: icecast/branches/kh/icecast/src/connection.c
===================================================================
--- icecast/branches/kh/icecast/src/connection.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/connection.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -1129,7 +1129,7 @@
 
     client->respcode = 200;
     snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-            "HTTP/1.0 200 OK\r\n\r\n");
+            "HTTP/1.0 200 OK\r\ncapability: streamlist\r\n\r\n");
     client->refbuf->len = strlen (client->refbuf->data);
     fserve_add_client_callback (client, stats_callback, NULL);
 }

Modified: icecast/branches/kh/icecast/src/format.c
===================================================================
--- icecast/branches/kh/icecast/src/format.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/format.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -378,4 +378,3 @@
     return 0;
 }
 
-

Modified: icecast/branches/kh/icecast/src/format.h
===================================================================
--- icecast/branches/kh/icecast/src/format.h	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/format.h	2008-10-04 01:09:17 UTC (rev 15372)
@@ -53,6 +53,7 @@
     void (*set_tag)(struct _format_plugin_tag *plugin, const char *tag, const char *value, const char *charset);
     void (*free_plugin)(struct _format_plugin_tag *self);
     void (*apply_settings)(client_t *client, struct _format_plugin_tag *format, struct _mount_proxy *mount);
+    int (*get_image)(client_t *client, struct _format_plugin_tag *format);
 
     /* for internal state management */
     void *_state;

Modified: icecast/branches/kh/icecast/src/format_kate.c
===================================================================
--- icecast/branches/kh/icecast/src/format_kate.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/format_kate.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -38,12 +38,12 @@
 
 typedef struct _kate_codec_tag
 {
-    int             headers_done;
+    unsigned int    headers_done;
 #ifdef HAVE_KATE
     kate_info       ki;
     kate_comment    kc;
 #endif
-    int             num_headers;
+    unsigned int    num_headers;
     int             granule_shift;
     ogg_int64_t     last_iframe;
     ogg_int64_t     prev_granulepos;

Modified: icecast/branches/kh/icecast/src/format_mp3.c
===================================================================
--- icecast/branches/kh/icecast/src/format_mp3.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/format_mp3.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -484,12 +484,12 @@
             }
             return 0;
         }
+        rate_add (format->in_bitrate, bytes, global.time);
     }
     source_mp3->read_count += bytes;
     refbuf = source_mp3->read_data;
     refbuf->len = source_mp3->read_count;
     format->read_bytes += bytes;
-    rate_add (format->in_bitrate, bytes, global.time);
 
     if (source_mp3->read_count < source_mp3->queue_block_size)
     {
@@ -586,7 +586,6 @@
                     sizeof (source_mp3->build_metadata));
             source_mp3->build_metadata_offset = 0;
             source_mp3->build_metadata_len = 1 + (*src * 16);
-            rate_add (plugin->in_bitrate, source_mp3->build_metadata_len, global.time);
         }
 
         /* do we have all of the metatdata block */

Modified: icecast/branches/kh/icecast/src/format_ogg.c
===================================================================
--- icecast/branches/kh/icecast/src/format_ogg.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/format_ogg.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -54,6 +54,7 @@
 static int  create_ogg_client_data(source_t *source, client_t *client);
 static void free_ogg_client_data (client_t *client);
 
+static int get_image (client_t *client, struct _format_plugin_tag *format);
 static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf);
 static refbuf_t *ogg_get_buffer (source_t *source);
 static int write_buf_to_client (client_t *client);
@@ -152,8 +153,7 @@
     while (codec)
     {
         ogg_codec_t *next = codec->next;
-        if (codec->possible_start)
-            refbuf_release (codec->possible_start);
+        refbuf_release (codec->possible_start);
         codec->codec_free (ogg_info, codec);
         codec = next;
     }
@@ -177,6 +177,7 @@
     plugin->write_buf_to_file = write_ogg_to_file;
     plugin->create_client_data = create_ogg_client_data;
     plugin->free_plugin = format_ogg_free_plugin;
+    plugin->get_image = get_image;
     plugin->set_tag = NULL;
     plugin->apply_settings = apply_ogg_settings;
     if (strcmp (httpp_getvar (source->parser, "content-type"), "application/x-ogg") == 0)
@@ -623,4 +624,25 @@
     write_ogg_data (source, refbuf);
 }
 
+static int get_image (client_t *client, struct _format_plugin_tag *format)
+{
+    const char *serialp = httpp_get_query_param (client->parser, "serial");
+    ogg_state_t *ogg_info = format->_state;
+    ogg_codec_t *codec = ogg_info->codecs;
+    long serial;
 
+    if (serialp)
+        serial = atoll (serialp);
+    while (codec)
+    {
+        if (serialp == NULL || serial == codec->os.serialno)
+        {
+            int ret = 0;
+            if (codec->get_image)
+                ret = codec->get_image (client, codec);
+            return ret;
+        }
+        codec = codec->next;
+    }
+    return 0;
+}

Modified: icecast/branches/kh/icecast/src/format_ogg.h
===================================================================
--- icecast/branches/kh/icecast/src/format_ogg.h	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/format_ogg.h	2008-10-04 01:09:17 UTC (rev 15372)
@@ -65,6 +65,7 @@
     refbuf_t *(*process_page)(ogg_state_t *ogg_info,
             struct ogg_codec_tag *codec, ogg_page *page);
     void (*codec_free)(ogg_state_t *ogg_info, struct ogg_codec_tag *codec);
+    int  (*get_image)(client_t *client, struct ogg_codec_tag *codec);
 } ogg_codec_t;
 
 

Modified: icecast/branches/kh/icecast/src/format_theora.c
===================================================================
--- icecast/branches/kh/icecast/src/format_theora.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/format_theora.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -31,8 +31,21 @@
 
 #define CATMODULE "format-theora"
 #include "logging.h"
+#ifdef WITH_VIDEO_PREVIEW
+#include <png.h>
 
+typedef struct _video_preview_struct
+{
+	png_byte*   rgb_image                     ;
+	int         png_compression_level         ;
 
+	int         video_width                   ;
+	int         video_height                  ;
+	int         x_crop_offset                 ;
+	int         y_crop_offset                 ;
+} video_preview_t;
+#endif
+
 typedef struct _theora_codec_tag
 {
     theora_info     ti;
@@ -40,9 +53,255 @@
     int             granule_shift;
     ogg_int64_t     last_iframe;
     ogg_int64_t     prev_granulepos;
+#ifdef WITH_VIDEO_PREVIEW
+    theora_state    td;
+    video_preview_t *video_preview;
+    int            frame_count;
+#endif
 } theora_codec_t;
 
 
+ /* video_preview.c 
+ * Copyright 2005, Silvano Galliani aka kysucix <kysucix at dyne.org>
+ */
+
+
+#include "logging.h"
+
+
+#ifdef WITH_VIDEO_PREVIEW
+struct preview_details
+{
+    unsigned int total_length;
+    refbuf_t **last;
+};
+
+static void yuv2rgb (yuv_buffer *_yuv, video_preview_t *video_preview);
+static int clip (int x);
+
+
+static void user_write_data (png_structp png_ptr, png_bytep data, png_size_t length)
+{
+#define BUFFER_BLOCK_SZ 316*1024
+    struct preview_details *details = png_get_io_ptr (png_ptr);
+    unsigned int offset = 0;
+    int c = 0;
+
+    if (*details->last == NULL)
+    {
+        /* special case */
+        refbuf_t *r = refbuf_new (BUFFER_BLOCK_SZ);
+        r->len = 0;
+        *details->last = r;
+        c++;
+    }
+    while (length)
+    {
+        unsigned int amount;
+        refbuf_t *buffer = *details->last;
+        if (buffer->len == BUFFER_BLOCK_SZ)
+        {
+            refbuf_t *last = buffer;
+            buffer = refbuf_new (BUFFER_BLOCK_SZ);
+            buffer->len = 0;
+            last->next = buffer;
+            details->last = &last->next;
+            c++;
+        }
+        amount = BUFFER_BLOCK_SZ - buffer->len;
+        if (amount > length)
+            amount = length;
+        memcpy (buffer->data+buffer->len, data+offset, amount);
+        buffer->len += amount;
+        offset += amount;
+        length -= amount;
+    }
+    details->total_length += offset;
+}
+
+
+static void user_flush_data (png_structp png_ptr)
+{
+}
+
+static void user_error (png_structp png_ptr, png_const_charp c)
+{
+    longjmp (png_ptr->jmpbuf, 1);
+}
+
+
+void free_video_preview (video_preview_t *video_preview)
+{
+    DEBUG0 ("freeing video preview");
+    free (video_preview->rgb_image);
+    free (video_preview);
+}
+
+
+video_preview_t *init_video_preview (int width, int height, int _x_crop_offset, int _y_crop_offset) 
+{
+    DEBUG0("init video preview");
+
+    video_preview_t *video_preview =  calloc (1, sizeof (video_preview_t));
+
+    /* init structure */
+    video_preview -> rgb_image              = NULL;
+    video_preview -> png_compression_level  = Z_BEST_SPEED;
+
+    video_preview -> video_width            = width;
+    video_preview -> video_height           = height;
+    video_preview -> x_crop_offset          = _x_crop_offset;
+    video_preview -> y_crop_offset          = _y_crop_offset;
+
+    /* malloc rgb image */
+    video_preview -> rgb_image = (png_byte *)calloc (1, video_preview -> video_width * video_preview -> video_height * 4 );
+    if (video_preview->rgb_image == NULL)
+    {
+        ERROR0 ("Can't allocate memory for rgb_image");
+        free_video_preview (video_preview);
+        return NULL;
+    }
+    return video_preview;
+}
+
+
+static int write_video_preview (client_t *client, video_preview_t *video_preview)
+{
+    int i;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+    struct preview_details preview;
+
+    preview.total_length = 0;
+    preview.last = &client->refbuf->next;
+
+    png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
+    if (png_ptr == NULL)
+        return -1;
+
+    info_ptr = png_create_info_struct (png_ptr);
+    if (info_ptr == NULL)
+    {
+        png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
+        return -1;
+    }
+
+    if (setjmp (png_ptr->jmpbuf))
+    {
+        png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
+        return -1;
+    }
+
+    png_set_error_fn (png_ptr, NULL, user_error, user_error);
+    png_set_write_fn (png_ptr, &preview, user_write_data, user_flush_data);
+
+    png_set_filter( png_ptr, 0, PNG_FILTER_NONE );
+
+    /* set the zlib compression level */
+    png_set_compression_level (png_ptr, video_preview->png_compression_level);
+
+    png_set_IHDR (png_ptr, info_ptr, video_preview->video_width , video_preview->video_height,
+            8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
+            PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+    png_write_info (png_ptr, info_ptr);
+    ERROR0("finished writing png header");
+
+    /* write image to hard disk */
+    for ( i = 0; i < video_preview -> video_height; i++)
+        png_write_row (png_ptr, 
+                video_preview->rgb_image + i * (video_preview->video_width) * 4 );
+
+    png_write_end (png_ptr, info_ptr);
+    png_destroy_write_struct (&png_ptr, &info_ptr);
+
+    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, "HTTP/1.0 200 OK\r\n"
+            "Content-Length: %d\r\nContentType: image/png\r\n\r\n", preview.total_length);
+    client->refbuf->len = strlen (client->refbuf->data);
+    client->respcode = 200;
+
+	return 0;
+}
+
+
+static int get_image (client_t *client, struct ogg_codec_tag *codec)
+{
+    theora_codec_t *theora = codec->specific;
+    return write_video_preview (client, theora->video_preview);
+}
+
+
+/* ok it has to be optimized but for now it's clean, and it's ok ;) */
+static void yuv2rgb (yuv_buffer *_yuv, video_preview_t *video_preview) 
+{
+	int                     i,j;
+	int                     crop_offset;
+
+	int			y_offset;
+	int			ypp_offset;
+	int			uv_offset;
+
+	/* rgba surface pointer */
+	unsigned char          *prgb;
+	yuv_buffer             *yuv;
+
+	unsigned char    	y;
+	unsigned char		ypp;
+	unsigned char		u;
+	unsigned char		v;
+
+	yuv = _yuv;
+	
+	crop_offset = (video_preview -> x_crop_offset) + 
+		(yuv -> y_stride) * (video_preview -> y_crop_offset);
+	prgb = (unsigned char *)video_preview -> rgb_image;
+
+	for (i = 0; i < video_preview -> video_height; i++ ) {
+		for ( j = 0; j < video_preview -> video_width / 2; j++ ) {
+
+			y_offset	= yuv -> y_stride  *  i    + j*2 + crop_offset;
+			ypp_offset	= yuv -> y_stride  *  i    + j*2 + crop_offset + 1;
+			uv_offset	= yuv -> uv_stride * (i/2) + j   + crop_offset;
+
+			y		= *(yuv->y + y_offset);
+			ypp		= *(yuv->y + ypp_offset);
+			u		= *(yuv->u + uv_offset);
+			v		= *(yuv->v + uv_offset);
+
+			/* R G B A */
+			*prgb				=  clip (y + 1.402f * (v-128)) ;
+			prgb++;
+			*prgb				=  clip (y - 0.34414f * (u-128) - 0.71414f * (v-128)) ;
+			prgb++;
+			*prgb				=  clip (y + 1.772 * (u-128)) ;
+			prgb++;
+			*prgb				= 255;
+			prgb++;
+
+			/* R G B A */
+			*prgb				=  clip (ypp + 1.402f * (v-128)) ;
+			prgb++;
+			*prgb				=  clip (ypp - 0.34414f * (u-128) - 0.71414f * (v-128)) ;
+			prgb++;
+			*prgb				=  clip (ypp + 1.772 * (u-128)) ;
+			prgb++;
+			*prgb				= 255;
+			prgb++;
+		}
+	}
+}
+
+static int clip (int x)
+{
+	if (x > 255)
+		return 255;
+	else if (x < 0)
+		return 0;
+	return x;
+}
+#endif
+
+
 static void theora_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
 {
     theora_codec_t *theora = codec->specific;
@@ -52,6 +311,14 @@
     stats_event (ogg_info->mount, "video_quality", NULL);
     stats_event (ogg_info->mount, "frame_rate", NULL);
     stats_event (ogg_info->mount, "frame_size", NULL);
+#ifdef WITH_VIDEO_PREVIEW
+    stats_event (ogg_info->mount, "video_preview", NULL);
+    if (theora->video_preview)
+    {
+        free_video_preview (theora->video_preview);
+        theora_clear (&theora->td);
+    }
+#endif
     theora_info_clear (&theora->ti);
     theora_comment_clear (&theora->tc);
     ogg_stream_clear (&codec->os);
@@ -113,7 +380,30 @@
             return NULL;
         }
         if (theora_packet_iskeyframe (&packet))
+        {
             has_keyframe = 1;
+#ifdef WITH_VIDEO_PREVIEW
+            if (theora->video_preview) 
+            {
+                if (theora->frame_count == -1) 
+                {
+                    theora_decode_init (&theora->td, &theora->ti);
+                    theora->frame_count = 0;
+                }
+                if (theora->frame_count % 16)
+                    theora->frame_count++;
+                else
+                {
+                    yuv_buffer      yuv;
+
+                    theora_decode_packetin (&theora->td, &packet);
+                    theora_decode_YUVout   (&theora->td, &yuv);
+                    yuv2rgb (&yuv, theora->video_preview);
+                    theora->frame_count = 1;
+                }
+            }
+#endif
+        }
     }
     if (header_page)
     {
@@ -126,8 +416,7 @@
 
     if (granulepos != theora->prev_granulepos || granulepos == 0)
     {
-        if (codec->possible_start)
-            refbuf_release (codec->possible_start);
+        refbuf_release (codec->possible_start);
         refbuf_addref (refbuf);
         codec->possible_start = refbuf;
     }
@@ -182,6 +471,20 @@
         codec->filtered = 1;
     codec->name = "Theora";
 
+#ifdef WITH_VIDEO_PREVIEW
+    /* check for video_preview config and video_preview initialization */
+    if (1)
+    {
+        theora_info *ti = &theora_codec->ti;
+        theora_codec->video_preview = init_video_preview (ti->width, ti->height, ti->offset_x, ti->offset_y);
+        theora_codec->frame_count = -1;
+        codec->get_image = get_image;
+        stats_event_args (ogg_info->mount, "video_preview", "/admin/showimage?mount=%s&serial=%ld", ogg_info->mount, codec->os.serialno);
+        //theora_decode_init (&theora_codec->td, &theora_codec->ti);
+        // theora_codec->frame_count = -1;
+    }
+#endif
+
     format_ogg_attach_header (codec, page);
     if (codec->filtered == 0)
         ogg_info->codec_sync = codec;

Modified: icecast/branches/kh/icecast/src/fserve.c
===================================================================
--- icecast/branches/kh/icecast/src/fserve.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/fserve.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -296,8 +296,9 @@
                             client_tree_changed = 1;
                             continue;
                         }
-                        client_set_queue (client, refbuf->next);
-                        refbuf = client->refbuf;
+                        refbuf = refbuf->next;
+                        refbuf_release (client->refbuf);
+                        client->refbuf = refbuf;
                         bytes = refbuf->len;
                     }
                     refbuf->len = bytes;

Modified: icecast/branches/kh/icecast/src/global.c
===================================================================
--- icecast/branches/kh/icecast/src/global.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/global.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -45,8 +45,7 @@
     global.source_tree = avl_tree_new(source_compare_sources, NULL);
     thread_mutex_create("global", &_global_mutex);
     thread_spin_create ("xyz", &global.spinlock);
-    /* do a sampling based on 1/10ths of a second */
-    global.out_bitrate = rate_setup (60);
+    global.out_bitrate = rate_setup (151, 1000);
 }
 
 void global_shutdown(void)
@@ -70,10 +69,8 @@
 
 void global_add_bitrates (struct rate_calc *rate, unsigned long value)
 {
-    time_t t = (time_t)(global.time_ms/100);
-
     thread_spin_lock (&global.spinlock);
-    rate_add (rate, value, t);
+    rate_add (rate, value, global.time_ms);
     thread_spin_unlock (&global.spinlock);
 }
 
@@ -88,7 +85,7 @@
 {
     unsigned long v;
     thread_spin_lock (&global.spinlock);
-    v = rate_avg (rate)*10L;
+    v = rate_avg (rate);
     thread_spin_unlock (&global.spinlock);
     return v;
 }

Modified: icecast/branches/kh/icecast/src/refbuf.c
===================================================================
--- icecast/branches/kh/icecast/src/refbuf.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/refbuf.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -35,7 +35,7 @@
 {
 }
 
-refbuf_t *refbuf_new(size_t size)
+refbuf_t *refbuf_new (unsigned int size)
 {
     refbuf_t *refbuf;
 

Modified: icecast/branches/kh/icecast/src/refbuf.h
===================================================================
--- icecast/branches/kh/icecast/src/refbuf.h	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/refbuf.h	2008-10-04 01:09:17 UTC (rev 15372)
@@ -35,7 +35,7 @@
 void refbuf_initialize(void);
 void refbuf_shutdown(void);
 
-refbuf_t *refbuf_new(size_t size);
+refbuf_t *refbuf_new(unsigned int size);
 void refbuf_addref(refbuf_t *self);
 void refbuf_release(refbuf_t *self);
 

Modified: icecast/branches/kh/icecast/src/slave.c
===================================================================
--- icecast/branches/kh/icecast/src/slave.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/slave.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -1075,12 +1075,17 @@
 
             update_from_master (config);
 
+            thread_mutex_lock (&(config_locks()->relay_lock));
             cleanup_relays = update_relays (&global.relays, config->relay);
 
             config_release_config();
         }
+        else
+            thread_mutex_lock (&(config_locks()->relay_lock));
+
         relay_check_streams (global.relays, cleanup_relays, skip_timer);
         relay_check_streams (global.master_relays, NULL, skip_timer);
+        thread_mutex_unlock (&(config_locks()->relay_lock));
 
         if (update_settings)
         {

Modified: icecast/branches/kh/icecast/src/source.c
===================================================================
--- icecast/branches/kh/icecast/src/source.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/source.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -254,6 +254,8 @@
     {
         refbuf_t *to_go = p;
         p = to_go->next;
+        if (to_go->_count > 1)
+            WARN1 ("buffer is %d", to_go->_count);
         refbuf_release (to_go);
     }
     /* the source holds 2 references on the very latest so that one
@@ -434,9 +436,9 @@
     unsigned long kbytes_read = source->bytes_read_since_update/1024;
 
     source->format->sent_bytes += kbytes_sent*1024;
-    stats_event_args (source->mount, "outgoing_bitrate", "%ld", 
+    stats_event_args (source->mount, "outgoing_kbitrate", "%ld",
             (8 * rate_avg (source->format->out_bitrate))/1000);
-    stats_event_args (source->mount, "incoming_bitrate", "%ld", incoming_rate/1000);
+    stats_event_args (source->mount, "incoming_bitrate", "%ld", incoming_rate);
     stats_event_args (source->mount, "total_bytes_read",
             "%"PRIu64, source->format->read_bytes);
     stats_event_args (source->mount, "total_bytes_sent",
@@ -682,7 +684,7 @@
 
         total_written += bytes;
     }
-    rate_add (source->format->out_bitrate, total_written, global.time);
+    rate_add (source->format->out_bitrate, total_written, global.time_ms);
     source->bytes_sent_since_update += total_written;
 
     global_add_bitrates (global.out_bitrate, total_written);
@@ -778,7 +780,7 @@
         }
         stats_event_args (source->mount, "listeners", "%lu", source->listeners);
         if (source->listeners == 0)
-            rate_add (source->format->out_bitrate, 0, 0);
+            rate_reduce (source->format->out_bitrate, 0);
         /* change of listener numbers, so reduce scope of global sampling */
         global_reduce_bitrate_sampling (global.out_bitrate);
         /* do we need to shutdown an on-demand relay */
@@ -874,8 +876,8 @@
 
     thread_mutex_lock (&source->lock);
 
-    source->format->in_bitrate = rate_setup (source->avg_bitrate_duration+1);
-    source->format->out_bitrate = rate_setup (source->avg_bitrate_duration+1);
+    source->format->in_bitrate = rate_setup (source->avg_bitrate_duration+1, 1);
+    source->format->out_bitrate = rate_setup (120, 1000);
     source->running = 1;
 }
 
@@ -1212,10 +1214,17 @@
         source->avg_bitrate_duration = 60;
 
     /* needs a better mechanism, probably via a client_t handle */
+    free (source->dumpfilename);
+    source->dumpfilename = NULL;
     if (mountinfo && mountinfo->dumpfile)
     {
-        free (source->dumpfilename);
-        source->dumpfilename = strdup (mountinfo->dumpfile);
+        time_t now = time(NULL);
+        struct tm local;
+        char buffer[PATH_MAX];
+
+        localtime_r (&now, &local);
+        strftime (buffer, sizeof (buffer), mountinfo->dumpfile, &local);
+        source->dumpfilename = strdup (buffer);
     }
     /* handle changes in intro file setting */
     if (source->intro_file)
@@ -1268,7 +1277,7 @@
     /*  skip if source is a fallback to file */
     if (source->running && source->client == NULL)
     {
-        stats_event_hidden (source->mount, NULL, 1);
+        stats_event_hidden (source->mount, NULL, NULL, STATS_HIDDEN);
         return;
     }
     /* set global settings first */
@@ -1294,13 +1303,6 @@
 
     if (mountinfo)
     {
-        if (mountinfo->hidden)
-        {
-            stats_event_hidden (source->mount, NULL, 1);
-            DEBUG0 ("hidden from public");
-        }
-        else
-            stats_event_hidden (source->mount, NULL, 0);
         if (mountinfo->on_connect)
             DEBUG1 ("connect script \"%s\"", mountinfo->on_connect);
         if (mountinfo->on_disconnect)
@@ -1309,12 +1311,21 @@
             DEBUG1 ("fallback_when_full to %u", mountinfo->fallback_when_full);
         DEBUG1 ("max listeners to %d", mountinfo->max_listeners);
         stats_event_args (source->mount, "max_listeners", "%d", mountinfo->max_listeners);
+        stats_event_hidden (source->mount, "cluster_password", mountinfo->cluster_password, STATS_SLAVE);
+        if (mountinfo->hidden)
+        {
+            stats_event_hidden (source->mount, NULL, NULL, STATS_HIDDEN);
+            DEBUG0 ("hidden from public");
+        }
+        else
+            stats_event_hidden (source->mount, NULL, NULL, STATS_PUBLIC);
     }
     else
     {
         DEBUG0 ("max listeners is not specified");
         stats_event (source->mount, "max_listeners", "unlimited");
-        stats_event_hidden (source->mount, NULL, 0);
+        stats_event_hidden (source->mount, "cluster_password", NULL, STATS_SLAVE);
+        stats_event_hidden (source->mount, NULL, NULL, STATS_PUBLIC);
     }
     DEBUG1 ("public set to %d", source->yp_public);
     DEBUG1 ("queue size to %u", source->queue_size_limit);
@@ -1459,7 +1470,7 @@
         httpp_setvar (parser, "content-type", type);
         free (type);
 
-        stats_event_hidden (source->mount, NULL, 1);
+        stats_event_hidden (source->mount, NULL, NULL, STATS_HIDDEN);
         source->yp_public = 0;
         source->intro_file = file;
         source->parser = parser;
@@ -1522,7 +1533,7 @@
             }
             else if (update_all)
             {
-                stats_event_hidden (mount->mountname, NULL, mount->hidden);
+                stats_event_hidden (mount->mountname, NULL, NULL, mount->hidden?STATS_HIDDEN:0);
                 stats_event_args (mount->mountname, "listenurl", "http://%s:%d%s",
                         config->hostname, config->port, mount->mountname);
                 stats_event (mount->mountname, "listeners", "0");

Modified: icecast/branches/kh/icecast/src/stats.c
===================================================================
--- icecast/branches/kh/icecast/src/stats.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/stats.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -53,7 +53,7 @@
 #define STATS_EVENT_ADD     3
 #define STATS_EVENT_SUB     4
 #define STATS_EVENT_REMOVE  5
-#define STATS_EVENT_HIDDEN  6
+#define STATS_EVENT_HIDDEN  0x80
 
 typedef struct _stats_node_tag
 {
@@ -80,43 +80,47 @@
     avl_tree *stats_tree;
 } stats_source_t;
 
-typedef struct _event_queue_tag
-{
-    volatile stats_event_t *head;
-    volatile stats_event_t **tail;
-} event_queue_t;
-
-#define event_queue_init(qp)    { (qp)->head = NULL; (qp)->tail = &(qp)->head; }
-
 typedef struct _event_listener_tag
 {
-    event_queue_t queue;
-    mutex_t mutex;
+    client_t *client;
     int master;
+    int hidden_level;
     char *source;
 
+    /* queue for unwritten stats to stats clients */
+    refbuf_t *queue, **queue_recent_p;
+    unsigned int content_len;
+
     struct _event_listener_tag *next;
 } event_listener_t;
 
+
+typedef struct _stats_tag
+{
+    avl_tree *global_tree;
+    avl_tree *source_tree;
+
+    /* list of listeners for stats */
+    event_listener_t *event_listeners, *listeners_removed;
+
+} stats_t;
+
 static volatile int _stats_running = 0;
-static volatile int _stats_threads = 0;
 
 static stats_t _stats;
 static mutex_t _stats_mutex;
 
-static volatile event_listener_t *_event_listeners;
 
-
 static int _compare_stats(void *a, void *b, void *arg);
 static int _compare_source_stats(void *a, void *b, void *arg);
 static int _free_stats(void *key);
 static int _free_source_stats(void *key);
-static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue);
 static stats_node_t *_find_node(avl_tree *tree, const char *name);
 static stats_source_t *_find_source(avl_tree *tree, const char *source);
-static void _free_event(stats_event_t *event);
-static stats_event_t *_get_event_from_queue (event_queue_t *queue);
 static void process_event (stats_event_t *event);
+static void _add_stats_to_stats_client (event_listener_t *listener, const char *fmt, va_list ap);
+static void stats_listener_send (int flags, const char *fmt, ...);
+static void process_event_unlocked (stats_event_t *event);
 
 
 /* simple helper function for creating an event */
@@ -125,8 +129,10 @@
     event->source = (char *)source;
     event->name = (char *)name;
     event->value = (char *)value;
+    event->hidden = STATS_PUBLIC;
+    if (source) event->hidden |= STATS_SLAVE;
     if (value)
-        event->action = 0;
+        event->action = STATS_EVENT_SET;
     else
         event->action = STATS_EVENT_REMOVE;
 }
@@ -134,76 +140,53 @@
 
 void stats_initialize(void)
 {
-    _event_listeners = NULL;
+    if (_stats_running)
+        return;
 
     /* set up global struct */
     _stats.global_tree = avl_tree_new(_compare_stats, NULL);
     _stats.source_tree = avl_tree_new(_compare_source_stats, NULL);
 
+    _stats.event_listeners = NULL;
+    _stats.listeners_removed = NULL;
+
     /* set up global mutex */
     thread_mutex_create("stats", &_stats_mutex);
 
-    /* fire off the stats thread */
     _stats_running = 1;
 
     stats_event_time (NULL, "server_start");
 
     /* global currently active stats */
-    stats_event (NULL, "clients", "0");
-    stats_event (NULL, "connections", "0");
-    stats_event (NULL, "sources", "0");
-    stats_event (NULL, "stats", "0");
+    stats_event_hidden (NULL, "clients", "0", STATS_COUNTERS);
+    stats_event_hidden (NULL, "connections", "0", STATS_COUNTERS);
+    stats_event_hidden (NULL, "sources", "0", STATS_COUNTERS);
+    stats_event_hidden (NULL, "stats", "0", STATS_COUNTERS);
     stats_event (NULL, "listeners", "0");
 
     /* global accumulating stats */
-    stats_event (NULL, "client_connections", "0");
-    stats_event (NULL, "source_client_connections", "0");
-    stats_event (NULL, "source_relay_connections", "0");
-    stats_event (NULL, "source_total_connections", "0");
-    stats_event (NULL, "stats_connections", "0");
-    stats_event (NULL, "listener_connections", "0");
-    stats_event (NULL, "outgoing_kbitrate", "0");
+    stats_event_hidden (NULL, "client_connections", "0", STATS_COUNTERS);
+    stats_event_hidden (NULL, "source_client_connections", "0", STATS_COUNTERS);
+    stats_event_hidden (NULL, "source_relay_connections", "0", STATS_COUNTERS);
+    stats_event_hidden (NULL, "source_total_connections", "0", STATS_COUNTERS);
+    stats_event_hidden (NULL, "stats_connections", "0", STATS_COUNTERS);
+    stats_event_hidden (NULL, "listener_connections", "0", STATS_COUNTERS);
+    stats_event_hidden (NULL, "outgoing_kbitrate", "0", STATS_COUNTERS);
 }
 
 void stats_shutdown(void)
 {
-    int n;
-
     if(!_stats_running) /* We can't shutdown if we're not running. */
         return;
 
-    /* wait for thread to exit */
     _stats_running = 0;
 
-    /* wait for other threads to shut down */
-    do {
-        thread_sleep(300000);
-        thread_mutex_lock(&_stats_mutex);
-        n = _stats_threads;
-        thread_mutex_unlock(&_stats_mutex);
-    } while (n > 0);
-    INFO0("stats thread finished");
-
-    /* free the queues */
-
     thread_mutex_destroy(&_stats_mutex);
     avl_tree_free(_stats.source_tree, _free_source_stats);
     avl_tree_free(_stats.global_tree, _free_stats);
 }
 
-stats_t *stats_get_stats(void)
-{
-    /* lock global stats
-    
-     copy stats
 
-     unlock global stats
-
-     return copied stats */
-
-    return NULL;
-}
-
 /* simple name=tag stat create/update */
 void stats_event(const char *source, const char *name, const char *value)
 {
@@ -259,29 +242,27 @@
 
 /* make stat hidden (non-zero). name can be NULL if it applies to a whole
  * source stats tree. */
-void stats_event_hidden (const char *source, const char *name, int hidden)
+void stats_event_hidden (const char *source, const char *name, const char *value, int hidden)
 {
-    const char *str = NULL;
     stats_event_t event;
 
-    if (hidden)
-        str = "";
-    build_event (&event, source, name, NULL);
-    event.action = STATS_EVENT_HIDDEN;
+    build_event (&event, source, name, value);
+    event.hidden = hidden;
+    event.action |= STATS_EVENT_HIDDEN;
     process_event (&event);
 }
 
 /* printf style formatting for stat create/update */
 void stats_event_args(const char *source, char *name, char *format, ...)
 {
-    char buf[1024];
     va_list val;
     int ret;
+    char buf[1024];
 
     if (name == NULL)
         return;
     va_start(val, format);
-    ret = vsnprintf(buf, 1024, format, val);
+    ret = vsnprintf(buf, sizeof (buf), format, val);
     va_end(val);
 
     if (ret < 0 || (unsigned int)ret >= sizeof (buf))
@@ -420,38 +401,16 @@
     return NULL;
 }
 
-static stats_event_t *_copy_event(stats_event_t *event)
-{
-    stats_event_t *copy = (stats_event_t *)calloc(1, sizeof(stats_event_t));
-    if (event->source) 
-        copy->source = (char *)strdup(event->source);
-    else
-        copy->source = NULL;
-    if (event->name)
-        copy->name = (char *)strdup(event->name);
-    if (event->value)
-        copy->value = (char *)strdup(event->value);
-    else
-        copy->value = NULL;
-    copy->hidden = event->hidden;
-    copy->next = NULL;
 
-    return copy;
-}
-
-
 /* helper to apply specialised changes to a stats node */
 static void modify_node_event (stats_node_t *node, stats_event_t *event)
 {
     if (node == NULL || event == NULL)
         return;
-    if (event->action == STATS_EVENT_HIDDEN)
+    if (event->action & STATS_EVENT_HIDDEN)
     {
-        if (event->value)
-            node->hidden = 1;
-        else
-            node->hidden = 0;
-        return;
+        node->hidden = event->hidden;
+        event->action &= ~STATS_EVENT_HIDDEN;
     }
     if (event->action != STATS_EVENT_SET)
     {
@@ -483,7 +442,7 @@
 
 static void process_global_event (stats_event_t *event)
 {
-    stats_node_t *node;
+    stats_node_t *node = NULL;
 
     /* DEBUG3("global event %s %s %d", event->name, event->value, event->action); */
     if (event->action == STATS_EVENT_REMOVE)
@@ -491,16 +450,18 @@
         /* we're deleting */
         node = _find_node(_stats.global_tree, event->name);
         if (node != NULL)
+        {
+            stats_listener_send (node->hidden, "DELETE global %s\n", event->name);
             avl_delete(_stats.global_tree, (void *)node, _free_stats);
+        }
         return;
     }
     node = _find_node(_stats.global_tree, event->name);
     if (node)
     {
         modify_node_event (node, event);
-        DEBUG3 ("update node on %s \"%s\" (%s)",
-                event->source ? event->source : "global",
-                node->name, node->value);
+        stats_listener_send (node->hidden, "EVENT global %s %s\n", node->name, node->value);
+        DEBUG2 ("update node on global \"%s\" (%s)", node->name, node->value);
     }
     else
     {
@@ -508,8 +469,10 @@
         node = (stats_node_t *)calloc(1, sizeof(stats_node_t));
         node->name = (char *)strdup(event->name);
         node->value = (char *)strdup(event->value);
+        node->hidden = event->hidden;
 
         avl_insert(_stats.global_tree, (void *)node);
+        stats_listener_send (node->hidden, "EVENT global %s %s\n", event->name, event->value);
     }
 }
 
@@ -517,6 +480,8 @@
 static void process_source_event (stats_event_t *event)
 {
     stats_source_t *snode = _find_source(_stats.source_tree, event->source);
+    stats_node_t *node = NULL;
+
     if (snode == NULL)
     {
         if (event->action == STATS_EVENT_REMOVE)
@@ -527,16 +492,13 @@
         DEBUG1 ("new source stat %s", event->source);
         snode->source = (char *)strdup(event->source);
         snode->stats_tree = avl_tree_new(_compare_stats, NULL);
-        if (event->action == STATS_EVENT_HIDDEN)
-            snode->hidden = 1;
-        else
-            snode->hidden = 0;
+        snode->hidden = STATS_SLAVE|STATS_GENERAL|STATS_HIDDEN;
 
         avl_insert(_stats.source_tree, (void *)snode);
     }
     if (event->name)
     {
-        stats_node_t *node = _find_node(snode->stats_tree, event->name);
+        node = _find_node (snode->stats_tree, event->name);
         if (node == NULL)
         {
             if (event->action == STATS_EVENT_REMOVE)
@@ -548,8 +510,11 @@
                 node = (stats_node_t *)calloc(1,sizeof(stats_node_t));
                 node->name = (char *)strdup(event->name);
                 node->value = (char *)strdup(event->value);
-                node->hidden = snode->hidden;
+                node->hidden = event->hidden;
+                if (snode->hidden | STATS_HIDDEN)
+                    node->hidden |= STATS_HIDDEN;
 
+                stats_listener_send (node->hidden, "EVENT %s %s %s\n", event->source, event->name, event->value);
                 avl_insert(snode->stats_tree, (void *)node);
             }
             return;
@@ -557,24 +522,42 @@
         if (event->action == STATS_EVENT_REMOVE)
         {
             DEBUG1 ("delete node %s", event->name);
+            stats_listener_send (node->hidden, "DELETE %s %s\n", event->source, event->name);
             avl_delete(snode->stats_tree, (void *)node, _free_stats);
             return;
         }
         modify_node_event (node, event);
         return;
     }
-    if (event->action == STATS_EVENT_HIDDEN)
+    /* change source hidden status */
+    if (event->action & STATS_EVENT_HIDDEN)
     {
         avl_node *node = avl_get_first (snode->stats_tree);
+        int visible = 0;
 
-        if (event->value)
-            snode->hidden = 1;
+        if ((event->hidden&STATS_HIDDEN) == (snode->hidden&STATS_HIDDEN))
+            return;
+        if (snode->hidden & STATS_HIDDEN)
+        {
+            snode->hidden &= ~STATS_HIDDEN;
+            stats_listener_send (snode->hidden, "NEW %s\n", snode->source);
+            visible = 1;
+        }
         else
-            snode->hidden = 0;
+        {
+            stats_listener_send (snode->hidden, "DELETE %s\n", snode->source);
+            snode->hidden |= STATS_HIDDEN;
+        }
         while (node)
         {
             stats_node_t *stats = (stats_node_t*)node->key;
-            stats->hidden = snode->hidden;
+            if (visible)
+            {
+                stats->hidden &= ~STATS_HIDDEN;
+                stats_listener_send (stats->hidden, "EVENT %s %s %s\n", snode->source, stats->name, stats->value);
+            }
+            else
+                stats->hidden |= STATS_HIDDEN;
             node = avl_get_next (node);
         }
         return;
@@ -582,6 +565,7 @@
     if (event->action == STATS_EVENT_REMOVE)
     {
         DEBUG1 ("delete source node %s", event->source);
+        stats_listener_send (snode->hidden, "DELETE %s\n", event->source);
         avl_delete(_stats.source_tree, (void *)snode, _free_source_stats);
     }
 }
@@ -595,39 +579,98 @@
 
     localtime_r (&now, &local);
     strftime (buffer, sizeof (buffer), ICECAST_TIME_FMT, &local);
-    stats_event (mount, name, buffer);
+    stats_event_hidden (mount, name, buffer, STATS_GENERAL);
 }
 
-void stats_listener_send (stats_event_t *event)
+
+static void stats_listeners_send (event_listener_t *listener)
 {
-    event_listener_t *listener = (event_listener_t *)_event_listeners;
+    int loop = 6;
+    int ret;
+    client_t *client = listener->client;
 
-    while (listener)
+    while (loop)
     {
-        int send_it = 1;
+        if (format_advance_queue (NULL, client) < 0)
+            break;
+        ret = format_generic_write_to_client (client);
+        if (ret < 0)
+            break;
+        listener->content_len -= ret;
+        loop--;
+    }
+}
 
-        if (event->source && listener->source &&
-                strcmp (event->source, listener->source) != 0)
-            send_it = 0;
+static void clear_stats_queue (event_listener_t *listener)
+{
+    while (listener->queue && listener->queue->_count == 1)
+    {
+        refbuf_t *to_go = listener->queue;
+        listener->queue = to_go->next;
+        refbuf_release (to_go);
+    }
+}
 
-        if (send_it)
+
+static void stats_listener_send (int hidden_level, const char *fmt, ...)
+{
+    va_list ap;
+    event_listener_t *listener = _stats.event_listeners,
+                     **trail = &_stats.event_listeners;
+
+    va_start(ap, fmt);
+
+    while (listener)
+    {
+        client_t *client = listener->client;
+
+        if (listener->hidden_level & hidden_level)
         {
-            stats_event_t *copy = _copy_event(event);
-            thread_mutex_lock (&listener->mutex);
-            _add_event_to_queue (copy, &listener->queue);
-            thread_mutex_unlock (&listener->mutex);
+            _add_stats_to_stats_client (listener, fmt, ap);
         }
+        stats_listeners_send (listener);
+        if (client->con->error || listener->content_len > 10000)
+        {
+            stats_event_t stats_count;
+            char buffer [20];
 
+            *trail = listener->next;
+
+            build_event (&stats_count, NULL, "stats_connections", buffer);
+            stats_count.action = STATS_EVENT_DEC;
+            process_event_unlocked (&stats_count);
+
+            /* moved this listener so that final cleanup can be done outside of lock */
+            listener->next = _stats.listeners_removed;
+            _stats.listeners_removed = listener;
+
+            listener = *trail;
+            continue;
+        }
+        /* reduce queue if unsued */
+        clear_stats_queue (listener);
+
+        trail = &listener->next;
         listener = listener->next;
     }
+    va_end(ap);
 }
 
 void stats_global (ice_config_t *config)
 {
-    stats_event (NULL, "server_id", config->server_id);
-    stats_event (NULL, "host", config->hostname);
+    stats_event_hidden (NULL, "server_id", config->server_id, STATS_GENERAL);
+    stats_event_hidden (NULL, "host", config->hostname, STATS_GENERAL);
     stats_event (NULL, "location", config->location);
     stats_event (NULL, "admin", config->admin);
+#if 0
+    /* restart a master stats connection */
+    config->master = calloc (1, sizeof ice_master_details);
+    config->master->hostname = xmlCharStrdup ("127.0.0.1");
+    config->master->port = 8000;
+    config->master->username = xmlCharStrdup ("relay");
+    config->master->password = xmlCharStrdup ("relayme");
+    _stats.sock = sock_connect_wto_bind (server, port, bind, 10);
+#endif
 }
 
 static void process_event_unlocked (stats_event_t *event)
@@ -637,10 +680,6 @@
         process_global_event (event);
     else
         process_source_event (event);
-
-    /* now we have an event that's been processed into the running stats */
-    /* this event should get copied to event listeners' queues */
-    stats_listener_send (event);
 }
 
 static void process_event (stats_event_t *event)
@@ -652,300 +691,230 @@
     thread_mutex_unlock (&_stats_mutex);
 }
 
-/* you must have the _stats_mutex locked here */
-static void _unregister_listener(event_listener_t *listener)
+
+static int _append_to_bufferv (refbuf_t *refbuf, int max_len, const char *fmt, va_list ap)
 {
-    event_listener_t **prev = (event_listener_t **)&_event_listeners,
-                     *current = *prev;
-    stats_event_t stats_count, *event;
-    char buffer [VAL_BUFSIZE];
+    char *buf = (char*)refbuf->data + refbuf->len;
+    int len = max_len - refbuf->len;
+    int ret;
+    va_list vl;
 
-    while (current)
-    {
-        if (current == listener)
-        {
-            *prev = current->next;
-            break;
-        }
-        prev = &current->next;
-        current = *prev;
-    }
-
-    /* remove this listener before sending this change */
-    build_event (&stats_count, NULL, "stats_connections", buffer);
-    stats_count.action = STATS_EVENT_DEC;
-    process_event_unlocked (&stats_count);
-
-    /* flush any extra that sneaked on at the last moment */
-    while ((event = _get_event_from_queue (&listener->queue)) != NULL)
-        _free_event (event);
+    va_copy (vl, ap);
+    ret = vsnprintf (buf, len, fmt, vl);
+    if (ret < 0 || ret >= len)
+        return -1;
+    refbuf->len += ret;
+    return 0;
 }
 
-
-static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
+static int _append_to_buffer (refbuf_t *refbuf, int max_len, const char *fmt, ...)
 {
-    stats_event_t *event = (stats_event_t *)malloc(sizeof(stats_event_t));
-    
-    if (source != NULL)
-        event->source = (char *)strdup(source);
-    else
-        event->source = NULL;
-    event->name = (char *)strdup(node->name);
-    event->value = (char *)strdup(node->value);
-    event->hidden = node->hidden;
-    event->action = STATS_EVENT_SET;
-    event->next = NULL;
+    int ret;
+    va_list va;
 
-    return event;
+    va_start (va, fmt);
+    ret = _append_to_bufferv (refbuf, max_len, fmt, va);
+    va_end(va);
+    return ret;
 }
 
 
-static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue)
+static void _add_node_to_stats_client (event_listener_t *listener, refbuf_t *refbuf)
 {
-    *queue->tail = event;
-    queue->tail = (volatile stats_event_t **)&event->next;
+    *listener->queue_recent_p = refbuf;
+    listener->queue_recent_p = &refbuf->next;
+    listener->content_len += refbuf->len;
 }
 
 
-static stats_event_t *_get_event_from_queue (event_queue_t *queue)
+static void _add_stats_to_stats_client (event_listener_t *listener,const char *fmt, va_list ap)
 {
-    stats_event_t *event = NULL;
-
-    if (queue && queue->head)
+    unsigned int size = 50;
+    while (size < 300)
     {
-        event = (stats_event_t *)queue->head;
-        queue->head = event->next;
-        if (queue->head == NULL)
-            queue->tail = &queue->head;
-    }
+        refbuf_t *refbuf = refbuf_new (size);
+        refbuf->len = 0;
 
-    return event;
-}
-
-static int _send_event_to_client(stats_event_t *event, client_t *client)
-{
-    int len;
-    char buf [200];
-
-    /* send data to the client!!!! */
-    len = snprintf (buf, sizeof (buf), "EVENT %s %s %s\n",
-            (event->source != NULL) ? event->source : "global",
-            event->name ? event->name : "null",
-            event->value ? event->value : "null");
-    if (len > 0 && len < (int)sizeof (buf))
-    {
-        client_send_bytes (client, buf, len);
-        if (client->con->error)
-            return -1;
+        if (_append_to_bufferv (refbuf, size, fmt, ap) == 0)
+        {
+            _add_node_to_stats_client (listener, refbuf);
+            return;
+        }
+        refbuf_release (refbuf);
+        size += 100;
     }
-    return 0;
 }
 
-void _dump_stats_to_queue (event_queue_t *queue)
+
+static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, const char *show_mount, int hidden)
 {
-    avl_node *node;
-    avl_node *node2;
-    stats_event_t *event;
-    stats_source_t *source;
+    avl_node *avlnode;
+    xmlNodePtr ret = NULL;
 
     thread_mutex_lock(&_stats_mutex);
-    /* first we fill our queue with the current stats */
-    /* start with the global stats */
-    node = avl_get_first(_stats.global_tree);
-    while (node) {
-        event = _make_event_from_node((stats_node_t *)node->key, NULL);
-        _add_event_to_queue(event, queue);
-
-        node = avl_get_next(node);
+    /* general stats first */
+    avlnode = avl_get_first(_stats.global_tree);
+    while (avlnode)
+    {
+        stats_node_t *stat = avlnode->key;
+        if (stat->hidden & hidden)
+            xmlNewTextChild (root, NULL, XMLSTR(stat->name), XMLSTR(stat->value));
+        avlnode = avl_get_next (avlnode);
     }
+    /* now per mount stats */
+    avlnode = avl_get_first(_stats.source_tree);
+    while (avlnode)
+    {
+        stats_source_t *source = (stats_source_t *)avlnode->key;
+        if (source->hidden & hidden &&
+                (show_mount == NULL || strcmp (show_mount, source->source) == 0))
+        {
+            avl_node *avlnode2 = avl_get_first (source->stats_tree);
+            xmlNodePtr xmlnode = xmlNewTextChild (root, NULL, XMLSTR("source"), NULL);
 
-    /* now the stats for each source */
-    node = avl_get_first(_stats.source_tree);
-    while (node) {
-        source = (stats_source_t *)node->key;
-        node2 = avl_get_first(source->stats_tree);
-        while (node2) {
-            event = _make_event_from_node((stats_node_t *)node2->key, source->source);
-            _add_event_to_queue(event, queue);
-
-            node2 = avl_get_next(node2);
+            xmlSetProp (xmlnode, XMLSTR("mount"), XMLSTR(source->source));
+            if (ret == NULL)
+                ret = xmlnode;
+            while (avlnode2)
+            {
+                stats_node_t *stat = avlnode2->key;
+                xmlNewTextChild (xmlnode, NULL, XMLSTR(stat->name), XMLSTR(stat->value));
+                avlnode2 = avl_get_next (avlnode2);
+            }
         }
-        
-        node = avl_get_next(node);
+        avlnode = avl_get_next (avlnode);
     }
     thread_mutex_unlock(&_stats_mutex);
+    return ret;
 }
 
+
 /* factoring out code for stats loops
 ** this function copies all stats to queue, and registers 
-** the queue for all new events atomically.
-** note: mutex must already be created!
 */
 static void _register_listener (event_listener_t *listener)
 {
     avl_node *node;
-    avl_node *node2;
-    stats_event_t *event;
-    stats_source_t *source;
     stats_event_t stats_count;
+    refbuf_t *refbuf;
+    size_t size = 8192;
     char buffer[20];
 
     build_event (&stats_count, NULL, "stats_connections", buffer);
     stats_count.action = STATS_EVENT_INC;
     process_event_unlocked (&stats_count);
 
-    /* first we fill our queue with the current stats */
-    
-    /* start with the global stats */
-    node = avl_get_first(_stats.global_tree);
-    while (node) {
-        event = _make_event_from_node((stats_node_t *)node->key, NULL);
-        _add_event_to_queue (event, &listener->queue);
+    while (size < 50000)    /* use a large limit */
+    {
+        /* first we fill our queue with the current stats */
+        refbuf = refbuf_new (size);
+        refbuf->len = 0;
 
-        node = avl_get_next(node);
-    }
+        /* starts with the http response header */
+        if (_append_to_buffer (refbuf, size, "HTTP/1.0 200 OK\r\ncapability: streamlist\r\n\r\n") < 0)
+        {
+            refbuf_release (refbuf);
+            break;
+        }
+        /* now the global stats */
+        node = avl_get_first(_stats.global_tree);
+        while (node)
+        {
+            stats_node_t *stat = node->key;
 
-    /* now the stats for each source */
-    node = avl_get_first(_stats.source_tree);
-    while (node) {
-        source = (stats_source_t *)node->key;
-        node2 = avl_get_first(source->stats_tree);
-        while (node2) {
-            event = _make_event_from_node((stats_node_t *)node2->key, source->source);
-            _add_event_to_queue (event, &listener->queue);
-
-            node2 = avl_get_next(node2);
+            node = avl_get_next(node);
+            if ((stat->hidden & listener->hidden_level) == 0)
+                continue;
+            if (_append_to_buffer (refbuf, size, "EVENT global %s %s\n", stat->name, stat->value) < 0)
+            {
+                size += 8192;
+                refbuf_release (refbuf);
+                break;
+            }
         }
-        
-        node = avl_get_next(node);
-    }
+        if (node) continue; /* catch buffer full case */
 
-    /* now we register to receive future event notices */
-    listener->next = (event_listener_t *)_event_listeners;
-    _event_listeners = listener;
-}
-
-static void check_uri (event_listener_t *listener, client_t *client)
-{
-    const char *mount = httpp_getvar (client->parser, HTTPP_VAR_URI);
-    if (strcmp (mount, "/") != 0)
-        listener->source = strdup (mount);
-}
-
-
-void *stats_connection(void *arg)
-{
-    client_t *client = (client_t *)arg;
-    stats_event_t *event;
-    event_listener_t listener;
-
-    INFO0 ("stats client starting");
-
-    memset (&listener, 0, sizeof (listener));
-    event_queue_init (&listener.queue);
-    check_uri (&listener, client);
-
-    /* increment the thread count */
-    thread_mutex_lock(&_stats_mutex);
-    _stats_threads++;
-    thread_mutex_create("stats local event", &listener.mutex);
-
-    _register_listener (&listener);
-    thread_mutex_unlock(&_stats_mutex);
-
-    while (_stats_running) {
-        thread_mutex_lock (&listener.mutex);
-        event = _get_event_from_queue (&listener.queue);
-        thread_mutex_unlock (&listener.mutex);
-        if (event != NULL) {
-            if (_send_event_to_client(event, client) < 0) {
-                _free_event(event);
+        /* now the stats for each source */
+        node = avl_get_first(_stats.source_tree);
+        while (node)
+        {
+            avl_node *node2;
+            stats_source_t *snode = (stats_source_t *)node->key;
+            node = avl_get_next(node);
+            if ((snode->hidden & listener->hidden_level) == 0)
+                continue;
+            if (_append_to_buffer (refbuf, size, "NEW %s\n", snode->source) < 0)
+            {
+                size += 8192;
+                refbuf_release (refbuf);
                 break;
             }
-            _free_event(event);
-            continue;
+            node2 = avl_get_first(snode->stats_tree);
+            while (node2)
+            {
+                stats_node_t *stat = node2->key;
+                node2 = avl_get_next(node2);
+
+                if ((stat->hidden & listener->hidden_level) == 0)
+                    continue;
+                if (_append_to_buffer (refbuf, size, "EVENT %s %s %s\n", snode->source, stat->name, stat->value) < 0)
+                {
+                    size += 8192;
+                    refbuf_release (refbuf);
+                    break;
+                }
+            }
         }
-        thread_sleep (500000);
+        if (node) continue; /* catch buffer full case */
+        break;
     }
 
-    thread_mutex_lock(&_stats_mutex);
-    _unregister_listener (&listener);
-    _stats_threads--;
-    thread_mutex_unlock(&_stats_mutex);
+    client_set_queue (listener->client, refbuf);
+    _add_node_to_stats_client (listener, refbuf);
 
-    thread_mutex_destroy (&listener.mutex);
-    free (listener.source);
-    client_destroy (client);
-    INFO0 ("stats client finished");
-
-    return NULL;
+    /* now we register to receive future event notices */
+    listener->next = _stats.event_listeners;
+    _stats.event_listeners = listener;
 }
 
-
-void stats_callback (client_t *client, void *notused)
+static void check_uri (event_listener_t *listener, const char *mount)
 {
-    if (client->con->error)
+    listener->hidden_level = STATS_PUBLIC;
+    if (strncmp (mount, "/admin/", 7) == 0)
     {
-        client_destroy (client);
-        return;
+        if (strcmp (mount+7, "streams") == 0)
+            listener->hidden_level = STATS_SLAVE|STATS_GENERAL;
     }
-    client_set_queue (client, NULL);
-    thread_create("Stats Connection", stats_connection, (void *)client, THREAD_DETACHED);
+    /* else
+     * check for reserved mountpoint, show hidden source stats */
 }
 
 
-typedef struct _source_xml_tag {
-    char *mount;
-    xmlNodePtr node;
 
-    struct _source_xml_tag *next;
-} source_xml_t;
-
-static xmlNodePtr _find_xml_node(const char *mount, source_xml_t **list, xmlNodePtr root)
+void stats_callback (client_t *client, void *arg)
 {
-    source_xml_t *node, *node2;
-    int found = 0;
+    event_listener_t * listener;
+    const char *mount = arg;
 
-    /* search for existing node */
-    node = *list;
-    while (node) {
-        if (strcmp(node->mount, mount) == 0) {
-            found = 1;
-            break;
-        }
-        node = node->next;
-    }
+    client_set_queue (client, NULL);
+    listener = calloc (1, sizeof (event_listener_t));
+    listener->client = client;
+    listener->queue_recent_p = &listener->queue;
+    check_uri (listener, mount);
 
-    if (found) return node->node;
-
-    /* if we didn't find it, we must build it and add it to the list */
-
-    /* build node */
-    node = (source_xml_t *)malloc(sizeof(source_xml_t));
-    node->mount = strdup(mount);
-    node->node = xmlNewChild(root, NULL, XMLSTR("source"), NULL);
-    xmlSetProp(node->node, XMLSTR("mount"), XMLSTR(mount));
-    node->next = NULL;
-
-    /* add node */
-    if (*list == NULL) {
-        *list = node;
-    } else {
-        node2 = *list;
-        while (node2->next) node2 = node2->next;
-        node2->next = node;
-    }
-
-    return node->node;
+    thread_mutex_lock(&_stats_mutex);
+    _register_listener (listener);
+    thread_mutex_unlock(&_stats_mutex);
 }
 
+
 void stats_transform_xslt(client_t *client, const char *uri)
 {
     xmlDocPtr doc;
     char *xslpath = util_get_path_from_normalised_uri (uri);
     const char *mount = httpp_get_query_param (client->parser, "mount");
 
-    stats_get_xml(&doc, 0, mount);
+    stats_get_xml(&doc, STATS_PUBLIC, mount);
 
     xslt_transform(doc, xslpath, client);
 
@@ -953,71 +922,28 @@
     free (xslpath);
 }
 
-void stats_get_xml(xmlDocPtr *doc, int show_hidden, const char *show_mount)
+void stats_get_xml(xmlDocPtr *doc, int hidden, const char *show_mount)
 {
-    stats_event_t *event;
-    event_queue_t queue;
-    xmlNodePtr node, srcnode;
-    source_xml_t *src_nodes = NULL;
-    source_xml_t *next;
+    xmlNodePtr node;
 
-    event_queue_init (&queue);
-    _dump_stats_to_queue (&queue);
-
     *doc = xmlNewDoc (XMLSTR("1.0"));
     node = xmlNewDocNode (*doc, NULL, XMLSTR("icestats"), NULL);
     xmlDocSetRootElement(*doc, node);
 
-    event = _get_event_from_queue(&queue);
-    while (event)
-    {
-        if (event->hidden <= show_hidden)
-        {
-            do
-            {
-                xmlChar *name, *value;
-                if (event->source)
-                {
-                    if (show_mount && strcmp (event->source, show_mount) != 0)
-                        break;
-                    srcnode = _find_xml_node(event->source, &src_nodes, node);
-                }
-                else
-                    srcnode = node;
+    node = _dump_stats_to_doc (node, show_mount, hidden);
 
-                name = xmlEncodeEntitiesReentrant (*doc, XMLSTR(event->name));
-                value = xmlEncodeEntitiesReentrant (*doc, XMLSTR(event->value));
-                xmlNewChild(srcnode, NULL, name, value);
-                xmlFree (value);
-                xmlFree (name);
-            } while (0);
-        }
-
-        _free_event(event);
-        event = _get_event_from_queue(&queue);
-    }
-    if (show_mount)
-    {
+    if (show_mount && node)
+	{
+		source_t *source;
         /* show each listener */
-        source_xml_t *src = src_nodes;
         avl_tree_rlock (global.source_tree);
-        while (src)
-        {
-            source_t *source = source_find_mount_raw (src->mount);
+        source = source_find_mount_raw (show_mount);
 
-            if (source)
-                admin_source_listeners (source, src->node);
+        if (source)
+            admin_source_listeners (source, node);
 
-            src = src->next;
-        }
         avl_tree_unlock (global.source_tree);
     }
-    while (src_nodes) {
-        next = src_nodes->next;
-        free(src_nodes->mount);
-        free(src_nodes);
-        src_nodes = next;
-    }
 }
 
 static int _compare_stats(void *arg, void *a, void *b)
@@ -1056,16 +982,7 @@
     return 1;
 }
 
-static void _free_event(stats_event_t *event)
-{
-    if (event->source) free(event->source);
-    if (event->name) free(event->name);
-    if (event->value) free(event->value);
-    free(event);
-}
 
-
-
 /* return a list of blocks which contain lines of text. Each line is a mountpoint
  * reference that a slave will use for relaying.  The prepend setting is to indicate
  * if some something else needs to be added to each line.
@@ -1149,19 +1066,35 @@
 
 void stats_global_calc (void)
 {
+    event_listener_t *listener;
+    static time_t next_update = 0;
     stats_event_t event;
     char buffer [VAL_BUFSIZE];
-    static time_t next_update = 0;
 
     if (global.time < next_update)
         return;
-    next_update = global.time + 1;
+
+    next_update = global.time + 2;
     build_event (&event, NULL, "outgoing_kbitrate", buffer);
+    event.hidden = STATS_COUNTERS|STATS_HIDDEN;
 
     thread_mutex_lock (&_stats_mutex);
     snprintf (buffer, sizeof(buffer), "%" PRIu64,
-            (int64_t)(global_getrate_avg (global.out_bitrate) * 8 / 1000.0 + 0.5));
+            (int64_t)global_getrate_avg (global.out_bitrate) * 8 / 1000);
     process_event_unlocked (&event);
+    /* retrieve the list of closing down clients */
+    listener = _stats.listeners_removed;
+    _stats.listeners_removed = NULL;
     thread_mutex_unlock (&_stats_mutex);
+
+    /* flush out any closed stats clients */
+    while (listener)
+    {
+        event_listener_t *to_go = listener;
+        listener = listener->next;
+        client_destroy (to_go->client);
+        clear_stats_queue (to_go);
+        free (to_go);
+    }
 }
 

Modified: icecast/branches/kh/icecast/src/stats.h
===================================================================
--- icecast/branches/kh/icecast/src/stats.h	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/stats.h	2008-10-04 01:09:17 UTC (rev 15372)
@@ -21,36 +21,17 @@
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 
+#define STATS_HIDDEN   1
+#define STATS_SLAVE    2
+#define STATS_GENERAL  4
+#define STATS_COUNTERS 8
+#define STATS_PUBLIC   (STATS_GENERAL|STATS_COUNTERS)
+#define STATS_ALL      ~0
 
-typedef struct _stats_tag
-{
-    avl_tree *global_tree;
-
-    /* global stats
-    start_time
-    total_users
-    max_users
-    total_sources
-    max_sources
-    total_user_connections
-    total_source_connections
-    */
-
-    avl_tree *source_tree;
-
-    /* stats by source, and for stats
-    start_time
-    total_users
-    max_users
-    */
-
-} stats_t;
-
 void stats_initialize(void);
 void stats_shutdown(void);
 
 void stats_global(ice_config_t *config);
-stats_t *stats_get_stats(void);
 void stats_get_streamlist (char *buffer, size_t remaining);
 refbuf_t *stats_get_streams (int prepend);
 void stats_clear_virtual_mounts (void);
@@ -63,11 +44,11 @@
 void stats_event_add(const char *source, const char *name, unsigned long value);
 void stats_event_sub(const char *source, const char *name, unsigned long value);
 void stats_event_dec(const char *source, const char *name);
-void stats_event_hidden (const char *source, const char *name, int hidden);
+void stats_event_hidden (const char *source, const char *name, const char *value, int hidden);
 void stats_event_time (const char *mount, const char *name);
 
 void *stats_connection(void *arg);
-void stats_callback (client_t *client, void *notused);
+void stats_callback (client_t *client, void *mount);
 void stats_global_calc(void);
 
 void stats_transform_xslt(client_t *client, const char *uri);

Modified: icecast/branches/kh/icecast/src/util.c
===================================================================
--- icecast/branches/kh/icecast/src/util.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/util.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -47,18 +47,18 @@
 
 struct rate_calc_node
 {
-    time_t time;
+    uint64_t index;
     long value;
-    struct rate_calc_node *next, *prev;
+    struct rate_calc_node *next;
 };
 
 struct rate_calc
 {
     uint64_t total;
-    unsigned int seconds;
-    unsigned int blocks;
-    unsigned int recalc_total;
     struct rate_calc_node *current;
+    unsigned int samples;
+    unsigned int ssec;
+    unsigned int blocks;
 };
 
 
@@ -695,102 +695,90 @@
 /* setup a rate block of so many seconds, so that an average can be
  * determined of that range
  */
-struct rate_calc *rate_setup (unsigned int seconds)
+struct rate_calc *rate_setup (unsigned int samples, unsigned int ssec)
 {
     struct rate_calc *calc = calloc (1, sizeof (struct rate_calc));
-    struct rate_calc_node *start = NULL;
-    unsigned int i;
 
-    if (calc == NULL || seconds == 0)
+    if (calc == NULL || samples < 2 || ssec == 0)
     {
         free (calc);
         return NULL;
     }
-    for (i=0 ; i<seconds; i++)
-    {
-        struct rate_calc_node *node = calloc (1, sizeof (*node));
-        if (calc->current)
-        {
-            calc->current->next = node;
-            node->next = start;
-        }
-        else
-            start = node;
-        node->prev = calc->current;
-        calc->current = node;
-    }
-    calc->current->next = start;
-    start->prev = calc->current;
-    calc->seconds = seconds;
+    calc->samples = samples;
+    calc->ssec = ssec;
     return calc;
 }
 
-/* */
-static void rate_recalc_total (struct rate_calc *calc)
-{
-    int i;
-    struct rate_calc_node *p = calc->current->prev;
-    calc->total = 0;
-    for (i=calc->blocks-1; i; i--)
-    {
-        calc->total += p->value;
-        p = p->prev;
-    }
-}
 
 /* add a value to sampled data, t is used to determine which sample
  * block the sample goes into.
  */
-void rate_add (struct rate_calc *calc, long value, time_t t)
+void rate_add (struct rate_calc *calc, long value, uint64_t sid) 
 {
-    if (t == 0)
+    if (calc->current == NULL || sid != calc->current->index)
     {
-        calc->blocks = 0;
-        return;
-    }
-    if (t != calc->current->time)
-    {
-        if (calc->recalc_total)
+        if (calc->blocks == calc->samples)
         {
-            /* here we keep the number of blocks and recalculate the total */
+            calc->total += calc->current->value;
             calc->current = calc->current->next;
-            rate_recalc_total (calc);
-            calc->recalc_total--;
+            calc->total -= calc->current->value;
+            calc->current->value = value;
+            calc->current->index = sid;
+            return;
         }
         else
         {
-            /* common case */
-            calc->total += calc->current->value;
-            calc->current = calc->current->next;
-            if (calc->blocks == calc->seconds)
-                calc->total -= calc->current->value;
+            struct rate_calc_node *node = calloc (1, sizeof (*node));
+            node->index = sid;
+            calc->blocks++;
+            if (calc->current)
+            {
+                node->next = calc->current->next;
+                calc->current->next = node;
+                calc->total += calc->current->value;
+            }
             else
-                calc->blocks++;
+            {
+                node->next = node;
+            }
+            calc->current = node;
         }
-        calc->current->value = 0;
-        calc->current->time = t;
     }
     calc->current->value += value;
 }
 
-
 /* return the average sample value over all the blocks except the 
  * current one, as that may be incomplete
  */
 long rate_avg (struct rate_calc *calc)
 {
+    uint64_t range;
+
     if (calc == NULL || calc->blocks < 2)
         return 0;
-    return (long)(calc->total / (calc->blocks-1));
+    range = (calc->current->index - calc->current->next->index) / calc->ssec;
+    if (range < 1)
+        range = 1;
+    return (long)(calc->total / range);
 }
 
 /* reduce the samples used to calculate average */
-void rate_reduce (struct rate_calc *calc, unsigned long count)
+void rate_reduce (struct rate_calc *calc, unsigned int count)
 {
-    if (calc && count < calc->blocks && count >= 2)
+    if (calc && count < calc->blocks)
     {
-        calc->recalc_total = count*2;
-        calc->blocks = count;
+        struct rate_calc_node *list = calc->current->next;
+        for (; calc->blocks > count; calc->blocks--)
+        {
+            struct rate_calc_node *to_go = list;
+            list = to_go->next;
+            calc->total -= to_go->value;
+            free (to_go);
+        }
+        if (calc->blocks)
+            calc->current->next = list;
+        else
+            calc->current = NULL;
     }
 }
 

Modified: icecast/branches/kh/icecast/src/util.h
===================================================================
--- icecast/branches/kh/icecast/src/util.h	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/util.h	2008-10-04 01:09:17 UTC (rev 15372)
@@ -58,11 +58,11 @@
 #endif
 char *util_conv_string (const char *string, const char *in_charset, const char *out_charset);
 
-struct rate_calc *rate_setup (unsigned int seconds);
-void rate_add (struct rate_calc *calc, long value, time_t t);
+struct rate_calc *rate_setup (unsigned int samples, unsigned int ssec);
+void rate_add (struct rate_calc *calc, long value, uint64_t t);
 long rate_avg (struct rate_calc *calc);
 void rate_free (struct rate_calc *calc);
-void rate_reduce (struct rate_calc *calc, unsigned long count);
+void rate_reduce (struct rate_calc *calc, unsigned int count);
 
 int get_line(FILE *file, char *buf, size_t siz);
 

Modified: icecast/branches/kh/icecast/src/yp.c
===================================================================
--- icecast/branches/kh/icecast/src/yp.c	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/src/yp.c	2008-10-04 01:09:17 UTC (rev 15372)
@@ -925,12 +925,10 @@
 void yp_touch (const char *mount)
 {
     struct yp_server *server = (struct yp_server *)active_yps;
-    time_t trigger;
     ypdata_t *search_list = NULL;
 
     thread_rwlock_rlock (&yp_lock);
-    /* do update in 3 secs, give stats chance to update */
-    trigger = time(NULL) + 3;
+
     if (server)
         search_list = server->mounts;
 
@@ -945,9 +943,9 @@
                 search_list = yp->next;
                 continue;
             }
-            /* only force if touch */
-            if (yp->process == do_yp_touch)
-                yp->next_update = trigger;
+            /* don't update the directory if there is a touch scheduled soon */
+            if (yp->process == do_yp_touch && now + yp->touch_interval - yp->next_update > 60)
+                yp->next_update = now;
         }
         server = server->next;
         if (server)

Modified: icecast/branches/kh/icecast/web/status.xsl
===================================================================
--- icecast/branches/kh/icecast/web/status.xsl	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/web/status.xsl	2008-10-04 01:09:17 UTC (rev 15372)
@@ -79,6 +79,17 @@
 <xsl:if test="server_url">
 <tr><td>Stream URL:</td><td class="streamdata"> <a target="_blank" href="{server_url}"><xsl:value-of select="server_url" /></a></td></tr>
 </xsl:if>
+<xsl:if test="video_preview">
+<xsl:choose>
+<xsl:when test="authenticator">
+<tr><td>Preview:</td><td class="videopreview"> <a href="auth.xsl"><img src="{video_preview}" border="1" align="left" height="400" width="300" alt="frame preview" title="click to start watching the video!" /></a></td></tr>
+</xsl:when>
+<xsl:otherwise>
+<tr><td>Preview:</td><td class="videopreview"> <a href="{@mount}.m3u"><img src="{video_preview}" border="1" align="left" height="200"  alt="frame preview" title="click to start watching the video!" /></a></td></tr>
+</xsl:otherwise>
+</xsl:choose>
+</xsl:if>
+
 <tr><td>Current Song:</td><td class="streamdata"> 
 <xsl:if test="artist"><xsl:value-of select="artist" /> - </xsl:if><xsl:value-of select="title" /></td></tr>
 </table>

Modified: icecast/branches/kh/icecast/win32/icecast.dsp
===================================================================
--- icecast/branches/kh/icecast/win32/icecast.dsp	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/win32/icecast.dsp	2008-10-04 01:09:17 UTC (rev 15372)
@@ -363,10 +363,6 @@
 # End Source File
 # Begin Source File
 
-SOURCE=..\src\os.h
-# End Source File
-# Begin Source File
-
 SOURCE=..\src\refbuf.h
 # End Source File
 # Begin Source File

Modified: icecast/branches/kh/icecast/win32/icecast2.iss
===================================================================
--- icecast/branches/kh/icecast/win32/icecast2.iss	2008-10-03 23:08:14 UTC (rev 15371)
+++ icecast/branches/kh/icecast/win32/icecast2.iss	2008-10-04 01:09:17 UTC (rev 15372)
@@ -3,7 +3,7 @@
 
 [Setup]
 AppName=Icecast2-KH
-AppVerName=Icecast v2.3.2-kh1
+AppVerName=Icecast v2.3.2-kh2
 AppPublisherURL=http://www.icecast.org
 AppSupportURL=http://www.icecast.org
 AppUpdatesURL=http://www.icecast.org
@@ -13,7 +13,7 @@
 LicenseFile=..\COPYING
 InfoAfterFile=..\README
 OutputDir=.
-OutputBaseFilename=icecast2_win32_v2.3.2-kh1_setup
+OutputBaseFilename=icecast2_win32_v2.3.2-kh2_setup
 WizardImageFile=icecast2logo2.bmp
 WizardImageStretch=no
 ; uncomment the following line if you want your installation to run on NT 3.51 too.



More information about the commits mailing list