[xiph-commits] r17267 - in icecast/branches/kh/icecast: . src win32

karl at svn.xiph.org karl at svn.xiph.org
Tue Jun 1 18:42:37 PDT 2010


Author: karl
Date: 2010-06-01 18:42:37 -0700 (Tue, 01 Jun 2010)
New Revision: 17267

Added:
   icecast/branches/kh/icecast/src/flv.c
   icecast/branches/kh/icecast/src/flv.h
Modified:
   icecast/branches/kh/icecast/NEWS
   icecast/branches/kh/icecast/config.h.vc6
   icecast/branches/kh/icecast/configure.in
   icecast/branches/kh/icecast/src/Makefile.am
   icecast/branches/kh/icecast/src/admin.c
   icecast/branches/kh/icecast/src/auth.c
   icecast/branches/kh/icecast/src/auth_url.c
   icecast/branches/kh/icecast/src/cfgfile.c
   icecast/branches/kh/icecast/src/cfgfile.h
   icecast/branches/kh/icecast/src/client.h
   icecast/branches/kh/icecast/src/connection.c
   icecast/branches/kh/icecast/src/connection.h
   icecast/branches/kh/icecast/src/format_mp3.c
   icecast/branches/kh/icecast/src/format_mp3.h
   icecast/branches/kh/icecast/src/format_ogg.c
   icecast/branches/kh/icecast/src/fserve.c
   icecast/branches/kh/icecast/src/mpeg.c
   icecast/branches/kh/icecast/src/mpeg.h
   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/win32/Icecast2win.clw
   icecast/branches/kh/icecast/win32/icecast.dsp
   icecast/branches/kh/icecast/win32/icecast2.iss
Log:
bump to kh24. Get things checked in. The flv wrapping has had some time to stablize
and looks to be working well. the rest are various fixes that have accumulated
from bug reports.


Modified: icecast/branches/kh/icecast/NEWS
===================================================================
--- icecast/branches/kh/icecast/NEWS	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/NEWS	2010-06-02 01:42:37 UTC (rev 17267)
@@ -16,6 +16,19 @@
 any extra tags are show in the conf/icecast.xml.dist file
 
 
+2.3.2-kh24
+. Allow for per-listener wrapping of existing mp3/aac streams when requested with
+  a type=.flv as a query arg. The actual trigger could be anything we want but this
+  allows for using the existing mountpoint, with easy mapping for URL auth and for
+  matching an extension in the player. Works with intro and fallback handling.
+. mpeg parser is more complete, handles more cases, avoiding bad input cases
+. fix possible bad pointer in ogg headers case.
+. fix for shoutcast-style metadata writes being truncated.
+. fixes for non-ogg buffer alignment causing resync or stalled streams.
+. prevent a failed stream list request from dropping current relays
+. handle relay startup shutdown better for memory usage and listeners 
+. use Host header if available for buildm3u
+
 2.3.2-kh23
 . if an inactive on-demand relay detects listeners on fallback then start relay
   but do not move listeners until the connection is complete.

Modified: icecast/branches/kh/icecast/config.h.vc6
===================================================================
--- icecast/branches/kh/icecast/config.h.vc6	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/config.h.vc6	2010-06-02 01:42:37 UTC (rev 17267)
@@ -95,7 +95,7 @@
 #define PACKAGE_NAME "Icecast"
 
 /* Version number of package */
-#define VERSION "2.3.2-kh23"
+#define VERSION "2.3.2-kh24"
 
 /* Define to the version of this package. */
 #define PACKAGE_VERSION VERSION

Modified: icecast/branches/kh/icecast/configure.in
===================================================================
--- icecast/branches/kh/icecast/configure.in	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/configure.in	2010-06-02 01:42:37 UTC (rev 17267)
@@ -1,4 +1,4 @@
-AC_INIT([Icecast], [2.3.2-kh23], [karl at xiph.org])
+AC_INIT([Icecast], [2.3.2-kh24], [karl at xiph.org])
 
 LT_INIT
 AC_PREREQ(2.59)

Modified: icecast/branches/kh/icecast/src/Makefile.am
===================================================================
--- icecast/branches/kh/icecast/src/Makefile.am	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/Makefile.am	2010-06-02 01:42:37 UTC (rev 17267)
@@ -13,12 +13,12 @@
     fnmatch_loop.c fnmatch.h \
     format.h format_ogg.h format_mp3.h \
     format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h \
-    format_kate.h format_skeleton.h mpeg.h
+    format_kate.h format_skeleton.h mpeg.h flv.h
 icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c \
     util.c slave.c source.c stats.c refbuf.c client.c \
     xslt.c fserve.c event.c admin.c md5.c \
     format.c format_ogg.c format_mp3.c format_midi.c format_flac.c \
-    auth.c auth_htpasswd.c format_kate.c format_skeleton.c mpeg.c
+    auth.c auth_htpasswd.c format_kate.c format_skeleton.c mpeg.c flv.c
 EXTRA_icecast_SOURCES = yp.c \
     auth_url.c auth_cmd.c \
     format_vorbis.c format_theora.c format_speex.c fnmatch.c

Modified: icecast/branches/kh/icecast/src/admin.c
===================================================================
--- icecast/branches/kh/icecast/src/admin.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/admin.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -556,7 +556,7 @@
     char str [50];
 
     xmlNewChild (relaynode, NULL, XMLSTR("localmount"), XMLSTR(relay->localmount));
-    snprintf (str, sizeof (str), "%d", relay->enable);
+    snprintf (str, sizeof (str), "%d", relay->running);
     xmlNewChild (relaynode, NULL, XMLSTR("enable"), XMLSTR(str));
     snprintf (str, sizeof (str), "%d", relay->on_demand);
     xmlNewChild (relaynode, NULL, XMLSTR("on_demand"), XMLSTR(str));
@@ -612,14 +612,8 @@
     msg = "no such relay";
     if (relay)
     {
-        relay->enable = atoi (enable);
+        relay->running = atoi (enable) ? 1 : 0;
         msg = "relay has been changed";
-        if (relay->enable == 0)
-        {
-            if (relay->source && source_running (relay->source) == 0)
-                relay->source->flags &= ~SOURCE_ON_DEMAND;
-        }
-        slave_update_all_mounts();
     }
     thread_mutex_unlock (&(config_locks()->relay_lock));
 
@@ -802,6 +796,7 @@
     const char *username = NULL;
     const char *password = NULL;
     ice_config_t *config;
+    const char *host = httpp_getvar (client->parser, "host");
 
     if (COMMAND_REQUIRE(client, "username", username) < 0 ||
             COMMAND_REQUIRE(client, "password", password) < 0)
@@ -809,17 +804,29 @@
 
     client->respcode = 200;
     config = config_get_config();
-    snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
-        "HTTP/1.0 200 OK\r\n"
-        "Content-Type: audio/x-mpegurl\r\n"
-        "Content-Disposition = attachment; filename=listen.m3u\r\n\r\n" 
-        "http://%s:%s@%s:%d%s\r\n",
-        username,
-        password,
-        config->hostname,
-        config->port,
-        mount
-    );
+    if (host)
+    {
+        char port[10] = "";
+        if (strchr (host, ':') == NULL)
+            snprintf (port, sizeof (port), ":%u",  config->port);
+        snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
+                "HTTP/1.0 200 OK\r\n"
+                "Content-Type: audio/x-mpegurl\r\n"
+                "Content-Disposition = attachment; filename=listen.m3u\r\n\r\n" 
+                "http://%s:%s@%s%s%s\r\n",
+                username, password,
+                host, port, mount);
+    }
+    else
+    {
+        snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
+                "HTTP/1.0 200 OK\r\n"
+                "Content-Type: audio/x-mpegurl\r\n"
+                "Content-Disposition = attachment; filename=listen.m3u\r\n\r\n" 
+                "http://%s:%s@%s:%d%s\r\n",
+                username, password,
+                config->hostname, config->port, mount);
+    }
     config_release_config();
 
     client->refbuf->len = strlen (client->refbuf->data);

Modified: icecast/branches/kh/icecast/src/auth.c
===================================================================
--- icecast/branches/kh/icecast/src/auth.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/auth.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -385,6 +385,7 @@
         {
             avl_tree_unlock (global.source_tree);
             source_setup_listener (source, client);
+            client->flags |= CLIENT_HAS_MOVED;
             thread_mutex_unlock (&source->lock);
             return 0;
         }

