Re: [Icecast] Icecast WebM Support Patch Second Edition

Krad Radio <[email protected]>
Newsgroups gmane.comp.audio.icecast.devel,gmane.comp.audio.icecast.general
Message-ID <CA+8JHGgb+p=ASk9Ma8vF7FLMgFO9x3MszwgaUEqHKk9kBpUWSw@mail.gmail.com>
One liner fix update for a potential segfault when a client connects and
the header is not yet received from the source.

Updated full patch attached.



On Thu, Feb 9, 2012 at 5:37 AM, Dennis Heerema <[email protected]> wrote:

>  Hi David,
>
>  Streaming live WebM would also give a boost to this open format.
>  Whitch Client do you use to stream the webm format to icecast with?
>

I'm using Krad Cam, which is in a very early alpha form. More information
is here:

https://gist.github.com/1773943

There is also a "Test Signal" client that sends a video only stream for
testing purposes.

-David



>  Regards,
>
> Dennis
>
>
>  -----Original Message-----
> From: Krad Radio <[email protected]>
> To: [email protected], [email protected]
> Date: Wed, 8 Feb 2012 23:53:05 -0500
> Subject: [Icecast] Icecast WebM Support Patch Second Edition
>
> Howdy,
>
>
>  tl;dr:  Nothing new or interesting to non-developers
>
>  Attached is a newer Icecast WebM support patch for Icecast SVN, there is
> simply aesthetic changes. Whitespace has been altered to match Icecast
> project style,
>  some functions have been renamed and moved around.
>
>  Some discussion.
>
>  The format_ebml.c file lines 0-296 operates much the same as the
> format_ogg.c file does. It uses a parsing library api to feed in and pull
> out bytes via
>  the _get_buffer function. In the case of Ogg this is of course libogg, in
> the case of ebml, the "parsing library" consists of the functions below
> line 296.
>  This mini-included library comes from some debugging code that I wrote
> during my work on a much more robust EBML muxing library, originally I had
> thought
>  icecast would need to do some manipulation of the EBML segment header in
> all cases, but it turns out in most cases this is better done by the source
> client.
>  This mini-included library implements the same function calls that
> Icecast would need to use if it was using this EBML muxing library, but
> "parse" is a very
>  generous descriptor of whats actually happening. If you are familiar with
> the internals of Ogg streaming, you know that the stream starts out with a
> few header pages
>  that are then followed by pages containing the acutual video and audio
> packets. EBML doctype WebM and MKV (The only currently known) work in
> a similar way.
>  There is a "Segment Header" followed by "Clusters" (Its actually more
> nuanced than this, but this oversimplification will suffice for the
> moment). Clusters are comparable to
>  Ogg pages. The connecting client needs to be sent the header, and then it
> can start on any cluster as long as it starts exactly on a cluster. (This
> is actually not a format requirement per se but all media players I am
> aware of lack the ability to re-sync themselves if started on a random
> byte, something I intend not to be a limitation of my own work). So, in a
> live WebM stream, everything before the first cluster is the "Header" and
> then the rest is the clusters, of which the boundary between them is marked
> with 4 specific bytes. So at every cluster the refbuf is marked as a sync
> point. The size of clusters could vary significantly during a stream or
> from stream to stream, but on a properly constructed one would indicate a
> keyframe in the case of a stream with video. The 'mini-library' doesn't
> actually 'parse' the stream at all, all it does is look for the four byte
> sequence indicating a cluster boundary and informs the format functions
> appropriately. I am a fraudulent mathematician at best, but I calculate
> that there is a 1 in 4.2 billion chance of this happening for any given 4
> bytes, and 1 in 4294 per megabyte, and likely once per 4.2 gigabytes. This
> is not a problem once the stream has started for the client however, it
> only matters when the source connects and the header is stored, and when a
> client connects and needs a proper starting point. I suppose that makes it
> very unlikely to cause a problem, even though its technically wrong. It
> also means that each byte is being compared, whilst when properly parsing
> most would be skipped, but computers are so damn fast that its moot. At any
> rate proper parsing could be added this this mini-library or provided by
> the external library as it matures.
>
>  Enjoy,
>
>  David
>
>
>
>
>
>
>

