diff --git a/configure.in b/configure.in index 103a7754..380e3fda 100644 --- a/configure.in +++ b/configure.in @@ -81,6 +81,19 @@ XIPH_PATH_SPEEX( [ AC_MSG_WARN([Speex support disabled!]) ]) +AC_CHECK_LIB(kate, kate_decode_init,[have_kate=yes],[have_kate=no], -logg) +if test "x$have_kate" == "xyes" +then + AC_CHECK_LIB(oggkate, kate_ogg_decode_headerin,[have_kate=yes],[have_kate=no],-lkate -logg) + if test "x$have_kate" == "xyes" + then + KATE_LIBS="-loggkate -lkate -logg" + AC_DEFINE([HAVE_KATE],[1],[Define if you have libkate]) + fi +fi +dnl we still use format_kate as it doesn't need libkate to work +#ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_kate.o" + ACX_PTHREAD(, AC_MSG_ERROR([POSIX threads missing])) XIPH_VAR_APPEND([XIPH_CFLAGS],[$PTHREAD_CFLAGS]) XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$PTHREAD_CPPFLAGS]) @@ -133,6 +146,8 @@ AC_SUBST(DEBUG) AC_SUBST(CFLAGS) AC_SUBST(PROFILE) AC_SUBST(ICECAST_OPTIONAL) +AC_SUBST(HAVE_KATE) +AC_SUBST(KATE_LIBS) AC_OUTPUT([Makefile conf/Makefile debian/Makefile src/Makefile src/avl/Makefile src/httpp/Makefile src/thread/Makefile src/log/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 8db2e5c3..8c2ef217 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,23 +11,24 @@ noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.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_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.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 \ - auth.c auth_htpasswd.c + auth.c auth_htpasswd.c format_kate.c format_skeleton.c EXTRA_icecast_SOURCES = yp.c \ auth_url.c \ format_vorbis.c format_theora.c format_speex.c - + icecast_DEPENDENCIES = @ICECAST_OPTIONAL@ net/libicenet.la thread/libicethread.la \ httpp/libicehttpp.la log/libicelog.la avl/libiceavl.la timing/libicetiming.la -icecast_LDADD = $(icecast_DEPENDENCIES) @XIPH_LIBS@ +icecast_LDADD = $(icecast_DEPENDENCIES) @XIPH_LIBS@ @KATE_LIBS@ AM_CFLAGS = @XIPH_CFLAGS@ AM_CPPFLAGS = @XIPH_CPPFLAGS@ -AM_LDFLAGS = @XIPH_LDFLAGS@ +AM_LDFLAGS = @XIPH_LDFLAGS@ @KATE_LIBS@ debug: diff --git a/src/format_kate.c b/src/format_kate.c new file mode 100644 index 00000000..a5ee80c4 --- /dev/null +++ b/src/format_kate.c @@ -0,0 +1,232 @@ +/* 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 , + * oddsock , + * Karl Heyes + * and others (see AUTHORS for details). + */ + + +/* Ogg codec handler for kate logical streams */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#ifdef HAVE_KATE +#include +#endif + +typedef struct source_tag source_t; + +#include "refbuf.h" +#include "format_ogg.h" +#include "format_kate.h" +#include "client.h" +#include "stats.h" + +#define CATMODULE "format-kate" +#include "logging.h" + + +typedef struct _kate_codec_tag +{ + int headers_done; +#ifdef HAVE_KATE + kate_info ki; + kate_comment kc; +#endif + int num_headers; + int granule_shift; + ogg_int64_t last_iframe; + ogg_int64_t prev_granulepos; +} kate_codec_t; + + +static void kate_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) +{ + kate_codec_t *kate = codec->specific; + + DEBUG0 ("freeing kate codec"); + /* TODO: should i replace with something or just remove + stats_event (ogg_info->mount, "video_bitrate", NULL); + stats_event (ogg_info->mount, "video_quality", NULL); + stats_event (ogg_info->mount, "frame_rate", NULL); + stats_event (ogg_info->mount, "frame_size", NULL); + */ +#ifdef HAVE_KATE + kate_info_clear (&kate->ki); + kate_comment_clear (&kate->kc); +#endif + ogg_stream_clear (&codec->os); + free (kate); + free (codec); +} + + +/* kate pages are not rebuilt, so here we just for headers and then + * pass them straight through to the the queue + */ +static refbuf_t *process_kate_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page) +{ + kate_codec_t *kate = codec->specific; + ogg_packet packet; + int header_page = 0; + refbuf_t *refbuf = NULL; + ogg_int64_t granulepos; + + if (ogg_stream_pagein (&codec->os, page) < 0) + { + ogg_info->error = 1; + return NULL; + } + granulepos = ogg_page_granulepos (page); + + while (ogg_stream_packetout (&codec->os, &packet) > 0) + { + if (!kate->headers_done) + { +#ifdef HAVE_KATE + int ret = kate_ogg_decode_headerin (&kate->ki, &kate->kc, &packet); + if (ret < 0) + { + ogg_info->error = 1; + WARN0 ("problem with kate header"); + return NULL; + } + header_page = 1; + kate->num_headers = kate->ki.num_headers; + codec->headers++; + if (ret > 0) + { + kate->headers_done = 1; + /* TODO: what to replace this with ? + ogg_info->bitrate += theora->ti.target_bitrate; + stats_event_args (ogg_info->mount, "video_bitrate", "%ld", + (long)theora->ti.target_bitrate); + stats_event_args (ogg_info->mount, "video_quality", "%ld", + (long)theora->ti.quality); + stats_event_args (ogg_info->mount, "frame_size", "%ld x %ld", + (long)theora->ti.frame_width, + (long)theora->ti.frame_height); + stats_event_args (ogg_info->mount, "frame_rate", "%.2f", + (float)theora->ti.fps_numerator/theora->ti.fps_denominator); + */ + } + continue; +#else + header_page = (packet.bytes>0 && (packet.packet[0]&0x80)); + if (!header_page) + break; + codec->headers++; + if (packet.packet[0]==0x80) + { + if (packet.bytes<64) return NULL; + /* we peek for the number of headers to expect */ + kate->num_headers = packet.packet[11]; + } + continue; +#endif + } + + if (codec->headers < kate->num_headers) + { + ogg_info->error = 1; + ERROR0 ("Not enough header packets"); + return NULL; + } + } + if (header_page) + { + format_ogg_attach_header (ogg_info, page); + return NULL; + } + + refbuf = make_refbuf_with_page (page); + /* DEBUG3 ("refbuf %p has pageno %ld, %llu", refbuf, ogg_page_pageno (page), (uint64_t)granulepos); */ + + if (codec->possible_start) + { + /* we don't bother trying to know where we can start, we'll just + start whenever we have to, video's more important and in the majority + of the cases it's ok if we lose an event we're seeking in the middle + of, as we won't have display artifacts as we'd have with video */ + codec->possible_start->sync_point = 1; + refbuf_release (codec->possible_start); + codec->possible_start = NULL; + } + if (granulepos != kate->prev_granulepos || granulepos == 0) + { + if (codec->possible_start) + refbuf_release (codec->possible_start); + refbuf_addref (refbuf); + codec->possible_start = refbuf; + } + kate->prev_granulepos = granulepos; + + return refbuf; +} + + +/* Check if specified BOS page is the start of a kate stream and + * if so, create a codec structure for handling it + */ +ogg_codec_t *initial_kate_page (format_plugin_t *plugin, ogg_page *page) +{ + ogg_state_t *ogg_info = plugin->_state; + ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t)); + ogg_packet packet; + + kate_codec_t *kate_codec = calloc (1, sizeof (kate_codec_t)); + + ogg_stream_init (&codec->os, ogg_page_serialno (page)); + ogg_stream_pagein (&codec->os, page); + +#ifdef HAVE_KATE + kate_info_init (&kate_codec->ki); + kate_comment_init (&kate_codec->kc); +#endif + + ogg_stream_packetout (&codec->os, &packet); + + DEBUG0("checking for kate codec"); +#ifdef HAVE_KATE + if (kate_ogg_decode_headerin (&kate_codec->ki, &kate_codec->kc, &packet) < 0) + { + kate_info_clear (&kate_codec->ki); + kate_comment_clear (&kate_codec->kc); + ogg_stream_clear (&codec->os); + free (kate_codec); + free (codec); + return NULL; + } +#else + /* we don't have libkate, so we examine the packet magic by hand */ + if ((packet.bytes<9) || memcmp(packet.packet, "\x80kate\0\0\0\0", 9)) + { + ogg_stream_clear (&codec->os); + free (kate_codec); + free (codec); + return NULL; + } +#endif + + INFO0 ("seen initial kate header"); + codec->specific = kate_codec; + codec->process_page = process_kate_page; + codec->codec_free = kate_codec_free; + codec->headers = 1; + codec->name = "Kate"; + + format_ogg_attach_header (ogg_info, page); + ogg_info->codec_sync = codec; + return codec; +} + diff --git a/src/format_kate.h b/src/format_kate.h new file mode 100644 index 00000000..16db4223 --- /dev/null +++ b/src/format_kate.h @@ -0,0 +1,21 @@ +/* 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 , + * oddsock , + * Karl Heyes + * and others (see AUTHORS for details). + */ + + +#ifndef __FORMAT_KATE_H +#define __FORMAT_KATE_H + +#include "format_ogg.h" + +ogg_codec_t *initial_kate_page (format_plugin_t *plugin, ogg_page *page); + +#endif /* __FORMAT_KATE_H */ diff --git a/src/format_ogg.c b/src/format_ogg.c index 0b490c89..42b9cee2 100644 --- a/src/format_ogg.c +++ b/src/format_ogg.c @@ -42,6 +42,8 @@ #endif #include "format_midi.h" #include "format_flac.h" +#include "format_kate.h" +#include "format_skeleton.h" #ifdef _WIN32 #define snprintf _snprintf @@ -237,6 +239,12 @@ static int process_initial_page (format_plugin_t *plugin, ogg_page *page) if (codec) break; #endif + codec = initial_kate_page (plugin, page); + if (codec) + break; + codec = initial_skeleton_page (plugin, page); + if (codec) + break; /* any others */ ERROR0 ("Seen BOS page with unknown type"); diff --git a/src/format_skeleton.c b/src/format_skeleton.c new file mode 100644 index 00000000..a0b81c12 --- /dev/null +++ b/src/format_skeleton.c @@ -0,0 +1,114 @@ +/* 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 , + * oddsock , + * Karl Heyes + * and others (see AUTHORS for details). + */ + + +/* Ogg codec handler for skeleton logical streams */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +typedef struct source_tag source_t; + +#include "refbuf.h" +#include "format_ogg.h" +#include "format_skeleton.h" +#include "client.h" +#include "stats.h" + +#define CATMODULE "format-skeleton" +#include "logging.h" + + +typedef struct _skeleton_codec_tag +{ +} skeleton_codec_t; + + +static void skeleton_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) +{ + skeleton_codec_t *skeleton = codec->specific; + + DEBUG0 ("freeing skeleton codec"); + ogg_stream_clear (&codec->os); + free (skeleton); + free (codec); +} + + +/* skeleton pages are not rebuilt, so here we just for headers and then + * pass them straight through to the the queue + */ +static refbuf_t *process_skeleton_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page) +{ + ogg_packet packet; + + if (ogg_stream_pagein (&codec->os, page) < 0) + { + ogg_info->error = 1; + return NULL; + } + + while (ogg_stream_packetout (&codec->os, &packet) > 0) + { + codec->headers++; + } + + /* all skeleon packets are headers */ + format_ogg_attach_header (ogg_info, page); + return NULL; +} + + +/* Check if specified BOS page is the start of a skeleton stream and + * if so, create a codec structure for handling it + */ +ogg_codec_t *initial_skeleton_page (format_plugin_t *plugin, ogg_page *page) +{ + ogg_state_t *ogg_info = plugin->_state; + ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t)); + ogg_packet packet; + + skeleton_codec_t *skeleton_codec = calloc (1, sizeof (skeleton_codec_t)); + + ogg_stream_init (&codec->os, ogg_page_serialno (page)); + ogg_stream_pagein (&codec->os, page); + + + ogg_stream_packetout (&codec->os, &packet); + + DEBUG0("checking for skeleton codec"); + + if ((packet.bytes<8) || memcmp(packet.packet, "fishead\0", 8)) + { + ogg_stream_clear (&codec->os); + free (skeleton_codec); + free (codec); + return NULL; + } + + INFO0 ("seen initial skeleton header"); + codec->specific = skeleton_codec; + codec->process_page = process_skeleton_page; + codec->codec_free = skeleton_codec_free; + codec->headers = 1; + codec->name = "Skeleton"; + + format_ogg_attach_header (ogg_info, page); + ogg_info->codec_sync = codec; + return codec; +} + diff --git a/src/format_skeleton.h b/src/format_skeleton.h new file mode 100644 index 00000000..15bf7a4c --- /dev/null +++ b/src/format_skeleton.h @@ -0,0 +1,21 @@ +/* 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 , + * oddsock , + * Karl Heyes + * and others (see AUTHORS for details). + */ + + +#ifndef __FORMAT_SKELETON_H +#define __FORMAT_SKELETON_H + +#include "format_ogg.h" + +ogg_codec_t *initial_skeleton_page (format_plugin_t *plugin, ogg_page *page); + +#endif /* __FORMAT_SKELETON_H */