Modified: icecast/branches/kh/icecast/src/auth_url.c
===================================================================
--- icecast/branches/kh/icecast/src/auth_url.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/auth_url.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -85,6 +85,7 @@
 #include "client.h"
 #include "cfgfile.h"
 #include "httpp/httpp.h"
+#include "mpeg.h"
 
 #include "logging.h"
 #define CATMODULE "auth_url"
@@ -116,6 +117,8 @@
 
 struct build_intro_contents
 {
+    format_type_t type;
+    mpeg_sync sync;
     refbuf_t *head, **tailp;
 };
 
@@ -230,6 +233,17 @@
                 auth_user->mount = mount;
             }
         }
+        if (strncasecmp (ptr, "content-type: ", 14) == 0)
+        {
+            format_type_t type = format_get_type ((char*)ptr+14);
+
+            if (client->refbuf && (type == FORMAT_TYPE_AAC || type == FORMAT_TYPE_MPEG))
+            {
+                struct build_intro_contents *x = (void*)client->refbuf->data;
+                x->type = type;
+                mpeg_setup (&x->sync, httpp_getvar (client->parser, HTTPP_VAR_URI));
+            }
+        }
     }
 
     return (int)bytes;
@@ -251,6 +265,24 @@
 
         n = refbuf_new (bytes);
         memcpy (n->data, ptr, bytes);
+        if (x->type)
+        {
+            int unprocessed = mpeg_complete_frames (&x->sync, n, 0);
+            if (unprocessed < 0)
+            {
+                mpeg_data_insert (&x->sync, n); /* maybe short read, less than frame */
+                return (int)(bytes);
+            }
+            if (unprocessed > 0)
+            {
+                refbuf_t *next = refbuf_new (unprocessed);
+                memcpy (next->data, n->data + n->len, unprocessed);
+                next->len = unprocessed;
+                mpeg_data_insert (&x->sync, next);
+            }
+            if (n->data[0] != (char)0xFF)
+                WARN0 ("unexpected, no frame marker for buffer");
+        }
         *x->tailp = n;
         x->tailp = &n->next;
     }
@@ -421,6 +453,7 @@
     atd->errormsg[0] = '\0';
     /* setup in case intro data is returned */
     x = (void *)client->refbuf->data;
+    x->type = 0;
     x->head = NULL;
     x->tailp = &x->head;
 
@@ -463,6 +496,8 @@
         n->next = NULL;
         refbuf_release (n);
     }
+    if (x->type)
+        mpeg_cleanup (&x->sync);
     auth_user->client = NULL;
     if (atoi (atd->errormsg) == 403)
         client_send_403 (client, atd->errormsg+4);

Modified: icecast/branches/kh/icecast/src/cfgfile.c
===================================================================
--- icecast/branches/kh/icecast/src/cfgfile.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/cfgfile.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -915,12 +915,12 @@
                             config_get_bool,    &relay->mp3metadata },
         { "username",       config_get_str,     &relay->username },
         { "password",       config_get_str,     &relay->password },
-        { "enable",         config_get_bool,    &relay->enable },
+        { "enable",         config_get_bool,    &relay->running },
         { NULL, NULL, NULL },
     };
 
     relay->mp3metadata = 1;
-    relay->enable = 1;
+    relay->running = 1;
     relay->interval = config->master_update_interval;
     relay->on_demand = config->on_demand;
     relay->masters = master;

Modified: icecast/branches/kh/icecast/src/cfgfile.h
===================================================================
--- icecast/branches/kh/icecast/src/cfgfile.h	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/cfgfile.h	2010-06-02 01:42:37 UTC (rev 17267)
@@ -188,7 +188,6 @@
     int on_demand;
     int running;
     int cleanup;
-    int enable;
     time_t start;
     struct _relay_server *next;
 } relay_server;

Modified: icecast/branches/kh/icecast/src/client.h
===================================================================
--- icecast/branches/kh/icecast/src/client.h	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/client.h	2010-06-02 01:42:37 UTC (rev 17267)
@@ -144,6 +144,7 @@
 #define CLIENT_NO_CONTENT_LENGTH    (020)
 #define CLIENT_HAS_INTRO_CONTENT    (040)
 #define CLIENT_SKIP_ACCESSLOG       (0100)
+#define CLIENT_HAS_MOVED            (0200)
 #define CLIENT_FORMAT_BIT           (01000)
 
 #endif  /* __CLIENT_H__ */

Modified: icecast/branches/kh/icecast/src/connection.c
===================================================================
--- icecast/branches/kh/icecast/src/connection.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/connection.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -499,11 +499,11 @@
         socklen_t slen = sizeof (sa);
 
         con->con_time = time(NULL);
+        con->sock = sock;
+        if (sock == SOCK_ERROR)
+            return -1;
         con->id = _next_connection_id();
         con->discon_time = con->con_time + header_timeout;