_______________________________________________
Icecast-dev mailing list
[email protected]
http://lists.xiph.org/mailman/listinfo/icecast-dev
icecast-webm-support-clean1-fix.patch (text/x-patch, 20.1 KB)
diff --git a/AUTHORS b/AUTHORS
index 0d4abc0..b063deb 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,3 +4,4 @@ oddsock <[email protected]>
 Karl Heyes <[email protected]>
 Philipp "ph3-der-loewe" Schafft <[email protected]>
 Thomas B. "dm8tbr" Ruecker <[email protected]>
+David "oneman" Richards <[email protected]>
diff --git a/src/Makefile.am b/src/Makefile.am
index 8c2ef21..3ed529e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -10,13 +10,13 @@ noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \
     global.h util.h slave.h source.h stats.h refbuf.h client.h \
     compat.h fserve.h xslt.h yp.h event.h md5.h \
     auth.h auth_htpasswd.h auth_url.h \
-    format.h format_ogg.h format_mp3.h \
+    format.h format_ogg.h format_mp3.h format_ebml.h\
     format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h \
     format_kate.h format_skeleton.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 \
+    format.c format_ogg.c format_mp3.c format_midi.c format_flac.c format_ebml.c\
     auth.c auth_htpasswd.c format_kate.c format_skeleton.c
 EXTRA_icecast_SOURCES = yp.c \
     auth_url.c \
diff --git a/src/format.c b/src/format.c
index 415391c..8409294 100644
--- a/src/format.c
+++ b/src/format.c
@@ -40,6 +40,7 @@
 
 #include "format_ogg.h"
 #include "format_mp3.h"
+#include "format_ebml.h"
 
 #include "logging.h"
 #include "stats.h"
@@ -64,6 +65,16 @@ format_type_t format_get_type (const char *contenttype)
         return FORMAT_TYPE_OGG;
     else if(strcmp(contenttype, "video/ogg") == 0)
         return FORMAT_TYPE_OGG;
+    else if(strcmp(contenttype, "audio/webm") == 0)
+        return FORMAT_TYPE_EBML;
+    else if(strcmp(contenttype, "video/webm") == 0)
+        return FORMAT_TYPE_EBML;
+    else if(strcmp(contenttype, "audio/x-matroska") == 0)
+        return FORMAT_TYPE_EBML;
+    else if(strcmp(contenttype, "video/x-matroska") == 0)
+        return FORMAT_TYPE_EBML;
+    else if(strcmp(contenttype, "video/x-matroska-3d") == 0)
+        return FORMAT_TYPE_EBML;
     else
         /* We default to the Generic format handler, which
            can handle many more formats than just mp3 */
@@ -78,6 +89,9 @@ int format_get_plugin(format_type_t type, source_t *source)
     case FORMAT_TYPE_OGG:
         ret = format_ogg_get_plugin (source);
         break;
+	case FORMAT_TYPE_EBML:
+		ret = format_ebml_get_plugin (source);
+		break;
     case FORMAT_TYPE_GENERIC:
         ret = format_mp3_get_plugin (source);
         break;
diff --git a/src/format.h b/src/format.h
index d52b9e9..0a96ee9 100644
--- a/src/format.h
+++ b/src/format.h
@@ -29,6 +29,7 @@ typedef enum _format_type_tag
 {
     FORMAT_ERROR, /* No format, source not processable */
     FORMAT_TYPE_OGG,
+    FORMAT_TYPE_EBML,
     FORMAT_TYPE_GENERIC
 } format_type_t;
 
