|
View:
New views
1 Messages
—
Rating Filter:
Alert me
|
|
|
Icecast WebM support patch, Version 2Rillian was right, the match byte function was not only hard to read, but also flawed. ;P
The concept of simply splitting on cluster boundaries is as sound as it was last week, but it is none the less,
highly coupled to actually having your code split on cluster boundaries more than just most of the time. I've reworked my patch, and I have been able to stream for over 30 hours continuously with gstreamer, and also with krad link.
In the last hour, I cleaned up the white-space and style issues, hopefully without accidentally changing how the code works. I'm not going to make any claims, other than this is version 2, until further testing is done.
Our intention is to move to full parsing, in order to enable a rich metadata experience. Currently essentially no-metadata is even possible, as including icy- http headers is likely to confuse client software. Tags are a top level element in WebM and are arbitrary as Vorbis tags are,
with a few conventions there are lots of possibilities here. Cheers, David [icecast-webm-support-version2.patch] diff --git a/AUTHORS b/AUTHORS index 0d4abc0..b063deb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,3 +4,4 @@ oddsock <oddsock@...> Karl Heyes <karl@...> Philipp "ph3-der-loewe" Schafft <lion@...> Thomas B. "dm8tbr" Ruecker <thomas.rucker@...> +David "oneman" Richards <kradradio@...> 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..e0bbe83 --- /dev/null +++ b/src/format_ebml.c @@ -0,0 +1,470 @@ +/* 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 <jack@..., + * Michael Smith <msmith@...>, + * oddsock <oddsock@...>, + * Karl Heyes <karl@...> + * 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" + +#define CATMODULE "format-ebml" + +#include "logging.h" + +#define EBML_DEBUG 0 +#define EBML_HEADER_MAX_SIZE 131072 +#define EBML_SLICE_SIZE 4096 + + +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_id; + int cluster_start; + + int position; + unsigned char *input_buffer; + unsigned char *buffer; + + int header_read; + int header_size; + int header_position; + int header_read_position; + unsigned char *header; + +}; + +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 int 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); + +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 ((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_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 * 4); + ebml->input_buffer = calloc(1, EBML_SLICE_SIZE); + + ebml->cluster_id = "\x1F\x43\xB6\x75"; + + ebml->cluster_start = -2; + + return ebml; + +} + +static int ebml_read_space(ebml_t *ebml) +{ + + int read_space; + + if (ebml->header_read == 1) + { + if (ebml->cluster_start > 0) + read_space = ebml->cluster_start; + else + read_space = ebml->position - 4; + + return read_space; + } + else + { + if (ebml->header_size != 0) + return ebml->header_size; + else + return 0; + } + +} + +static int ebml_read(ebml_t *ebml, char *buffer, int len) +{ + + int read_space; + int to_read; + + if (len < 1) + return 0; + + if (ebml->header_read == 1) + { + if (ebml->cluster_start > 0) + read_space = ebml->cluster_start; + else + read_space = ebml->position - 4; + + if (read_space < 1) + return 0; + + if (read_space >= len ) + to_read = len; + else + to_read = read_space; + + memcpy(buffer, ebml->buffer, to_read); + memmove(ebml->buffer, ebml->buffer + to_read, ebml->position - to_read); + ebml->position -= to_read; + + if (ebml->cluster_start > 0) + ebml->cluster_start -= 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->cluster_start == 0) + { + ebml->cluster_start -= 1; + return 0; + } + + if (ebml->cluster_start == -1) + { + ebml->cluster_start -= 1; + 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; + + if (ebml->header_size == 0) + { + if ((ebml->header_position + len) > EBML_HEADER_MAX_SIZE) + { + ERROR0("EBML Header too 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 + { + memcpy(ebml->buffer + ebml->position, ebml->input_buffer, len); + } + + for (b = 0; b < len - 4; b++) + { + if (!memcmp(ebml->input_buffer + b, ebml->cluster_id, 4)) + { + if (EBML_DEBUG) + { + printf("EBML: found cluster\n"); + } + + if (ebml->header_size == 0) + { + ebml->header_size = ebml->header_position - len + b; + memcpy(ebml->buffer, ebml->input_buffer + b, len - b); + ebml->position = len - b; + ebml->cluster_start = -1; + return len; + } + else + { + ebml->cluster_start = ebml->position + b; + } + } + } + + 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 <jack@..., + * Michael Smith <msmith@...>, + * oddsock <oddsock@...>, + * Karl Heyes <karl@...> + * 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__ */ _______________________________________________ Icecast-dev mailing list Icecast-dev@... http://lists.xiph.org/mailman/listinfo/icecast-dev |
| Free embeddable forum powered by Nabble | Forum Help |