-        con->sock = sock;
-        if (sock == SOCK_ERROR)
-            return 0;
         if (addr)
         {
             con->ip = strdup (addr);
@@ -1018,10 +1018,10 @@
 /* Called when activating a source. Verifies that the source count is not
  * exceeded and applies any initial parameters.
  */
-int connection_complete_source (source_t *source, http_parser_t *parser)
+int connection_complete_source (source_t *source)
 {
     client_t *client = source->client;
-    const char *contenttype = httpp_getvar (parser, "content-type");
+    const char *contenttype = httpp_getvar (client->parser, "content-type");
     mount_proxy *mountinfo;
     format_type_t format_type;
     ice_config_t *config;
@@ -1044,9 +1044,9 @@
         format_type = FORMAT_TYPE_GENERIC;
     }
 
+    format_plugin_clear (source->format, client);
     source->format->type = format_type;
     source->format->mount = source->mount;
-    source->format->parser = parser;
     if (format_get_plugin (source->format, client) < 0)
     {
         WARN1 ("plugin format failed for \"%s\"", source->mount);
@@ -1245,11 +1245,20 @@
 {
     char *pattern = config->access_log.exclude_ext;
     char *extension = strrchr (uri, '.');
+    const char *type = httpp_get_query_param (client->parser, "type");
 
-    if (extension == NULL || pattern == NULL)
+    if ((extension && strcmp (extension+1, "flv") == 0) || 
+        (type && strcmp (type, ".flv") == 0))
+    {
+        client->flags |= CLIENT_WANTS_FLV;
+        DEBUG0 ("listener has flv extension so flag it for special handling");
+    }
+    if (extension == NULL || uri == NULL)
         return;
 
     extension++;
+    if (pattern == NULL)
+        return;
     while (*pattern)
     {
         int len = strcspn (pattern, " ");

Modified: icecast/branches/kh/icecast/src/connection.h
===================================================================
--- icecast/branches/kh/icecast/src/connection.h	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/connection.h	2010-06-02 01:42:37 UTC (rev 17267)
@@ -58,7 +58,7 @@
 int  connection_setup_sockets (struct ice_config_tag *config);
 void connection_close(connection_t *con);
 int  connection_init (connection_t *con, sock_t sock, const char *addr);
-int  connection_complete_source (struct source_tag *source, http_parser_t *parser);
+int  connection_complete_source (struct source_tag *source);
 void connection_uses_ssl (connection_t *con);
 void connection_add_banned_ip (const char *ip, int duration);
 void connection_stats (void);

Added: icecast/branches/kh/icecast/src/flv.c
===================================================================
--- icecast/branches/kh/icecast/src/flv.c	                        (rev 0)
+++ icecast/branches/kh/icecast/src/flv.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -0,0 +1,358 @@
+/* -*- c-basic-offset: 4; -*- */
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2009-2010,     Karl Heyes <karl at xiph.org>
+ */
+
+/* flv.c
+ *
+ * routines for processing an flv container
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+
+#include "logging.h"
+#include "mpeg.h"
+#include "format_mp3.h"
+
+#define CATMODULE "flv"
+
+
+struct flv
+{
+    int prev_tagsize;
+    int block_pos;
+    uint64_t prev_ms;
+    int64_t samples;
+    refbuf_t *seen_metadata;
+    mpeg_sync mpeg_sync;
+    unsigned char tag[30];
+};
+
+struct flvmeta
+{
+    int meta_pos;
+    int arraylen;
+};
+
+#define FLVHEADER       11
+
+
+static void flv_hdr (struct flv *flv, unsigned int len)
+{
+    unsigned long v = flv->prev_tagsize;
+
+    v = flv->prev_tagsize;
+    flv->tag [3] = v & 0xFF;
+    v >>= 8;
+    flv->tag [2] = v & 0xFF;
+    v >>= 8;
+    flv->tag [1] = v & 0xFF; // assume less than 2^24 
+
+    v = len;
+    flv->tag [7] = v & 0xFF;
+    v >>= 8;
+    flv->tag [6] = v & 0xFF;
+    v >>= 8;
+    flv->tag [5] = v & 0xFF;
+
+    v = flv->prev_ms;
+    flv->tag [10] = v & 0xFF;
+    v >>= 8;
+    flv->tag [9] = v & 0xFF;
+    v >>= 8;
+    flv->tag [8] = v & 0xFF;
+    v >>= 8;
+    flv->tag [11] = v & 0xFF;
+
+}
+
+/* Here we append to the scratch buffer each mp3 type frame. This frame includes the
+ * header so with the flv header as well it becomes fairly wasteful but that is what
+ * works.
+ */
+static int flv_mpX_hdr (struct mpeg_sync *mp, unsigned char *frame, unsigned int len)
+{
+    struct flv *flv = mp->callback_key;
+
+    if (mp->raw_offset + len + 16 > mp->raw->len)
+        return -1;
+    flv_hdr (flv, len + 1);
+    if (flv->tag[15] == 0x22)
+    {
+        /* it is unclear what the flv flags are for other samplerates */
+        switch (mp->samplerate)
+        {
+            case 11025: flv->tag[15] |= (1<<2); break;
+            case 22050: flv->tag[15] |= (2<<2); break;
+            default:    flv->tag[15] |= (3<<2); break;
+        }
+        if (mp->channels == 2)
+            flv->tag[15] |= 0x1;
+    }
+    memcpy (mp->raw->data + mp->raw_offset, &flv->tag[0], 16);
+    flv->samples += mp->sample_count;
+    flv->prev_ms = (flv->samples / (mp->samplerate/1000.0)) + 0.5;
+    // The extra byte is for the flv audio id, usually 0x2F 
+    flv->prev_tagsize = (len + FLVHEADER + 1);
+    mp->raw_offset += 16;
+    return 0;
+}
+
+/* Here we append to the scratch buffer each aac headerless frame. The flv tag data size
+ * will be 2 bytes more than the frame for codes 0xAF and 0x1
+ */
+static int flv_aac_hdr (struct mpeg_sync *mp, unsigned char *frame, unsigned int len)
+{
+    struct flv *flv = mp->callback_key;
+
+    if (mp->raw_offset + len + 17 > mp->raw->len)
+        return -1;
+    flv_hdr (flv, len + 2);
+    // a single frame (headerless) follows this
+    memcpy (mp->raw->data + mp->raw_offset, &flv->tag[0], 17);
+    flv->samples += mp->sample_count;
+    flv->prev_ms = (flv->samples / (mp->samplerate/1000.0)) + 0.5;
+    // frame length + FLVHEADER + AVHEADER
+    flv->prev_tagsize = (len + 11 + 2);
+    mp->raw_offset += 17;
+    return 0;
+}
+
+
+/* Simple function for generating codes for commonly used streams
+ * players require this before they start playback. This is typically 2 bytes but can be more
+ * 5 bits - codec details, we assume AAC LC here
+ * 4 bits - samplerate table index.
+ * 4 bits - channels table index.
+ *
+ */
+static int  audio_specific_config (mpeg_sync *mp, unsigned char *p)
+{
+    int rates [] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0 };
+    unsigned char count = 0;
+    for (count = 0; rates[count] != mp->samplerate; count++)
+    {
+        if (rates [count] == 0)
+            return 0;
+    }
+    p[0] = ((2 << 3) | (count >> 1));
+    p[1] = ((unsigned char)(count << 7) | (mp->channels << 3));
+    return 2;
+}
+
+static int flv_aac_firsthdr (struct mpeg_sync *mp, unsigned char *frame, unsigned int len)
+{
+    struct flv *flv = mp->callback_key;
+    int c = audio_specific_config (&flv->mpeg_sync, &flv->tag[17]);
+
+    flv_hdr (flv, 2+c);
+    flv->tag[15] = 0xAF; // AAC audio, need these codes first
+    flv->tag[16] = 0x0;
+    memcpy (mp->raw->data, &flv->tag[0], 11+4+2+c);
+    mp->raw_offset = 11+4+2+c;
+    flv->prev_tagsize = 11 + 2 + c;
+    flv->tag[16] = 0x01;   // as per spec. headerless frame follows this
+    flv->mpeg_sync.frame_callback = flv_aac_hdr;
+    // DEBUG2 ("codes for audiospecificconfig are %x, %x", flv->tag[17], flv->tag[18]);
+
+    return flv_aac_hdr (mp, frame, len);
+}
+
+
+static int send_flv_buffer (client_t *client, struct flv *flv)
+{
+    int ret;
+    char *buf = flv->mpeg_sync.raw->data + flv->block_pos;
+    unsigned int len  = flv->mpeg_sync.raw_offset - flv->block_pos;
+
+    if (len <= 0)
+        return 0;
+    ret = client_send_bytes (client, buf, len);
+    if (ret < (int)len)
+        client->schedule_ms += 300;
+    if (ret > 0)
+        flv->block_pos += ret;
+    if (flv->block_pos == flv->mpeg_sync.raw_offset)
+        flv->block_pos = flv->mpeg_sync.raw_offset = 0;
+    return ret;
+}
+
+
+int write_flv_buf_to_client (client_t *client) 
+{
+    refbuf_t *ref = client->refbuf, *scmeta = ref->associated;
+    mp3_client_data *client_mp3 = client->format_data;
+    struct flv *flv = client_mp3->specific;
+    int unprocessed, ret = -1;
+
+    if (flv->block_pos)
+        return send_flv_buffer (client, flv);
+
+    /* check for metadata updates and insert if needed */
+    if (flv->seen_metadata != scmeta)
+    {
+        /* the first assoc block is shoutcast meta, second is flv meta */
+        refbuf_t *flvmeta = scmeta->associated;
+        int len;
+        char *src, *dst = flv->mpeg_sync.raw->data;
+
+        if (flvmeta)
+        {
+            struct flvmeta *flvm = (struct flvmeta *)flvmeta->data;
+
+            src = (char *)flvm + sizeof (*flvm);
+            len  = flvm->meta_pos - sizeof (*flvm);
+        }
+        else
+        {
+            src="\002\000\012onMetaData"    // 13
+                "\010\000\000\000\001"      //  5
+                "\000\005title"             //  7
+                "\002\000\001 "             //  4
+                "\000\000\011";             //  3
+            len = 32;
+        }
+
+        if (len + 15 < flv->mpeg_sync.raw->len)
+        {
+            unsigned char prev_type = flv->tag[4];
+            flv->tag[4] = 18;    // metadata
+            flv_hdr (flv, len);
+            memcpy (dst, &flv->tag[0], 15);
+            memcpy (dst+15, src, len);
+            flv->mpeg_sync.raw_offset = len + 15;
+            flv->prev_tagsize = len + 11;
+            flv->tag[4] = prev_type;
+            flv->seen_metadata = ref->associated;
+            return send_flv_buffer (client, flv);
+        }
+        flv->seen_metadata = ref->associated;
+    }
+
+    unprocessed = mpeg_complete_frames (&flv->mpeg_sync, ref, client->pos);
+    client->pos = ref->len;
+    client->queue_pos += ref->len;
+    if (unprocessed >= 0)
+    {
+        ret = send_flv_buffer (client, flv);
+        if (unprocessed > 0)
+            ref->len += unprocessed;   /* output was truncated, so revert changes */
+    }
+    return ret;
+}
+
+
+void flv_write_UI16 (unsigned char *p, unsigned val)
+{
+    p[1] = val & 0xFF;
+    val >>= 8;
+    p[0] = val & 0xFF;
+}
+
+
+refbuf_t *flv_meta_allocate (size_t len)
+{
+    char *ptr;
+    refbuf_t *buffer = refbuf_new (sizeof (struct flvmeta) + len + 30);
+    struct flvmeta *flvm = (struct flvmeta *)buffer->data;
+    memset (flvm, 0, sizeof (struct flvmeta));
+    ptr = buffer->data + sizeof (struct flvmeta);
+    memcpy (ptr, "\002\000\012onMetaData", 13);
+    memcpy (ptr+13, "\010\000\000\000\000", 5);
+    flvm->meta_pos = sizeof (struct flvmeta) + 18;
+    return buffer;
+}
+
+
+void flv_meta_append (refbuf_t *buffer, const char *tag, const char *value)
+{
+    struct flvmeta *flvm = (struct flvmeta *)buffer->data;
+    int taglen = 0, valuelen = 0;
+    unsigned char *ptr, *array_sizing;
+
+    if (tag)   taglen = strlen (tag);
+    if (value) valuelen = strlen (value);
+
+    if (taglen + valuelen + 5 + flvm->meta_pos > buffer->len - 3)
+        tag = NULL; // force end of the metadata
+    if (tag == NULL)
+    {
+        memcpy (buffer->data+flvm->meta_pos, "\000\000\011", 3);
+        flvm->meta_pos += 3;
+        return;
+    }
+    array_sizing = (unsigned char *)buffer->data + sizeof (*flvm) +16; // 64k elements enough?
+    ptr = (unsigned char *)buffer->data + flvm->meta_pos;
+
+    flv_write_UI16 (ptr, taglen);
+    memcpy (ptr+2, tag, taglen);
+    ptr += (taglen + 2);
+    *ptr = 0x02; // a text string
+    ptr++;
+    flv_write_UI16 (ptr, valuelen);
+    memcpy (ptr+2, value, valuelen);
+    ptr += (valuelen + 2);
+    flvm->arraylen++;
+    flv_write_UI16 (array_sizing, flvm->arraylen); // over 64k tags not handled
+    flvm->meta_pos = (char*)ptr - buffer->data;
+}
+
+
+void flv_create_client_data (format_plugin_t *plugin, client_t *client)
+{
+    mp3_client_data *client_mp3 = client->format_data;
+    struct flv *flv = calloc (1, sizeof (struct flv));
+    int bytes;
+    char *ptr = client->refbuf->data;
+
+    mpeg_setup (&flv->mpeg_sync, plugin->mount);
+    client_mp3->specific = flv;
+
+    bytes = snprintf (ptr, 200, "HTTP/1.0 200 OK\r\n"
+            "content-type: video/x-flv\r\n"
+            "Cache-Control: no-cache\r\n"
+            "Pragma: no-cache\r\n"
+            "\r\n"
+            "FLV\x1\x4%c%c%c\x9", 0,0,0);
+
+    flv->mpeg_sync.raw = refbuf_new (4096);
+    flv->tag[4] = 8;    // Audio details only
+    if (plugin->type == FORMAT_TYPE_AAC)
+    {
+        flv->mpeg_sync.frame_callback = flv_aac_firsthdr;
+    }
+    if (plugin->type == FORMAT_TYPE_MPEG)
+    {
+        flv->tag[15] = 0x22; // MP3, specific settings not available yet
+        flv->mpeg_sync.frame_callback = flv_mpX_hdr;
+    }
+    flv->mpeg_sync.callback_key = flv;
+
+    client->respcode = 200;
+    client->refbuf->len = bytes;
+}
+
+
+void free_flv_client_data (struct flv *flv)
+{
+    mpeg_cleanup (&flv->mpeg_sync);
+}
+

Added: icecast/branches/kh/icecast/src/flv.h
===================================================================
--- icecast/branches/kh/icecast/src/flv.h	                        (rev 0)
+++ icecast/branches/kh/icecast/src/flv.h	2010-06-02 01:42:37 UTC (rev 17267)
@@ -0,0 +1,36 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2009-2010,     Karl Heyes <karl at xiph.org>
+ */
+
+/* flv.c
+ *
+ * routines for processing an flv container
+ *
+ */
+
+#include "format.h"
+#include "client.h"
+#include "mpeg.h"
+
+
+struct flv
+{
+    int prev_tagsize;
+    int block_pos;
+    uint64_t prev_ms;
+    uint64_t samples;
+    mpeg_sync mpeg_sync;
+    unsigned char tag[20];
+};
+
+
+int  write_flv_buf_to_client (client_t *client);
+void flv_create_client_data (format_plugin_t *plugin, client_t *client);
+void free_flv_client_data (struct flv *flv);
+
+refbuf_t *flv_meta_allocate (size_t len);
+void flv_meta_append (refbuf_t *buffer, const char *tag, const char *value);

Modified: icecast/branches/kh/icecast/src/format_mp3.c
===================================================================
--- icecast/branches/kh/icecast/src/format_mp3.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/format_mp3.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -39,6 +39,7 @@
 #include "logging.h"
 
 #include "format_mp3.h"
+#include "flv.h"
 #include "mpeg.h"
 
 #define CATMODULE "format-mp3"
@@ -55,23 +56,18 @@
 static int  format_mp3_create_client_data (format_plugin_t *plugin, client_t *client);
 static void free_mp3_client_data (client_t *client);
 static int  format_mp3_write_buf_to_client(client_t *client);
+static int  write_mpeg_buf_to_client (client_t *client);
 static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf);
 static void mp3_set_tag (format_plugin_t *plugin, const char *tag, const char *in_value, const char *charset);
 static void format_mp3_apply_settings (format_plugin_t *format, mount_proxy *mount);
 
 
-typedef struct {
-   refbuf_t *associated;
-   unsigned short interval;
-   short metadata_offset;
-   unsigned short since_meta_block;
-} mp3_client_data;
 
 /* client format flags */
 #define CLIENT_IN_METADATA          CLIENT_FORMAT_BIT
 #define CLIENT_USING_BLANK_META     (CLIENT_FORMAT_BIT<<1)
 
-static refbuf_t blank_meta = { 17, 1, "\001StreamTitle='';", NULL, NULL, 0 };
+static refbuf_t blank_meta = { 0, 1, NULL, NULL, "\001StreamTitle='';", 17 };
 
 
 int format_mp3_get_plugin (format_plugin_t *plugin, client_t *client)
@@ -121,7 +117,8 @@
         if (plugin->type == FORMAT_TYPE_AAC || plugin->type == FORMAT_TYPE_MPEG)
         {
             client->format_data = malloc (sizeof (mpeg_sync));
-            mpeg_setup (client->format_data);
+            mpeg_setup (client->format_data, plugin->mount);
+            plugin->write_buf_to_client = write_mpeg_buf_to_client;
         }
     }
 
@@ -208,7 +205,7 @@
     source_mp3->interval = -1;
     free (format->charset);
     format->charset = NULL;
-    source_mp3->queue_block_size = 1400;
+    source_mp3->queue_block_size = 2900;
 
     if (mount)
     {
@@ -281,17 +278,23 @@
     p = refbuf_new (size);
     if (p)
     {
+        refbuf_t *flvmeta = flv_meta_allocate (1000);
         mp3_state *source_mp3 = source->format->_state;
         int r;
         char *title;
 
         memset (p->data, '\0', size);
+        p->associated = flvmeta;
         if (source_mp3->url_artist && source_mp3->url_title)
+        {
             r = snprintf (p->data, size, "%c%s%s - %s';", len_byte, streamtitle,
                     source_mp3->url_artist, source_mp3->url_title);
+            flv_meta_append (flvmeta, "artist", source_mp3->url_artist);
+        }
         else
             r = snprintf (p->data, size, "%c%s%s';", len_byte, streamtitle,
                     source_mp3->url_title);
+        flv_meta_append (flvmeta, "title", source_mp3->url_title);
         if (r > 0)
         {
             if (source_mp3->inline_url)
@@ -303,7 +306,10 @@
                     snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->inline_url+11);
             }
             else if (source_mp3->url)
+            {
                 snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->url);
+                flv_meta_append (flvmeta, "URL", source_mp3->url);
+            }
         }
         DEBUG1 ("shoutcast metadata block setup with %s", p->data+1);
         title = filter_shoutcast_metadata (source, p->data, size);
