Icecast WebM support patch
Krad Radio <[email protected]>
| Newsgroups | gmane.comp.audio.icecast.devel,gmane.comp.audio.icecast.general |
|---|---|
| Message-ID | <CA+8JHGh7-h_PREprpak6OAitnVoXADfQhGtSYM+Vv3KK0j0tYg@mail.gmail.com> |
Attached is a patch for adding webm streaming support to icecast svn. Also available is my git tree: https://github.com/krad-radio/icecast-oneman/tree/format_ebml_internal My git tree is behind the svn version a little bit, I will update it soon, the webm code is identical. Please note the following: 1) This version has no external dependencies, the EBML parsing is built into the format_ebml plugin. In the future this will most likely be an external library, that's the plan, but my goal is to satisfy whatever the requirements are, and also provide easy access to working webm streaming as soon as possible. 2) Your queue size setting most likely needs to be increased. WebM streaming is dependent on starting on a cluster of a known size (after the header), if a cluster is larger than your queue then icecast will drop it. This is the same idea as Ogg pages. This limitation is due to the existing code in media player clients, not the format itself. On my test server I use the following: <queue-size>5000000</queue-size> <burst-size>512000</burst-size> We may want to add a separate setting for burst/queue size for ebml based streams since the bitrate can be 50-100X higher than audio. We will figure out the best course of action here as more people start testing and using this. 3) I will be providing binaries of my V4L2 source client very soon, sources are available now but are tricky to compile at the moment. At that time I'll also provide an open public testing server. Enjoy! -David _______________________________________________ Icecast-dev mailing list [email protected] http://lists.xiph.org/mailman/listinfo/icecast-dev
icecast-webm-support.patch
(text/x-patch, 17.1 KB)
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..d3ed598 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,18 @@ 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/x-krad-opus") == 0)
+ return FORMAT_TYPE_EBML;
+ 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 +91,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..bc94aff
--- /dev/null
+++ b/src/format_ebml.c
@@ -0,0 +1,483 @@
+/* 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-2004, 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"
+
+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);
+
+typedef struct ebml_client_data_St ebml_client_data_t;
+
+struct ebml_client_data_St {
+
+ refbuf_t *header;
+ int header_pos;
+
+};
+
+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 = krad_ebml_create_feedbuffer();
+ 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);
+ krad_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 = 4096;
+ 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;
+
+ while (1)
+ {
+
+ if ((bytes = krad_ebml_read_space(ebml_source_state->ebml)) > 0)
+ {
+ refbuf = refbuf_new(bytes);
+ krad_ebml_read(ebml_source_state->ebml, refbuf->data, bytes);
+
+ if (ebml_source_state->header == NULL)
+ {
+ ebml_source_state->header = refbuf;
+ continue;
+ }
+
+ if (krad_ebml_last_was_sync(ebml_source_state->ebml))
+ {
+ refbuf->sync_point = 1;
+ }
+ return refbuf;
+
+ }
+ else
+ {
+
+ data = krad_ebml_write_buffer(ebml_source_state->ebml, 4096);
+ bytes = client_read_bytes (source->client, data, 4096);
+ if (bytes <= 0)
+ {
+ krad_ebml_wrote (ebml_source_state->ebml, 0);
+ return NULL;
+ }
+ format->read_bytes += bytes;
+ krad_ebml_wrote (ebml_source_state->ebml, bytes);
+ }
+ }
+}
+
+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_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 mini krad ebml parsing */
+
+unsigned char krad_ebml_get_next_match_byte(unsigned char match_byte, uint64_t position, uint64_t *matched_byte_num, uint64_t *winner) {
+
+ if (winner != NULL) {
+ *winner = 0;
+ }
+
+ if (matched_byte_num != NULL) {
+ if (match_byte == KRAD_EBML_CLUSTER_BYTE1) {
+ if (matched_byte_num != NULL) {
+ *matched_byte_num = position;
+ }
+ return KRAD_EBML_CLUSTER_BYTE2;
+ }
+
+ if ((*matched_byte_num == position - 1) && (match_byte == KRAD_EBML_CLUSTER_BYTE2)) {
+ return KRAD_EBML_CLUSTER_BYTE3;
+ }
+
+ if ((*matched_byte_num == position - 2) && (match_byte == KRAD_EBML_CLUSTER_BYTE3)) {
+ return KRAD_EBML_CLUSTER_BYTE4;
+ }
+
+ if ((*matched_byte_num == position - 3) && (match_byte == KRAD_EBML_CLUSTER_BYTE4)) {
+ if (winner != NULL) {
+ *winner = *matched_byte_num;
+ }
+ *matched_byte_num = 0;
+ return KRAD_EBML_CLUSTER_BYTE1;
+ }
+
+ *matched_byte_num = 0;
+ }
+
+ return KRAD_EBML_CLUSTER_BYTE1;
+
+}
+
+void krad_ebml_destroy(krad_ebml_t *krad_ebml) {
+
+ free(krad_ebml->buffer);
+ free(krad_ebml->header);
+ free(krad_ebml);
+
+}
+
+krad_ebml_t *krad_ebml_create_feedbuffer() {
+
+ krad_ebml_t *krad_ebml = calloc(1, sizeof(krad_ebml_t));
+
+ krad_ebml->buffer = calloc(1, KRAD_EBML_BUFFER_SIZE);
+ krad_ebml->header = calloc(1, KRAD_EBML_HEADER_MAX_SIZE);
+
+
+ krad_ebml->cluster_mark[0] = KRAD_EBML_CLUSTER_BYTE1;
+ krad_ebml->cluster_mark[1] = KRAD_EBML_CLUSTER_BYTE2;
+ krad_ebml->cluster_mark[2] = KRAD_EBML_CLUSTER_BYTE3;
+ krad_ebml->cluster_mark[3] = KRAD_EBML_CLUSTER_BYTE4;
+
+ return krad_ebml;
+
+}
+
+size_t krad_ebml_read_space(krad_ebml_t *krad_ebml) {
+
+ size_t read_space;
+
+ if (krad_ebml->header_read == 1) {
+ read_space = (krad_ebml->position - krad_ebml->header_size) - krad_ebml->read_position;
+
+ return read_space;
+ } else {
+ if (krad_ebml->header_size != 0) {
+ return krad_ebml->header_size - krad_ebml->header_read_position;
+ } else {
+ return 0;
+ }
+ }
+
+
+}
+
+int krad_ebml_read(krad_ebml_t *krad_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 (krad_ebml->header_read == 1) {
+ read_space = (krad_ebml->position - krad_ebml->header_size) - krad_ebml->read_position;
+
+ if (read_space < 1) {
+ return 0;
+ }
+
+ if (read_space >= len ) {
+ to_read = len;
+ } else {
+ to_read = read_space;
+ }
+
+ if (krad_ebml->cluster_position != 0) {
+ read_space_to_cluster = (krad_ebml->cluster_position - krad_ebml->header_size) - krad_ebml->read_position;
+ if ((read_space_to_cluster != 0) && (read_space_to_cluster <= to_read)) {
+ to_read = read_space_to_cluster;
+ krad_ebml->cluster_position = 0;
+ krad_ebml->last_was_cluster_end = 1;
+ } else {
+ if (read_space_to_cluster == 0) {
+ krad_ebml->this_was_cluster_start = 1;
+ }
+ }
+ }
+
+ memcpy(buffer, krad_ebml->buffer, to_read);
+ krad_ebml->read_position += to_read;
+
+
+ memmove(krad_ebml->buffer, krad_ebml->buffer + to_read, krad_ebml->buffer_position_internal - to_read);
+ krad_ebml->buffer_position_internal -= to_read;
+
+ } else {
+ if (krad_ebml->header_size != 0) {
+
+ read_space = krad_ebml->header_size - krad_ebml->header_read_position;
+
+ if (read_space >= len ) {
+ to_read = len;
+ } else {
+ to_read = read_space;
+ }
+
+ memcpy(buffer, krad_ebml->header, to_read);
+ krad_ebml->header_read_position += to_read;
+
+ if (krad_ebml->header_read_position == krad_ebml->header_size) {
+ krad_ebml->header_read = 1;
+ }
+
+ } else {
+ return 0;
+ }
+ }
+
+
+ return to_read;
+
+}
+
+int krad_ebml_last_was_sync(krad_ebml_t *krad_ebml) {
+
+ if (krad_ebml->last_was_cluster_end == 1) {
+ krad_ebml->last_was_cluster_end = 0;
+ krad_ebml->this_was_cluster_start = 1;
+ }
+
+ if (krad_ebml->this_was_cluster_start == 1) {
+ krad_ebml->this_was_cluster_start = 0;
+ return 1;
+ }
+
+ return 0;
+
+}
+
+char *krad_ebml_write_buffer(krad_ebml_t *krad_ebml, int len) {
+
+ krad_ebml->input_buffer = malloc(len);
+
+ return (char *)krad_ebml->input_buffer;
+
+}
+
+
+int krad_ebml_wrote(krad_ebml_t *krad_ebml, int len) {
+
+ int b;
+
+ for (b = 0; b < len; b++) {
+ if ((krad_ebml->input_buffer[b] == krad_ebml->match_byte) || (krad_ebml->matched_byte_num > 0)) {
+ krad_ebml->match_byte = krad_ebml_get_next_match_byte(krad_ebml->input_buffer[b], krad_ebml->position + b,
+ &krad_ebml->matched_byte_num, &krad_ebml->found);
+ if (krad_ebml->found > 0) {
+ if (krad_ebml->header_size == 0) {
+ if (b > 0) {
+ memcpy(krad_ebml->header + krad_ebml->header_position, krad_ebml->input_buffer, b);
+ krad_ebml->header_position += b;
+ }
+ krad_ebml->header_size = (krad_ebml->header_position - 4) + 1;
+ if (KRAD_EBML_DEBUG) {
+ printf("\ngot header, size is %d\n", krad_ebml->header_size);
+ }
+ /* first cluster */
+ memcpy(krad_ebml->buffer, krad_ebml->cluster_mark, 4);
+ krad_ebml->buffer_position_internal += 4;
+ if ((b + 1) < len) {
+ memcpy(krad_ebml->buffer + krad_ebml->buffer_position_internal,
+ krad_ebml->input_buffer + (b + 1),
+ len - (b + 1));
+ krad_ebml->buffer_position_internal += len - (b + 1);
+ }
+ if (KRAD_EBML_DEBUG) {
+ printf("\nfound first cluster starting at %zu\n", krad_ebml->found);
+ }
+ krad_ebml->cluster_position = krad_ebml->found;
+ krad_ebml->position += len;
+ free(krad_ebml->input_buffer);
+ return len;
+
+ }
+ if (KRAD_EBML_DEBUG) {
+ printf("\nfound cluster starting at %zu\n", krad_ebml->found);
+ }
+ krad_ebml->cluster_position = krad_ebml->found;
+ }
+ }
+ }
+
+ if (krad_ebml->header_size == 0) {
+ if (KRAD_EBML_DEBUG) {
+ printf("\nadding to header header, pos is %d size is %d adding %d\n",
+ krad_ebml->header_size, krad_ebml->header_position, len);
+ }
+ memcpy(krad_ebml->header + krad_ebml->header_position, krad_ebml->input_buffer, len);
+ krad_ebml->header_position += len;
+ } else {
+
+ memcpy(krad_ebml->buffer + krad_ebml->buffer_position_internal, krad_ebml->input_buffer, len);
+ krad_ebml->buffer_position_internal += len;
+ }
+
+ krad_ebml->position += len;
+
+ free(krad_ebml->input_buffer);
+
+ return len;
+}
+
+
diff --git a/src/format_ebml.h b/src/format_ebml.h
new file mode 100644
index 0000000..f5e886c
--- /dev/null
+++ b/src/format_ebml.h
@@ -0,0 +1,91 @@
+/* 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-2004, 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 krad_ebml_St krad_ebml_t;
+typedef struct ebml_source_state_St ebml_source_state_t;
+
+struct ebml_source_state_St {
+
+ krad_ebml_t *ebml;
+ refbuf_t *header;
+ int file_headers_written;
+
+};
+
+int format_ebml_get_plugin (source_t *source);
+
+/* internal mini krad ebml parsing */
+
+#define KRAD_EBML_DEBUG 0
+#define KRAD_EBML_HEADER_MAX_SIZE 8192 * 4
+#define KRAD_EBML_BUFFER_SIZE 8192 * 512
+
+#define KRAD_EBML_CLUSTER_BYTE1 0x1F
+#define KRAD_EBML_CLUSTER_BYTE2 0x43
+#define KRAD_EBML_CLUSTER_BYTE3 0xB6
+#define KRAD_EBML_CLUSTER_BYTE4 0x75
+
+struct krad_ebml_St {
+
+ char cluster_mark[4];
+ int cluster_count;
+
+ int ret;
+ int b;
+ uint64_t position;
+ uint64_t read_position;
+
+ int buffer_position_internal;
+ 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 endcut;
+
+ int last_was_cluster_end;
+ int this_was_cluster_start;
+
+};
+
+
+void krad_ebml_destroy(krad_ebml_t *krad_ebml);
+krad_ebml_t *krad_ebml_create_feedbuffer();
+size_t krad_ebml_read_space(krad_ebml_t *krad_ebml);
+int krad_ebml_read(krad_ebml_t *krad_ebml, char *buffer, int len);
+int krad_ebml_last_was_sync(krad_ebml_t *krad_ebml);
+char *krad_ebml_write_buffer(krad_ebml_t *krad_ebml, int len);
+int krad_ebml_wrote(krad_ebml_t *krad_ebml, int len);
+unsigned char krad_ebml_get_next_match_byte(unsigned char match_byte, uint64_t position, uint64_t *matched_byte_num, uint64_t *winner);
+
+
+
+#endif /* __FORMAT_EBML_H__ */