diff --git a/src/format_ebml.c b/src/format_ebml.c
new file mode 100644
index 0000000..16b7fb7
--- /dev/null
+++ b/src/format_ebml.c
@@ -0,0 +1,571 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2012, Jack Moffitt <[email protected], 
+ *                      Michael Smith <[email protected]>,
+ *                      oddsock <[email protected]>,
+ *                      Karl Heyes <[email protected]>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format_ebml.c
+ *
+ * format plugin for EBML
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "refbuf.h"
+#include "source.h"
+#include "client.h"
+
+#include "stats.h"
+#include "format.h"
+#include "format_ebml.h"
+
+#include "logging.h"
+
+#define CATMODULE "format-ebml"
+
+#define EBML_DEBUG 0
+#define EBML_HEADER_MAX_SIZE 131072
+#define EBML_SLICE_SIZE 4096
+
+#define EBML_CLUSTER_BYTE1 0x1F
+#define EBML_CLUSTER_BYTE2 0x43
+#define EBML_CLUSTER_BYTE3 0xB6
+#define EBML_CLUSTER_BYTE4 0x75
+
+typedef struct ebml_client_data_st ebml_client_data_t;
+
+struct ebml_client_data_st {
+
+    refbuf_t *header;
+    int header_pos;
+
+};
+
+struct ebml_st {
+
+    char cluster_mark[4];
+
+    uint64_t position;
+    uint64_t read_position;
+    int buffer_position;
+    uint64_t cluster_position;
+
+    int header_read;
+
+    int header_size;
+    int header_position;
+    int header_read_position;
+
+    unsigned char *input_buffer;
+    unsigned char *buffer;
+    unsigned char *header;
+
+    uint64_t found;
+    uint64_t matched_byte_num;
+    unsigned char match_byte;
+
+    int last_was_cluster_end;
+    int this_was_cluster_start;
+
+};
+
+static void ebml_free_plugin (format_plugin_t *plugin);
+static refbuf_t *ebml_get_buffer (source_t *source);
+static int  ebml_write_buf_to_client (client_t *client);
+static void  ebml_write_buf_to_file (source_t *source, refbuf_t *refbuf);
+static int  ebml_create_client_data (source_t *source, client_t *client);
+static void ebml_free_client_data (client_t *client);
+
+static ebml_t *ebml_create();
+static void ebml_destroy(ebml_t *ebml);
+static size_t ebml_read_space(ebml_t *ebml);
+static int ebml_read(ebml_t *ebml, char *buffer, int len);
+static int ebml_last_was_sync(ebml_t *ebml);
+static char *ebml_write_buffer(ebml_t *ebml, int len);
+static int ebml_wrote(ebml_t *ebml, int len);
+static void ebml_debug(ebml_t *ebml);
+static unsigned char ebml_get_next_match_byte(unsigned char match_byte, uint64_t position,
+                                              uint64_t *matched_byte_num, uint64_t *found);
+
+int format_ebml_get_plugin (source_t *source)
+{
+
+    ebml_source_state_t *ebml_source_state = calloc(1, sizeof(ebml_source_state_t));
+    format_plugin_t *plugin = calloc(1, sizeof(format_plugin_t));
+
+    plugin->get_buffer = ebml_get_buffer;
+    plugin->write_buf_to_client = ebml_write_buf_to_client;
+    plugin->create_client_data = ebml_create_client_data;
+    plugin->free_plugin = ebml_free_plugin;
+    plugin->write_buf_to_file = ebml_write_buf_to_file;
+    plugin->set_tag = NULL;
+    plugin->apply_settings = NULL;
+
+    plugin->contenttype = httpp_getvar (source->parser, "content-type");
+
+    plugin->_state = ebml_source_state;
+    source->format = plugin;
+
+    ebml_source_state->ebml = ebml_create();
+    return 0;
+}
+
+static void ebml_free_plugin (format_plugin_t *plugin)
+{
+
+    ebml_source_state_t *ebml_source_state = plugin->_state;
+
+    refbuf_release (ebml_source_state->header);
+    ebml_destroy(ebml_source_state->ebml);
+    free (ebml_source_state);
+    free (plugin);
+
+}
+
+static int send_ebml_header (client_t *client)
+{
+
+    ebml_client_data_t *ebml_client_data = client->format_data;
+    int len = EBML_SLICE_SIZE;
+    int ret;
+
+    if (ebml_client_data->header->len - ebml_client_data->header_pos < len) 
+    {
+        len = ebml_client_data->header->len - ebml_client_data->header_pos;
+    }
+    ret = client_send_bytes (client, 
+                             ebml_client_data->header->data + ebml_client_data->header_pos,
+                             len);
+
+    if (ret > 0)
+    {
+        ebml_client_data->header_pos += ret;
+    }
+
+    return ret;
+
+}
+
+static int ebml_write_buf_to_client (client_t *client)
+{
+
+    ebml_client_data_t *ebml_client_data = client->format_data;
+
+    if (ebml_client_data->header_pos != ebml_client_data->header->len)
+    {
+        return send_ebml_header (client);
+    }
+    else
+    {
+        client->write_to_client = format_generic_write_to_client;
+        return client->write_to_client(client);
+    }
+
+}
+
+static refbuf_t *ebml_get_buffer (source_t *source)
+{
+
+    ebml_source_state_t *ebml_source_state = source->format->_state;
+    format_plugin_t *format = source->format;
+    char *data = NULL;
+    int bytes = 0;
+    refbuf_t *refbuf;
+    int ret;
+
+    while (1)
+    {
+    
+        if (EBML_DEBUG) {
+            ebml_debug(ebml_source_state->ebml);
+        }
+
+        if ((bytes = ebml_read_space(ebml_source_state->ebml)) > 0)
+        {
+            refbuf = refbuf_new(bytes);
+            ebml_read(ebml_source_state->ebml, refbuf->data, bytes);
+
+            if (ebml_source_state->header == NULL)
+            {
+                ebml_source_state->header = refbuf;
+                continue;
+            }
+
+            if (ebml_last_was_sync(ebml_source_state->ebml))
+            {
+                refbuf->sync_point = 1;
+            }
+            return refbuf;
+
+        }
+        else
+        {
+
+            data = ebml_write_buffer(ebml_source_state->ebml, EBML_SLICE_SIZE);
+            bytes = client_read_bytes (source->client, data, EBML_SLICE_SIZE);
+            if (bytes <= 0)
+            {
+                ebml_wrote (ebml_source_state->ebml, 0);
+                return NULL;
+            }
+            format->read_bytes += bytes;
+            ret = ebml_wrote (ebml_source_state->ebml, bytes);
+            if (ret != bytes) {
+                ERROR0 ("Problem processing stream");
+                source->running = 0;
+                return NULL;
+            }
+        }
+    }
+}
+
+static int ebml_create_client_data (source_t *source, client_t *client)
+{
+
+    ebml_client_data_t *ebml_client_data = calloc(1, sizeof(ebml_client_data_t));
+    ebml_source_state_t *ebml_source_state = source->format->_state;
+
+    int ret = -1;
+
+    if ((ebml_client_data) && (ebml_source_state->header))
+    {
+        ebml_client_data->header = ebml_source_state->header;
+        refbuf_addref (ebml_client_data->header);
+        client->format_data = ebml_client_data;
+        client->free_client_data = ebml_free_client_data;
+        ret = 0;
+    }
+
+    return ret;
+
+}
+
+
+static void ebml_free_client_data (client_t *client)
+{
+
+    ebml_client_data_t *ebml_client_data = client->format_data;
+
+    refbuf_release (ebml_client_data->header);
+    free (client->format_data);
+    client->format_data = NULL;
+}
+
+
+static void ebml_write_buf_to_file_fail (source_t *source)
+{
+    WARN0 ("Write to dump file failed, disabling");
+    fclose (source->dumpfile);
+    source->dumpfile = NULL;
+}
+
+
+static void ebml_write_buf_to_file (source_t *source, refbuf_t *refbuf)
+{
+
+    ebml_source_state_t *ebml_source_state = source->format->_state;
+
+    if (ebml_source_state->file_headers_written == 0)
+    {
+        if (fwrite (ebml_source_state->header->data, 1,
+                    ebml_source_state->header->len, 
+                    source->dumpfile) != ebml_source_state->header->len)
+            ebml_write_buf_to_file_fail(source);
+        else
+            ebml_source_state->file_headers_written = 1;
+    }
+
+    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) != refbuf->len)
+    {
+        ebml_write_buf_to_file_fail(source);
+    }
+
+}
+
+
+/* internal ebml parsing */
+
+static void ebml_debug(ebml_t *ebml) {
+    printf("EBML Stream Write Position: %zu Read Position: %zu Buffer Position: %d "
+           "Cluster Position: %zu Header Read: %d Header Size: %d Header Write "
+           "Position: %d Header Read Position: %d\n",
+           ebml->position,
+           ebml->read_position,
+           ebml->buffer_position,
+           ebml->cluster_position,
+           ebml->header_read,
+           ebml->header_size,
+           ebml->header_position,
+           ebml->header_read_position);
+}
+
+
+static unsigned char ebml_get_next_match_byte(unsigned char match_byte, uint64_t position, 
+                                              uint64_t *matched_byte_num, uint64_t *found) {
+
+    if (found != NULL) {
+        *found = 0;
+    }
+
+    if (matched_byte_num != NULL) {
+        if (match_byte == EBML_CLUSTER_BYTE1) {
+            if (matched_byte_num != NULL) {
+                *matched_byte_num = position;
+            }
+            return EBML_CLUSTER_BYTE2;
+        }
+
+        if ((*matched_byte_num == position - 1) && (match_byte == EBML_CLUSTER_BYTE2)) {
+            return EBML_CLUSTER_BYTE3;
+        }
+
+        if ((*matched_byte_num == position - 2) && (match_byte == EBML_CLUSTER_BYTE3)) {
+            return EBML_CLUSTER_BYTE4;
+        }
+
+        if ((*matched_byte_num == position - 3) && (match_byte == EBML_CLUSTER_BYTE4)) {
+            if (found != NULL) {
+                *found = *matched_byte_num;
+            }
+            *matched_byte_num = 0;
+            return EBML_CLUSTER_BYTE1;
+        }
+
+        *matched_byte_num = 0;
+    }
+
+    return EBML_CLUSTER_BYTE1;
+
+}
+
+static void ebml_destroy(ebml_t *ebml) {
+
+    free(ebml->header);
+    free(ebml->input_buffer);
+    free(ebml->buffer);
+    free(ebml);
+
+}
+
+static ebml_t *ebml_create() {
+
+    ebml_t *ebml = calloc(1, sizeof(ebml_t));
+
+    ebml->header = calloc(1, EBML_HEADER_MAX_SIZE);
+    ebml->buffer = calloc(1, EBML_SLICE_SIZE);
+    ebml->input_buffer = calloc(1, EBML_SLICE_SIZE);
+
+    ebml->cluster_mark[0] = EBML_CLUSTER_BYTE1;
+    ebml->cluster_mark[1] = EBML_CLUSTER_BYTE2;
+    ebml->cluster_mark[2] = EBML_CLUSTER_BYTE3;
+    ebml->cluster_mark[3] = EBML_CLUSTER_BYTE4;
+
+    return ebml;
+
+}
+
+static size_t ebml_read_space(ebml_t *ebml) {
+
+    size_t read_space;
+
+    if (ebml->header_read == 1) {
+        read_space = (ebml->position - ebml->header_size) - ebml->read_position;
+
+        return read_space;
+    } else {
+        if (ebml->header_size != 0) {
+            return ebml->header_size - ebml->header_read_position;
+        } else {
+            return 0;
+        }
+    }
+
+
+}
+
+static int ebml_read(ebml_t *ebml, char *buffer, int len) {
+
+    size_t read_space;
+    size_t read_space_to_cluster;
+    int to_read;
+
+    read_space_to_cluster = 0;
+
+    if (len < 1) {
+        return 0;
+    }
+
+    if (ebml->header_read == 1) {
+        read_space = (ebml->position - ebml->header_size) - ebml->read_position;
+
+        if (read_space < 1) {
+            return 0;
+        }
+
+        if (read_space >= len ) {
+            to_read = len;
+        } else {
+            to_read = read_space;
+        }
+
+        if (ebml->cluster_position != 0) {
+            read_space_to_cluster = 
+            (ebml->cluster_position - ebml->header_size) - ebml->read_position;
+            if ((read_space_to_cluster != 0) && (read_space_to_cluster <= to_read)) {
+                to_read = read_space_to_cluster;
+                ebml->cluster_position = 0;
+                ebml->last_was_cluster_end = 1;
+            } else {
+                if (read_space_to_cluster == 0) {
+                    ebml->this_was_cluster_start = 1;
+                }
+            }
+        }
+
+        memcpy(buffer, ebml->buffer, to_read);
+        ebml->read_position += to_read;
+        memmove(ebml->buffer, ebml->buffer + to_read, ebml->buffer_position - to_read);
+        ebml->buffer_position -= to_read;
+
+    } else {
+        if (ebml->header_size != 0) {
+
+            read_space = ebml->header_size - ebml->header_read_position;
+
+            if (read_space >= len ) {
+                to_read = len;
+            } else {
+                to_read = read_space;
+            }
+
+            memcpy(buffer, ebml->header, to_read);
+            ebml->header_read_position += to_read;
+
+            if (ebml->header_read_position == ebml->header_size) {
+                ebml->header_read = 1;
+            }
+
+        } else {
+            return 0;
+        }
+    }
+
+
+    return to_read;
+
+}
+
+static int ebml_last_was_sync(ebml_t *ebml) {
+
+    if (ebml->last_was_cluster_end == 1) {
+        ebml->last_was_cluster_end = 0;
+        ebml->this_was_cluster_start = 1;
+    }
+
+    if (ebml->this_was_cluster_start == 1) {
+        ebml->this_was_cluster_start = 0;
+        return 1;
+    }
+
+    return 0;
+
+}
+
+static char *ebml_write_buffer(ebml_t *ebml, int len) {
+
+    return (char *)ebml->input_buffer;
+
+}
+
+
+static int ebml_wrote(ebml_t *ebml, int len) {
+
+    int b;
+
+    for (b = 0; b < len; b++) {
+        if ((ebml->input_buffer[b] == ebml->match_byte) || (ebml->matched_byte_num > 0)) {
+            ebml->match_byte = ebml_get_next_match_byte(ebml->input_buffer[b],
+                                                        ebml->position + b,
+                                                        &ebml->matched_byte_num,
+                                                        &ebml->found);
+            if (ebml->found > 0) {
+                if (ebml->header_size == 0) {
+                    if (b > 0) {
+                        if ((ebml->header_position + b) > EBML_HEADER_MAX_SIZE) {
+                            ERROR0("EBML Header to large, failing");
+                            return -1;
+                        }
+                        memcpy(ebml->header + ebml->header_position, ebml->input_buffer, b);
+                        ebml->header_position += b;
+                    }
+                    ebml->header_size = (ebml->header_position - 4) + 1;
+                    if (EBML_DEBUG) {
+                        printf("EBML: Got header %d bytes\n", ebml->header_size);
+                    }
+                    /* first cluster */
+                    memcpy(ebml->buffer, ebml->cluster_mark, 4);
+                    ebml->buffer_position += 4;
+                    if ((b + 1) < len) {
+                        if ((ebml->buffer_position + (len - (b + 1))) > EBML_SLICE_SIZE) {
+                            ERROR0("EBML Overflow, failing");
+                            return -1;
+                        }
+                        memcpy(ebml->buffer + ebml->buffer_position, 
+                               ebml->input_buffer + (b + 1),
+                               len - (b + 1));
+                        ebml->buffer_position += len - (b + 1);
+                    }
+                    if (EBML_DEBUG) {
+                        printf("EBML: Found first cluster starting at offset: %zu\n",
+                               ebml->found);
+                    }
+                    ebml->cluster_position = ebml->found;
+                    ebml->position += len;
+                    return len;
+
+                }
+                if (EBML_DEBUG) {
+                    printf("EBML: Found cluster starting at offset: %zu\n", ebml->found);
+                }
+                ebml->cluster_position = ebml->found;
+            }
+        }
+    }
+
+    if (ebml->header_size == 0) {
+        if ((ebml->header_position + len) > EBML_HEADER_MAX_SIZE) {
+            ERROR0("EBML Header to large, failing");
+            return -1;
+        }
+        if (EBML_DEBUG) {
+            printf("EBML: Adding to header, ofset is %d size is %d adding %d\n", 
+                   ebml->header_size, ebml->header_position, len);
+        }
+        memcpy(ebml->header + ebml->header_position, ebml->input_buffer, len);
+        ebml->header_position += len;
+    } else {
+        if ((ebml->buffer_position + len) > EBML_SLICE_SIZE) {
+            ERROR0("EBML Overflow, failing");
+            return -1;
+        }
+        memcpy(ebml->buffer + ebml->buffer_position, ebml->input_buffer, len);
+        ebml->buffer_position += len;
+    }
+
+    ebml->position += len;
+
+    return len;
+}
diff --git a/src/format_ebml.h b/src/format_ebml.h
new file mode 100644
index 0000000..c1d6d07
--- /dev/null
+++ b/src/format_ebml.h
@@ -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 2000-2012, Jack Moffitt <[email protected], 
+ *                      Michael Smith <[email protected]>,
+ *                      oddsock <[email protected]>,
+ *                      Karl Heyes <[email protected]>
+ *                      and others (see AUTHORS for details).
+ */
+
+/* format_ebml.h
+**
+** ebml format plugin header
+**
+*/
+#ifndef __FORMAT_EBML_H__
+#define __FORMAT_EBML_H__
+
+#include "format.h"
+
+typedef struct ebml_st ebml_t;
+typedef struct ebml_source_state_st ebml_source_state_t;
+
+struct ebml_source_state_st {
+
+    ebml_t *ebml;
+    refbuf_t *header;
+    int file_headers_written;
+
+};
+
+int format_ebml_get_plugin (source_t *source);
+
+#endif  /* __FORMAT_EBML_H__ */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.