@@ -311,6 +317,7 @@
         yp_touch (source->mount);
         free (title);
 
+        flv_meta_append (flvmeta, NULL, NULL);
         refbuf_release (source_mp3->metadata);
         source_mp3->metadata = p;
     }
@@ -445,12 +452,10 @@
                 ret = send_stream_metadata (client, refbuf, remaining);
                 if (client->flags & CLIENT_IN_METADATA)
                     break;
-                written += ret;
                 buf += remaining;
                 len -= remaining;
-                /* limit how much mp3 we send if using small intervals */
-                if (len > client_mp3->interval)
-                    len = client_mp3->interval;
+                if (ret <= (int)remaining || len > client_mp3->interval)
+                    break;
             }
         }
         /* write any mp3, maybe after the metadata block */
@@ -478,6 +483,15 @@
     return written == 0 ? -1 : written;
 }
 
+
+static int write_mpeg_buf_to_client (client_t *client) 
+{
+    if (client->flags & CLIENT_WANTS_FLV)
+        return write_flv_buf_to_client (client);
+    return format_mp3_write_buf_to_client (client);
+}
+
+
 static void format_mp3_free_plugin (format_plugin_t *plugin, client_t *client)
 {
     /* free the plugin instance */
@@ -506,11 +520,8 @@
  */
 static int complete_read (source_t *source)
 {
-    int bytes = 0;
     format_plugin_t *format = source->format;
     mp3_state *source_mp3 = format->_state;
-    char *buf;
-    refbuf_t *refbuf;
     client_t *client = source->client;
 
     if (source_mp3->read_data == NULL)
@@ -518,21 +529,19 @@
         source_mp3->read_data = refbuf_new (source_mp3->queue_block_size);
         source_mp3->read_count = 0;
     }
-    buf = source_mp3->read_data->data + source_mp3->read_count;
-    if (source_mp3->read_count < source_mp3->queue_block_size)
+    if (source_mp3->read_count < source_mp3->read_data->len)
     {
-        int read_in = source_mp3->queue_block_size - source_mp3->read_count;
-        bytes = client_read_bytes (client, buf, read_in);
+        char *buf = source_mp3->read_data->data + source_mp3->read_count;
+        int read_in = source_mp3->read_data->len - source_mp3->read_count;
+        int bytes = client_read_bytes (client, buf, read_in);
         if (bytes < 0)
             return 0;
+        //DEBUG2 ("read in %d of %d bytes", bytes, read_in);
         rate_add (format->in_bitrate, bytes, client->worker->current_time.tv_sec);
+        source_mp3->read_count += bytes;
+        format->read_bytes += bytes;
     }
-    source_mp3->read_count += bytes;
-    refbuf = source_mp3->read_data;
-    refbuf->len = source_mp3->read_count;
-    format->read_bytes += bytes;
-
-    if (source_mp3->read_count < source_mp3->queue_block_size)
+    if (source_mp3->read_count < source_mp3->read_data->len)
         return 0;
     return 1;
 }
@@ -557,21 +566,29 @@
             }
             else
             {
+                if (unprocessed > 8000)
+                    return -1;
                 leftover = refbuf_new (unprocessed);
                 memcpy (leftover->data, refbuf->data + refbuf->len, unprocessed);
-                leftover->len = unprocessed;
                 mpeg_data_insert (mpeg_sync, leftover); /* will need to merge this after metadata */
                 return 0;
             }
         }
         /* make sure the new block has a minimum of queue_block_size */
-        len = unprocessed > source_mp3->queue_block_size ? unprocessed : source_mp3->queue_block_size;
+        if (unprocessed < source_mp3->queue_block_size)
+            len = source_mp3->queue_block_size;
+        else
+        {
+            if (unprocessed > 8000)
+                return -1;
+            len = unprocessed + 2000;
+        }
         leftover = refbuf_new (len);
         memcpy (leftover->data, refbuf->data + refbuf->len, unprocessed);
         source_mp3->read_data = leftover;
         source_mp3->read_count = unprocessed;
     }
-    return 0;
+    return refbuf->len ? 0 : -1;
 }
 
 
@@ -699,14 +716,18 @@
             {
                 char *title = filter_shoutcast_metadata (source,
                         source_mp3->build_metadata, source_mp3->build_metadata_len);
+                refbuf_t *flvmeta = flv_meta_allocate (strlen(title)+20);
                 logging_playlist (source->mount, title, source->listeners);
                 stats_event_conv (source->mount, "title", title, source->format->charset);
+                flv_meta_append (flvmeta, "title", title);
                 stats_event_time (source->mount, "metadata_updated");
                 yp_touch (source->mount);
                 free (title);
+                source_mp3->inline_url = strstr (meta->data+1, "StreamUrl='");
+                flv_meta_append (flvmeta, NULL, NULL);
+                meta->associated = flvmeta;
                 refbuf_release (source_mp3->metadata);
                 source_mp3->metadata = meta;
-                source_mp3->inline_url = strstr (meta->data+1, "StreamUrl='");
             }
             else
             {
@@ -757,6 +778,17 @@
     client->free_client_data = free_mp3_client_data;
     client->refbuf->len = 0;
 
+    if (client->flags & CLIENT_WANTS_FLV)
+    {
+        flv_create_client_data (plugin, client); // special case
+        return 0;
+    }
+    if (plugin->type == FORMAT_TYPE_AAC || plugin->type == FORMAT_TYPE_MPEG)
+    {
+        client_mp3->specific = calloc (1, sizeof(mpeg_sync));
+        mpeg_setup (client_mp3->specific, plugin->mount);
+    }
+
     if (format_general_headers (plugin, client) < 0)
         return -1;
 
@@ -812,6 +844,11 @@
 {
     mp3_client_data *client_mp3 = client->format_data;
 
+    if (client->flags & CLIENT_WANTS_FLV)
+        free_flv_client_data (client_mp3->specific);
+    else
+        mpeg_cleanup (client_mp3->specific);
+    free (client_mp3->specific);
     refbuf_release (client_mp3->associated);
     free (client->format_data);
     client->format_data = NULL;

Modified: icecast/branches/kh/icecast/src/format_mp3.h
===================================================================
--- icecast/branches/kh/icecast/src/format_mp3.h	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/format_mp3.h	2010-06-02 01:42:37 UTC (rev 17267)
@@ -20,7 +20,18 @@
 
 #include "format.h"
 
+#define CLIENT_WANTS_FLV            (CLIENT_FORMAT_BIT<<20)
+
 typedef struct {
+    refbuf_t *associated;
+    unsigned short interval;
+    short metadata_offset;
+    unsigned short since_meta_block;
+    void         *specific;
+} mp3_client_data;
+
+
+typedef struct {
     /* These are for inline metadata */
     int inline_metadata_interval;
     int offset;

Modified: icecast/branches/kh/icecast/src/format_ogg.c
===================================================================
--- icecast/branches/kh/icecast/src/format_ogg.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/format_ogg.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -503,8 +503,14 @@
 {
     struct ogg_client *client_data = client->format_data;
     refbuf_t *refbuf;
-    int written = 0;
+    int written = 0, loop = 10;
 
+    if (client->flags & CLIENT_HAS_MOVED)
+    {
+        /* if client has been moved then we need to send all header pages */
+        client_data->headers_sent = 1;
+        client->flags &= ~CLIENT_HAS_MOVED;
+    }
     if (client_data->headers_sent)
     {
         client_data->header_page = headers;
@@ -516,23 +522,33 @@
     {
         char *data = refbuf->data + client_data->pos;
         unsigned len = refbuf->len - client_data->pos;
-        int ret;
+        int ret = -1;
 
-        ret = client_send_bytes (client, data, len);
+        if (len < 8000 && client->connection.error == 0)
+            ret = client_send_bytes (client, data, len);
         if (ret > 0)
+        {
            written += ret;
+           client_data->pos += ret;
+        }
         if (ret < (int)len)
+        {
+            client->schedule_ms += 250;
             return written ? written : -1;
-        client_data->pos += ret;
+        }
         if (client_data->pos == refbuf->len)
         {
             refbuf = refbuf->associated;
             client_data->header_page = refbuf;
             client_data->pos = 0;
         }
+        if (written > 10000 || loop == 0)
+            return written;
+        loop--;
     }
     client_data->headers_sent = 1;
     client_data->headers = headers;
+    client_data->header_page = NULL;
     return written;
 }
 
@@ -553,9 +569,10 @@
         if (client_data->headers != refbuf->associated)
         {
             ret = send_ogg_headers (client, refbuf->associated);
+            if (ret > 0)
+                written += ret;
             if (client_data->headers_sent == 0)
                 break;
-            written += ret;
         }
         ret = client_send_bytes (client, buf, len);
 
@@ -563,21 +580,14 @@
         {
             client->pos += ret;
             client->queue_pos += ret;
+            written += ret;
         }
 
         if (ret < (int)len)
-            break;
-        written += ret;
-        /* we have now written the page(s) */
-        ret = 0;
+            client->schedule_ms += 250;
     } while (0);
 
-    if (ret > 0) /* short write */
-    {
-        client->schedule_ms += 250;
-        written += ret;
-    }
-    return written;
+    return written ? written : -1;
 }
 
 

Modified: icecast/branches/kh/icecast/src/fserve.c
===================================================================
--- icecast/branches/kh/icecast/src/fserve.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/fserve.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -244,7 +244,7 @@
                 if (result->refcount > result->peak)
                 {
                     result->peak = result->refcount;
-                    stats_event_flags (result->finfo.mount, "listener_peak", "%ld", result->peak);
+                    stats_event_args (result->finfo.mount, "listener_peak", "%ld", result->peak);
                 }
             }
             avl_insert (result->clients, client);

Modified: icecast/branches/kh/icecast/src/mpeg.c
===================================================================
--- icecast/branches/kh/icecast/src/mpeg.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/mpeg.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -47,9 +47,26 @@
 static int handle_aac_frame (struct mpeg_sync *mp, unsigned char *p, int len)
 {
     int frame_len = get_aac_frame_len (p);
+    int blocks, header_len = 9;
+    unsigned char *s = p;
     if (len - frame_len < 0)
         return 0;
 
+    blocks = (p[6] & 0x3) + 1;
+    if (p[1] & 0x1) // no crc
+        header_len -= 2;
+    s += header_len;
+    mp->sample_count = (blocks * 1024);
+    if (mp->raw)
+    {
+        unsigned raw_frame_len = frame_len - header_len;
+
+        if (mp->frame_callback)
+            if (mp->frame_callback (mp, s, raw_frame_len) < 0)
+                return -1;
+        memcpy (mp->raw->data + mp->raw_offset, s, raw_frame_len);
+        mp->raw_offset += raw_frame_len;
+    }
     return frame_len;
 }
 
@@ -81,22 +98,37 @@
 }
 
 
-static int get_frame_samples (struct mpeg_sync *mp, unsigned char *p)
+static int get_samples_per_mpegframe(int version, int layer)
 {
+    int samples_per_frame [4][4] = {
+        { -1,  576, 1152, 384 },    /* v2.5 - L3, L2, L1 */
+        { -1,   -1,   -1,  -1 },
+        { -1,  576, 1152, 384 },    /* v2 - L3, L2, L1 */
+        { -1, 1152, 1152, 576 }     /* v1 - L3, L2, L1 */
+    };
+    return samples_per_frame [version] [layer];
+}
+
+
+static int get_mpeg_frame_length (struct mpeg_sync *mp, unsigned char *p)
+{
     int padding = (p[2] & 0x2) >> 1;
     int frame_len = 0;
 
     int64_t bitrate = get_mpeg_bitrate (mp, p);
-    if (bitrate > 0)
+    int samples = get_samples_per_mpegframe (mp->ver, mp->layer);
+
+    mp->sample_count = samples;
+    if (bitrate > 0 && samples > 0)
     {
         bitrate *= 1000;
         if (mp->layer == LAYER_1)
         {
-            frame_len = (12 * bitrate / mp->samplerate + padding) * 4;
+            frame_len = (12 * bitrate / mp->samplerate + padding) * 4; // ??
         }
         else
         {
-            frame_len = 144 * bitrate / mp->samplerate + padding;
+            frame_len = samples / 8 * bitrate / mp->samplerate + padding;
         }
     }
     return frame_len;
@@ -105,20 +137,25 @@
 
 static int handle_mpeg_frame (struct mpeg_sync *mp, unsigned char *p, int remaining)
 {
-    int frame_len = get_frame_samples (mp, p);
+    int frame_len = get_mpeg_frame_length (mp, p);
 
     if (remaining - frame_len < 0)
         return 0;
+    if (mp->raw)
+    {
+        if (mp->frame_callback)
+            if (mp->frame_callback (mp, p, frame_len) < 0)
+                return -1;
+        memcpy (mp->raw->data + mp->raw_offset, p, frame_len);
+        mp->raw_offset += frame_len;
+    }
     return frame_len;
 }
 
 
 /* return -1 for no valid frame at this specified address, 0 for more data needed */
-static int get_initial_frame (struct mpeg_sync *mp, unsigned char *p, unsigned remaining)
+static int check_for_aac (struct mpeg_sync *mp, unsigned char *p, unsigned remaining)
 {
-    if (p[1] < 0xE0) 
-        return -1;
-    mp->layer = (p[1] & 0x6) >> 1;
     //nocrc = p[1] & 0x1;
     if (mp->layer == 0 && (p[1] >= 0xF0))
     {
@@ -126,15 +163,19 @@
             v = (p[2] << 8) + p[3],
             channels_idx = (v & 0x1C0) >> 6;
         int id =  p[1] & 0x8;
-        int checking = 3;
+        int checking = 4;
         unsigned char *fh = p;
 
         while (checking)
         {
+            //DEBUG1 ("checking frame %d", 5-checking);
             int frame_len = get_aac_frame_len (fh);
+            if (frame_len <= 0 || frame_len > 8192)
+                return -1;
             if (frame_len+5 >= remaining)
                 return 0;
-            if (fh[frame_len] != 255 || fh[frame_len+1] != p[1])
+            if (fh[frame_len] != 255 || fh[frame_len+1] != p[1] || fh[frame_len+2] != p[2]
+                    || (fh[frame_len+3]&0xF0) != (p[3]&0xF0))
                 return -1;
             remaining -= frame_len;
             fh += frame_len;
@@ -150,10 +191,16 @@
         }
         mp->syncbytes = 3;
         memcpy (&mp->fixed_headerbits[0], p, 3);
-        DEBUG3 ("detected AAC MPEG-%s, rate %d, channels %d", id ? "2" : "4", mp->samplerate, mp->channels);
+        INFO4 ("detected AAC MPEG-%s, rate %d, channels %d on %s", id ? "2" : "4", mp->samplerate,
+                mp->channels, mp->mount);
         mp->process_frame = handle_aac_frame;
         return 1;
     }
+    return -1;
+}
+
+static int check_for_mp3 (struct mpeg_sync *mp, unsigned char *p, unsigned remaining)
+{
     if (mp->layer && (p[1] >= 0xE0))
     {
         const char *version[] = { "MPEG 2.5", NULL, "MPEG 2", "MPEG 1" };
@@ -168,6 +215,7 @@
                 { 12000, 0, 24000, 48000 },
                 {  8000, 0, 16000, 32000 },
                 { 0,0,0 } };
+            char stream_type[20];
 
             // au.crc = (p[1] & 0x1) == 0;
             mp->samplerate = samplerates [(p[2]&0xC) >> 2][mp->ver];
@@ -175,13 +223,23 @@
                 return -1;
             while (checking)
             {
-                int frame_len = get_frame_samples (mp, fh);
-                if (frame_len <= 0)
+                int frame_len = get_mpeg_frame_length (mp, fh);
+                if (frame_len <= 0 || frame_len > 3000)
+                {
+                    //DEBUG2 ("checking frame %d, but len %d invalid", 5-checking, frame_len);
                     return -1;
+                }
                 if (frame_len+4 >= remaining)
+                {
+                    //DEBUG3 ("checking frame %d, but need more data (%d,%d)", 5-checking, frame_len, remaining);
                     return 0;
+                }
                 if (fh[frame_len] != 255 || fh[frame_len+1] != p[1])
+                {
+                    //DEBUG4 ("checking frame %d, but code is %x %x %x", 5-checking, fh[frame_len], fh[frame_len+1], fh[frame_len+2]);
                     return -1;
+                }
+                //DEBUG4 ("frame %d checked, next header codes are %x %x %x", 5-checking, fh[frame_len], fh[frame_len+1], fh[frame_len+2]);
                 remaining -= frame_len;
                 fh += frame_len;
                 checking--;
@@ -192,7 +250,8 @@
                 mp->channels = 2;
             mp->syncbytes = 2;
             memcpy (&mp->fixed_headerbits[0], p, 2);
-            DEBUG4 ("%s %s detected (%d, %d)", version [mp->ver], layer[mp->layer], mp->samplerate, mp->channels);
+            snprintf (stream_type, sizeof (stream_type), "%s %s", version [mp->ver], layer[mp->layer]);
+            INFO4 ("%s detected (%d, %d) on %s", stream_type, mp->samplerate, mp->channels, mp->mount);
             mp->process_frame = handle_mpeg_frame;
             return 1;
         }
@@ -200,17 +259,35 @@
     return -1;
 }
 
+/* return -1 for no valid frame at this specified address, 0 for more data needed */
+static int get_initial_frame (struct mpeg_sync *mp, unsigned char *p, unsigned remaining)
+{
+    int ret;
 
+    if (p[1] < 0xE0)
+        return -1;
+    mp->layer = (p[1] & 0x6) >> 1;
+    ret = check_for_aac (mp, p, remaining);
+    if (ret < 0)
+    {
+        ret = check_for_mp3 (mp, p, remaining);
+        if (ret < 0)
+            DEBUG0 ("neither aac or mp3");
+    }
+    return ret;
+}
+
 /* return number from 1 to remaining */
 static int find_align_sync (unsigned char *start, int remaining)
 {
+    int skip = 0;
     unsigned char *p = memchr (start, 255, remaining);
     if (p)
     {
-        remaining -= (p - start);
-        memmove (start, p, remaining);
+        skip = p - start;
+        memmove (start, p, remaining - skip);
     }
-    return remaining;
+    return skip;
 }
 
 
@@ -222,6 +299,7 @@
     if (mp == NULL)
         return 0;  /* leave as-is */
     
+    mp->sample_count = 0;
     if (mp->surplus)
     {
         int new_len = mp->surplus->len + new_block->len;
@@ -236,20 +314,24 @@
     }
     start = (unsigned char *)new_block->data + offset;
     remaining = new_block->len - offset;
-    end = (unsigned char*)new_block->data + new_block->len;
+    mp->raw_offset = 0;
     while (1)
     {
+        end = (unsigned char*)new_block->data + new_block->len;
         remaining = end - start;
+        //DEBUG2 ("block size %d, remaining now %d", new_block->len, remaining);
         if (remaining < 10) /* make sure we have some bytes to check */
             break;
         if (*start != 255)
         {
             // need to resync
             int ret = find_align_sync (start, remaining);
-            if (ret == remaining)
+            if (ret == 0)
                 break; // no sync in the rest, so dump it
-            new_block->len -= (remaining - ret);
-            end = (unsigned char*)new_block->data + new_block->len;
+            DEBUG1 ("no frame sync, re-checking after skipping %d", ret);
+            new_block->len -= ret;
+            mp->resync_count++;
+            mp->syncbytes = 0; /* force an initial recheck */
             continue;
         }
         if (mp->syncbytes == 0)
@@ -259,32 +341,32 @@
             {
                 // failed to detect a complete frame, try again
                 memmove (start, start+1, remaining-1);
-                end--;
+                new_block->len--;
                 continue;
             }
             if (ret == 0)
-                break;
+            {
+                if (remaining > 10000)
+                    return -1;
+                new_block->len = 0;
+                return remaining;
+            }
         }
         if (memcmp (start, &mp->fixed_headerbits[0], mp->syncbytes) != 0)
         {
             memmove (start, start+1, remaining-1);
-            end--;
+            new_block->len--;
             continue;
         }
         frame_len = mp->process_frame (mp, start, remaining);
+        //DEBUG2 ("seen frame of %d (%d) bytes", frame_len, remaining);
         if (frame_len <= 0)  // frame fragment at the end
             break;
         start += frame_len;
         completed++;
     }
-    if (completed == 0)
-    {
-        /* none found, so either shrink it or drop it */
-        if (remaining > 1500)
-            remaining = 1500;
-        else
-            return -1;
-    }
+    if (remaining < 0 || remaining > new_block->len)
+        abort();
     new_block->len -= remaining;
     return remaining;
 }
@@ -296,9 +378,10 @@
         mp->surplus = inserted;
 }
 
-void mpeg_setup (mpeg_sync *mpsync)
+void mpeg_setup (mpeg_sync *mpsync, const char *mount)
 {
     memset (mpsync, 0, sizeof (mpeg_sync));
+    mpsync->mount = mount;
 }
 
 void mpeg_cleanup (mpeg_sync *mpsync)
@@ -306,5 +389,6 @@
     if (mpsync)
     {
         refbuf_release (mpsync->surplus);
+        refbuf_release (mpsync->raw);
     }
 }

Modified: icecast/branches/kh/icecast/src/mpeg.h
===================================================================
--- icecast/branches/kh/icecast/src/mpeg.h	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/mpeg.h	2010-06-02 01:42:37 UTC (rev 17267)
@@ -22,13 +22,20 @@
     char syncbytes;
     int (*process_frame) (struct mpeg_sync *mp, unsigned char *p, int len);
     refbuf_t *surplus;
+    long sample_count;
+    long resync_count;
+    void *callback_key;
+    int (*frame_callback)(struct mpeg_sync *mp, unsigned char *p, unsigned int len);
+    refbuf_t *raw;
+    int raw_offset;
     int ver;
     int layer;
     int samplerate;
     int channels;
+    const char *mount;
 } mpeg_sync;
 
-void mpeg_setup (mpeg_sync *mpsync);
+void mpeg_setup (mpeg_sync *mpsync, const char *mount);
 void mpeg_cleanup (mpeg_sync *mpsync);
 
 int  mpeg_complete_frames (mpeg_sync *mp, refbuf_t *new_block, unsigned offset);

Modified: icecast/branches/kh/icecast/src/refbuf.h
===================================================================
--- icecast/branches/kh/icecast/src/refbuf.h	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/refbuf.h	2010-06-02 01:42:37 UTC (rev 17267)
@@ -22,12 +22,12 @@
 
 typedef struct _refbuf_tag
 {
-    unsigned int len;
+    unsigned int flags;
     unsigned int _count;
+    struct _refbuf_tag *next;
+    struct _refbuf_tag *associated;
     char *data;
-    struct _refbuf_tag *associated;
-    struct _refbuf_tag *next;
-    unsigned int flags;
+    unsigned int len;
 
 } refbuf_t;
 

Modified: icecast/branches/kh/icecast/src/slave.c
===================================================================
--- icecast/branches/kh/icecast/src/slave.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/slave.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -162,10 +162,10 @@
         copy->mp3metadata = r->mp3metadata;
         copy->on_demand = r->on_demand;
         copy->interval = r->interval;
-        copy->enable = r->enable;
         copy->source = r->source;
+        copy->running = 1;
         r->source = NULL;
-        DEBUG1 ("copy relay at %p", copy);
+        DEBUG2 ("copy relay %s at %p", copy->localmount, copy);
     }
     return copy;
 }
@@ -386,7 +386,7 @@
         thread_mutex_unlock (&client->worker->lock);
         streamsock = sock_connect_wto_bind (server, port, bind, timeout);
         free (bind);
-        if (streamsock == SOCK_ERROR || connection_init (con, streamsock, server) < 0)
+        if (connection_init (con, streamsock, server) < 0)
         {
             WARN2 ("Failed to connect to %s:%d", server, port);
             thread_mutex_lock (&client->worker->lock);
@@ -463,7 +463,6 @@
     free (server);
     free (mount);
     free (auth_header);
-    connection_close (con);
     if (parser)
         httpp_destroy (parser);
     return -1;
@@ -486,13 +485,15 @@
         ret = open_relay_connection (client, relay, master);
         thread_mutex_lock (&src->lock);
 
-        if (relay != client->shared_data) // relay data changed, retry with new data
-            return open_relay (client->shared_data);
+        if (relay != client->shared_data)
+        {
+            relay = client->shared_data; // relay data may of changed
+            master = relay->masters;
+        }
         if (ret < 0)
             continue;
-        source_clear_source (src); // clear any old data
 
-        if (connection_complete_source (src, client->parser) < 0)
+        if (connection_complete_source (src) < 0)
         {
             WARN1 ("Failed to complete initialisation on %s", relay->localmount);
             continue;
@@ -541,14 +542,15 @@
     } while (0);
 
     relay = client->shared_data; // relay may of changed during open_relay
-    relay->running = 1;
     client->ops = &relay_client_ops;
     client->schedule_ms = timing_get_time();
 
     if (failed)
     {
         /* failed to start, better clean up and reset */
-        if (relay->on_demand == 0)
+        if (relay->on_demand)
+            src->flags &= ~SOURCE_ON_DEMAND;
+        else
             yp_remove (relay->localmount);
 
         INFO2 ("listener count remaining on %s is %d", src->mount, src->listeners);
@@ -889,7 +891,7 @@
             r->mp3metadata = 1;
             r->on_demand = master->on_demand;
             r->interval = master->max_interval;
-            r->enable = 1;
+            r->running = 1;
             if (master->send_auth)
             {
                 r->username = (char *)xmlStrdup (XMLSTR(master->username));
@@ -915,7 +917,7 @@
     struct master_conn_details *master = arg;
     CURL *handle;
     const char *protocol = "http";
-    int port = master->port;
+    int port = master->port, have_new_list = 1;
     relay_server *cleanup_relays;
     char error [CURL_ERROR_SIZE];
     char url [1024], auth [100];
@@ -951,17 +953,23 @@
                 protocol, master->server, port, master->args);
         curl_easy_setopt (handle, CURLOPT_URL, url);
         if (curl_easy_perform (handle) != 0)
+        {
             WARN2 ("Failed URL access \"%s\" (%s)", url, error);
+            have_new_list = 0;
+        }
     }
-    /* merge retrieved relays */
-    thread_mutex_lock (&(config_locks()->relay_lock));
-    cleanup_relays = update_relays (&global.master_relays, master->new_relays);
+    if (have_new_list)
+    {
+        /* merge retrieved relays */
+        thread_mutex_lock (&(config_locks()->relay_lock));
+        cleanup_relays = update_relays (&global.master_relays, master->new_relays);
 
-    relay_check_streams (global.master_relays, cleanup_relays, 1);
+        relay_check_streams (global.master_relays, cleanup_relays, 1);
+
+        thread_mutex_unlock (&(config_locks()->relay_lock));
+    }
     relay_check_streams (NULL, master->new_relays, 0);
 
-    thread_mutex_unlock (&(config_locks()->relay_lock));
-
     curl_easy_cleanup (handle);
     free (master->server);
     free (master->username);
@@ -1249,7 +1257,7 @@
     thread_mutex_lock (&source->lock);
     if (source_running (source))
     {
-        if (relay->enable == 0 || relay->cleanup)
+        if (relay->cleanup)
             source->flags &= ~SOURCE_RUNNING;
         if (relay->on_demand && source->listeners == 0)
             source->flags &= ~SOURCE_RUNNING;
@@ -1258,10 +1266,24 @@
     if ((source->flags & SOURCE_TERMINATING) == 0)
     {
         int fallback = 1;
-        if (relay->running && relay->enable && client->connection.con_time)
-            fallback = 0;
+        if (client->connection.con_time)
+        {
+            if (relay->running)
+                fallback = 0;
+            global_lock();
+            global.sources--;
+            stats_event_args (NULL, "sources", "%d", global.sources);
+            global_unlock();
+            global_reduce_bitrate_sampling (global.out_bitrate);
+            if (client->worker->current_time.tv_sec - client->connection.con_time < 300)
+            {
+                INFO1 ("relay %s terminated quickly, will restart in 60s", relay->localmount);
+                relay->start = client->worker->current_time.tv_sec + 60;
+            }
+            connection_close (&client->connection);
+        }
         /* don't pause listeners if relay shutting down */
-        if (relay->running == 0 || relay->enable == 0)
+        if (relay->running == 0)
             source->flags &= ~SOURCE_PAUSE_LISTENERS;
         // fallback listeners unless relay is to be retried
         source_shutdown (source, fallback);
@@ -1276,10 +1298,9 @@
     }
     DEBUG1 ("all listeners have now been checked on %s", relay->localmount);
     source->flags &= ~SOURCE_TERMINATING;
-    if (relay->running && relay->enable)
+    if (relay->running)
     {
         INFO1 ("standing by to restart relay on %s", relay->localmount);
-        connection_close (&client->connection);
         thread_mutex_unlock (&source->lock);
         ret = 0;
     }
@@ -1292,23 +1313,12 @@
             return 0; /* listeners may be paused, recheck and let them leave this stream */
         }
         INFO1 ("shutting down relay %s", relay->localmount);
-        if (relay->enable == 0)
-        {
-            source_clear_source (source);
-            relay->running = 0;
-            ret = 0;
-        }
         source_clear_listeners (source);
         thread_mutex_unlock (&source->lock);
         stats_event (relay->localmount, NULL, NULL); // needed???
         slave_update_all_mounts();
     }
     client->ops = &relay_startup_ops;
-    global_lock();
-    global.sources--;
-    stats_event_args (NULL, "sources", "%d", global.sources);
-    global_unlock();
-    global_reduce_bitrate_sampling (global.out_bitrate);
     return ret;
 }
 
@@ -1327,15 +1337,14 @@
 
     if (relay->cleanup)
     {
-        source_t *source = relay->source;
-        thread_mutex_lock (&source->lock);
-        source_clear_listeners (source);
-        thread_mutex_unlock (&source->lock);
-        return -1;
+        /* listeners may be still on, do a recheck */
+        relay->running = 0;
+        client->ops = &relay_client_ops;
+        return 0;
     }
     if (global.running != ICE_RUNNING)
         return 0; /* wait for cleanup */
-    if (relay->enable == 0 || relay->start > client->worker->current_time.tv_sec)
+    if (relay->running == 0 || relay->start > client->worker->current_time.tv_sec)
     {
         client->schedule_ms = client->worker->time_ms + 1000;
         return 0;
@@ -1373,7 +1382,7 @@
             client->schedule_ms = client->worker->time_ms + 1000;
             return 0;
         }
-        DEBUG1 ("Detected listeners on relay %s", relay->localmount);
+        INFO1 ("Detected listeners on relay %s", relay->localmount);
     }
 
     /* limit the number of relays starting up at the same time */

Modified: icecast/branches/kh/icecast/src/source.c
===================================================================
--- icecast/branches/kh/icecast/src/source.c	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/src/source.c	2010-06-02 01:42:37 UTC (rev 17267)
@@ -879,6 +879,7 @@
             if (client->refbuf && (client->refbuf->flags & SOURCE_QUEUE_BLOCK))
                 client_set_queue (client, NULL);
             client->ops = &listener_pause_ops;
+            client->flags |= CLIENT_HAS_MOVED;
             client->schedule_ms = client->worker->time_ms + 100;
             return 0;
         }
@@ -1010,6 +1011,7 @@
     source->client_stats_update = source->last_read + 3;
     source->skip_duration = 80;
 
+    util_dict_free (source->audio_info);
     source->audio_info = util_dict_new();
     if (source->client)
     {
@@ -1897,14 +1899,15 @@
         stats_event_args (NULL, "sources", "%d", global.sources);
         global_unlock();
         thread_mutex_lock (&source->lock);
-        if (connection_complete_source (source, client->parser) < 0)
+        source->client = client;
+        if (connection_complete_source (source) < 0)
         {
+            source->client = NULL;
             client_send_403 (client, "content type not supported");
             thread_mutex_unlock (&source->lock);
             source_free_source (source);
             return 0;
         }
-        source->client = client;
         source->parser = client->parser;
         client->respcode = 200;
         client->shared_data = source;

Modified: icecast/branches/kh/icecast/win32/Icecast2win.clw
===================================================================
--- icecast/branches/kh/icecast/win32/Icecast2win.clw	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/win32/Icecast2win.clw	2010-06-02 01:42:37 UTC (rev 17267)
@@ -27,14 +27,14 @@
 Resource9=IDD_ABOUTBOX
 Resource10=IDR_MENU4
 Resource11=IDR_TRAY (English (U.S.))
-Resource12=IDD_ABOUTBOX (English (U.S.))
+Resource12=IDR_MENU4 (English (U.S.))
 Resource13=IDD_ICECAST2WIN_DIALOG (English (U.S.))
 Resource14=IDD_SSTATUS (English (U.S.))
 Resource15=IDD_CONFIGDIALOG (English (U.S.))
 Resource16=IDD_STATSDIALOG (English (U.S.))
 Resource17=IDR_MENU2 (English (U.S.))
 Resource18=IDR_MENU3 (English (U.S.))
-Resource19=IDR_MENU4 (English (U.S.))
+Resource19=IDD_ABOUTBOX (English (U.S.))
 
 [CLS:CIcecast2winApp]
 Type=0

Modified: icecast/branches/kh/icecast/win32/icecast.dsp
===================================================================
--- icecast/branches/kh/icecast/win32/icecast.dsp	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/win32/icecast.dsp	2010-06-02 01:42:37 UTC (rev 17267)
@@ -130,6 +130,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=..\src\flv.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\fnmatch.c
 
 !IF  "$(CFG)" == "icecast - Win32 Release"
@@ -311,6 +315,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=..\src\flv.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\format.h
 # End Source File
 # Begin Source File

Modified: icecast/branches/kh/icecast/win32/icecast2.iss
===================================================================
--- icecast/branches/kh/icecast/win32/icecast2.iss	2010-06-02 00:22:13 UTC (rev 17266)
+++ icecast/branches/kh/icecast/win32/icecast2.iss	2010-06-02 01:42:37 UTC (rev 17267)
@@ -3,7 +3,7 @@
 
 [Setup]
 AppName=Icecast2-KH
-AppVerName=Icecast v2.3.2-kh23
+AppVerName=Icecast v2.3.2-kh24
 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-kh23_setup
+OutputBaseFilename=icecast2_win32_v2.3.2-kh24_setup
 WizardImageFile=icecast2logo2.bmp
 WizardImageStretch=no
 VersionInfoVersion=2.3.2



More information about the commits mailing list