Commit 2bd23d90 authored by Karl Heyes's avatar Karl Heyes

merge multi ogg codec handling. Handle theora and/or vorbis. Place new

clients before keyframe. For vorbis-only streams, perform rebuild to
flush pages more frequently and to provide url updating mechanism for
titles 

svn path=/icecast/trunk/icecast/; revision=8341
parent 82497129
......@@ -73,9 +73,23 @@ XIPH_PATH_XSLT
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$XSLT_CFLAGS])
XIPH_VAR_PREPEND([XIPH_LIBS],[$XSLT_LIBS])
XIPH_PATH_VORBIS(, AC_MSG_ERROR([must have Ogg Vorbis v1.0 or above installed!]))
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$VORBIS_CFLAGS])
XIPH_VAR_PREPEND([XIPH_LIBS],[$VORBIS_LIBS])
XIPH_PATH_VORBIS([
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$VORBIS_CFLAGS])
XIPH_VAR_PREPEND([XIPH_LIBS],[$VORBIS_LIBS])
XIPH_VAR_APPEND([XIPH_LDFLAGS],[$VORBIS_LDFLAGS])
ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_vorbis.o"
],
[AC_MSG_ERROR([must have Ogg Vorbis v1.0 or above installed])
])
XIPH_PATH_THEORA([
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$THEORA_CFLAGS])
XIPH_VAR_APPEND([XIPH_LDFLAGS],[$THEORA_LDFLAGS])
XIPH_VAR_PREPEND([XIPH_LIBS],[$THEORA_LIBS])
ICECAST_OPTIONAL="$ICECAST_OPTIONAL format_theora.o"
],
[ AC_MSG_WARN([Theora disabled!])
])
ACX_PTHREAD(, AC_MSG_ERROR([POSIX threads missing]))
XIPH_VAR_APPEND([XIPH_CFLAGS],[$PTHREAD_CFLAGS])
......@@ -109,6 +123,7 @@ dnl Make substitutions
AC_SUBST(XIPH_CPPFLAGS)
AC_SUBST(XIPH_CFLAGS)
AC_SUBST(XIPH_LIBS)
AC_SUBST(XIPH_LDFLAGS)
AC_SUBST(PTHREAD_CPPFLAGS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_LIBS)
......
......@@ -6,13 +6,16 @@ SUBDIRS = avl thread httpp net log timing
bin_PROGRAMS = icecast
noinst_HEADERS = admin.h cfgfile.h os.h logging.h sighandler.h connection.h global.h\
util.h slave.h source.h stats.h refbuf.h client.h format.h format_vorbis.h\
compat.h format_mp3.h fserve.h xslt.h yp.h event.h auth.h md5.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 format.c format_vorbis.c\
format_mp3.c xslt.c fserve.c event.c admin.c auth.c md5.c
EXTRA_icecast_SOURCES = yp.c
noinst_HEADERS = admin.h cfgfile.h os.h logging.h sighandler.h connection.h \
global.h util.h slave.h source.h stats.h refbuf.h client.h format.h \
compat.h format_mp3.h fserve.h xslt.h yp.h event.h md5.h \
auth.h format_ogg.h \
format_vorbis.h format_theora.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 format.c format_ogg.c \
format_mp3.c xslt.c fserve.c event.c admin.c auth.c md5.c
EXTRA_icecast_SOURCES = yp.c \
format_vorbis.c format_theora.c
icecast_DEPENDENCIES = @ICECAST_OPTIONAL@ net/libicenet.la thread/libicethread.la \
httpp/libicehttpp.la log/libicelog.la avl/libiceavl.la timing/libicetiming.la
......@@ -20,6 +23,7 @@ icecast_LDADD = $(icecast_DEPENDENCIES) @XIPH_LIBS@
AM_CFLAGS = @XIPH_CFLAGS@
AM_CPPFLAGS = @XIPH_CPPFLAGS@
AM_LDFLAGS = @XIPH_LDFLAGS@
debug:
......
......@@ -825,18 +825,15 @@ static void command_fallback(client_t *client, source_t *source,
static void command_metadata(client_t *client, source_t *source)
{
char *action;
char *value;
mp3_state *state;
char *song, *title, *artist;
format_plugin_t *plugin;
DEBUG0("Got metadata update request");
COMMAND_REQUIRE(client, "mode", action);
COMMAND_REQUIRE(client, "song", value);
if (source->format->type == FORMAT_TYPE_VORBIS) {
client_send_400 (client, "Cannot update metadata on vorbis streams");
return;
}
COMMAND_OPTIONAL(client, "song", song);
COMMAND_OPTIONAL(client, "title", title);
COMMAND_OPTIONAL(client, "artist", artist);
if (strcmp (action, "updinfo") != 0)
{
......@@ -844,22 +841,32 @@ static void command_metadata(client_t *client, source_t *source)
return;
}
state = source->format->_state;
mp3_set_tag (source->format, "title", value);
DEBUG2("Metadata on mountpoint %s changed to \"%s\"",
source->mount, value);
stats_event(source->mount, "title", value);
plugin = source->format;
/* At this point, we assume that the metadata passed in
is encoded in UTF-8 */
logging_playlist(source->mount, value, source->listeners);
/* If we get an update on the mountpoint, force a
yp touch */
yp_touch (source->mount);
if (plugin && plugin->set_tag)
{
if (song)
{
plugin->set_tag (plugin, "song", song);
DEBUG2("Metadata on mountpoint %s changed to \"%s\"", source->mount, song);
}
else
{
if (artist && title)
{
plugin->set_tag (plugin, "title", title);
plugin->set_tag (plugin, "artist", artist);
INFO3("Metadata on mountpoint %s changed to \"%s - %s\"",
source->mount, artist, title);
}
}
html_success(client, "Metadata update successful");
html_success(client, "Metadata update successful");
}
else
{
client_send_400 (client, "mountpoint will not accept URL updates");
}
}
static void command_shoutcast_metadata(client_t *client, source_t *source)
......@@ -873,7 +880,7 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
COMMAND_REQUIRE(client, "mode", action);
COMMAND_REQUIRE(client, "song", value);
if (source->format->type == FORMAT_TYPE_VORBIS) {
if (source->format->type == FORMAT_TYPE_OGG) {
client_send_400 (client, "Cannot update metadata on vorbis streams");
return;
}
......@@ -890,11 +897,7 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
DEBUG2("Metadata on mountpoint %s changed to \"%s\"",
source->mount, value);
stats_event(source->mount, "title", value);
/* If we get an update on the mountpoint, force a
yp touch */
yp_touch (source->mount);
html_success(client, "Metadata update successful");
}
......
......@@ -51,9 +51,9 @@
format_type_t format_get_type(char *contenttype)
{
if(strcmp(contenttype, "application/x-ogg") == 0)
return FORMAT_TYPE_VORBIS; /* Backwards compatibility */
return FORMAT_TYPE_OGG; /* Backwards compatibility */
else if(strcmp(contenttype, "application/ogg") == 0)
return FORMAT_TYPE_VORBIS; /* Now blessed by IANA */
return FORMAT_TYPE_OGG; /* Now blessed by IANA */
else
/* We default to the Generic format handler, which
can handle many more formats than just mp3 */
......@@ -65,8 +65,8 @@ int format_get_plugin(format_type_t type, source_t *source)
int ret = -1;
switch (type) {
case FORMAT_TYPE_VORBIS:
ret = format_vorbis_get_plugin (source);
case FORMAT_TYPE_OGG:
ret = format_ogg_get_plugin (source);
break;
case FORMAT_TYPE_GENERIC:
ret = format_mp3_get_plugin (source);
......
......@@ -26,9 +26,9 @@ struct source_tag;
typedef enum _format_type_tag
{
FORMAT_TYPE_VORBIS,
FORMAT_TYPE_GENERIC,
FORMAT_ERROR /* No format, source not processable */
FORMAT_ERROR, /* No format, source not processable */
FORMAT_TYPE_OGG,
FORMAT_TYPE_GENERIC
} format_type_t;
typedef struct _format_plugin_tag
......@@ -46,6 +46,7 @@ typedef struct _format_plugin_tag
int (*create_client_data)(struct source_tag *source, client_t *client);
void (*client_send_headers)(struct _format_plugin_tag *format,
struct source_tag *source, client_t *client);
void (*set_tag)(struct _format_plugin_tag *plugin, char *tag, char *value);
void (*free_plugin)(struct _format_plugin_tag *self);
/* for internal state management */
......
......@@ -80,7 +80,7 @@ int format_mp3_get_plugin (source_t *source)
mp3_state *state = calloc(1, sizeof(mp3_state));
refbuf_t *meta;
plugin = (format_plugin_t *)malloc(sizeof(format_plugin_t));
plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
plugin->type = FORMAT_TYPE_GENERIC;
plugin->get_buffer = mp3_get_no_meta;
......@@ -89,6 +89,7 @@ int format_mp3_get_plugin (source_t *source)
plugin->create_client_data = format_mp3_create_client_data;
plugin->client_send_headers = format_mp3_send_headers;
plugin->free_plugin = format_mp3_free_plugin;
plugin->set_tag = mp3_set_tag;
plugin->contenttype = httpp_getvar (source->parser, "content-type");
if (plugin->contenttype == NULL) {
......@@ -179,6 +180,7 @@ static void filter_shoutcast_metadata (source_t *source, char *metadata, unsigne
if (p)
{
memcpy (p, metadata+13, len);
logging_playlist (source->mount, p, source->listeners);
stats_event (source->mount, "title", p);
yp_touch (source->mount);
free (p);
......@@ -421,6 +423,7 @@ static refbuf_t *mp3_get_no_meta (source_t *source)
refbuf->len = bytes;
refbuf->associated = source_mp3->metadata;
refbuf_addref (source_mp3->metadata);
refbuf->sync_point = 1;
return refbuf;
}
refbuf_release (refbuf);
......@@ -561,6 +564,7 @@ static refbuf_t *mp3_get_filter_meta (source_t *source)
}
refbuf->associated = source_mp3->metadata;
refbuf_addref (source_mp3->metadata);
refbuf->sync_point = 1;
return refbuf;
}
......
This diff is collapsed.
/* 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 <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
*/
/* format_ogg.h
**
** vorbis format plugin header
**
*/
#ifndef __FORMAT_OGG_H__
#define __FORMAT_OGG_H__
#include <ogg/ogg.h>
#include "refbuf.h"
#include "format.h"
typedef struct ogg_state_tag
{
char *mount;
ogg_sync_state oy;
int error;
struct ogg_codec_tag *codecs;
char *artist;
char *title;
int log_metadata;
refbuf_t *file_headers;
refbuf_t *header_pages;
refbuf_t *header_pages_tail;
refbuf_t **bos_end;
int bos_completed;
long bitrate;
struct ogg_codec_tag *current;
struct ogg_codec_tag *codec_sync;
} ogg_state_t;
/* per codec/logical structure */
typedef struct ogg_codec_tag
{
struct ogg_codec_tag *next;
ogg_stream_state os;
unsigned headers;
void *specific;
refbuf_t *possible_start;
refbuf_t *page;
refbuf_t *(*process)(ogg_state_t *ogg_info, struct ogg_codec_tag *codec);
refbuf_t *(*process_page)(ogg_state_t *ogg_info,
struct ogg_codec_tag *codec, ogg_page *page);
void (*codec_free)(ogg_state_t *ogg_info, struct ogg_codec_tag *codec);
} ogg_codec_t;
refbuf_t *make_refbuf_with_page (ogg_page *page);
void format_ogg_attach_header (ogg_state_t *ogg_info, ogg_page *page);
void format_ogg_free_headers (ogg_state_t *ogg_info);
int format_ogg_get_plugin (source_t *source);
#endif /* __FORMAT_OGG_H__ */
/* 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 <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
*/
/* Ogg codec handler for theora logical streams */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <ogg/ogg.h>
#include <theora/theora.h>
typedef struct source_tag source_t;
#include "refbuf.h"
#include "format_ogg.h"
#include "format_theora.h"
#include "client.h"
#include "stats.h"
#define CATMODULE "format-theora"
#include "logging.h"
typedef struct _theora_codec_tag
{
theora_info ti;
theora_comment tc;
int granule_shift;
ogg_int64_t last_iframe;
ogg_int64_t prev_granulepos;
} theora_codec_t;
static void theora_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
{
theora_codec_t *theora = codec->specific;
DEBUG0 ("freeing theora codec");
stats_event (ogg_info->mount, "video_bitrate", NULL);
stats_event (ogg_info->mount, "framerate", NULL);
stats_event (ogg_info->mount, "frame_size", NULL);
theora_info_clear (&theora->ti);
theora_comment_clear (&theora->tc);
ogg_stream_clear (&codec->os);
free (theora);
free (codec);
}
/* theora pages are not rebuilt, so here we just for headers and then
* pass them straight through to the the queue
*/
static refbuf_t *process_theora_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page)
{
theora_codec_t *theora = codec->specific;
ogg_packet packet;
int header_page = 0;
int has_keyframe = 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 (theora_packet_isheader (&packet))
{
if (theora_decode_header (&theora->ti, &theora->tc, &packet) < 0)
{
ogg_info->error = 1;
WARN0 ("problem with theora header");
return NULL;
}
header_page = 1;
codec->headers++;
continue;
}
if (codec->headers < 3)
{
ogg_info->error = 1;
ERROR0 ("Not enough header packets");
return NULL;
}
if (theora_packet_iskeyframe (&packet))
has_keyframe = 1;
}
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 (has_keyframe && codec->possible_start)
{
codec->possible_start->sync_point = 1;
refbuf_release (codec->possible_start);
codec->possible_start = NULL;
}
if (granulepos != theora->prev_granulepos || granulepos == 0)
{
if (codec->possible_start)
refbuf_release (codec->possible_start);
refbuf_addref (refbuf);
codec->possible_start = refbuf;
}
theora->prev_granulepos = granulepos;
return refbuf;
}
/* Check if specified BOS page is the start of a theora stream and
* if so, create a codec structure for handling it
*/
ogg_codec_t *initial_theora_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;
theora_codec_t *theora_codec = calloc (1, sizeof (theora_codec_t));
ogg_stream_init (&codec->os, ogg_page_serialno (page));
ogg_stream_pagein (&codec->os, page);
theora_info_init (&theora_codec->ti);
theora_comment_init (&theora_codec->tc);
ogg_stream_packetout (&codec->os, &packet);
DEBUG0("checking for theora codec");
if (theora_decode_header (&theora_codec->ti, &theora_codec->tc, &packet) < 0)
{
theora_info_clear (&theora_codec->ti);
theora_comment_clear (&theora_codec->tc);
ogg_stream_clear (&codec->os);
free (theora_codec);
free (codec);
return NULL;
}
INFO0 ("seen initial theora header");
codec->specific = theora_codec;
codec->process_page = process_theora_page;
codec->codec_free = theora_codec_free;
codec->headers = 1;
format_ogg_attach_header (ogg_info, page);
ogg_info->codec_sync = codec;
return codec;
}
/* 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 <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
*/
#ifndef __FORMAT_THEORA_H
#define __FORMAT_THEORA_H
#include "format_ogg.h"
ogg_codec_t *initial_theora_page (format_plugin_t *plugin, ogg_page *page);
#endif /* __FORMAT_THEORA_H */
This diff is collapsed.
......@@ -10,14 +10,12 @@
* and others (see AUTHORS for details).
*/
/* format_vorbis.h
**
** vorbis format plugin header
**
*/
#ifndef __FORMAT_VORBIS_H__
#define __FORMAT_VORBIS_H__
int format_vorbis_get_plugin(source_t *source);
#ifndef __FORMAT_VORBIS_H
#define __FORMAT_VORBIS_H
#endif /* __FORMAT_VORBIS_H__ */
#include "format_ogg.h"
ogg_codec_t *initial_vorbis_page (format_plugin_t *plugin, ogg_page *page);
#endif /* __FORMAT_VORBIS_H */
......@@ -51,6 +51,7 @@ refbuf_t *refbuf_new(unsigned long size)
}
}
refbuf->len = size;
refbuf->sync_point = 0;
refbuf->_count = 1;
refbuf->next = NULL;
refbuf->associated = NULL;
......
......@@ -22,6 +22,7 @@ typedef struct _refbuf_tag
{
char *data;
long len;
int sync_point;
struct _refbuf_tag *associated;
struct _refbuf_tag *next;
......
......@@ -381,6 +381,26 @@ void source_move_clients (source_t *source, source_t *dest)
thread_mutex_unlock (&move_clients_mutex);
}
/* clients need to be start from somewhere in the queue
* * so we will look for a refbuf which has been previous
* * marked as a sync point */
static void find_client_start (source_t *source, client_t *client)
{
refbuf_t *refbuf = source->burst_point;
while (refbuf)
{
if (refbuf->sync_point)
{
client_set_queue (client, refbuf);
break;
}
refbuf = refbuf->next;
}
}
/* get some data from the source. The stream data is placed in a refbuf
* and sent back, however NULL is also valid as in the case of a short
* timeout and there's no data pending.
......@@ -442,10 +462,9 @@ static void send_to_listener (source_t *source, client_t *client, int deletion_e
/* new users need somewhere to start from */
if (client->refbuf == NULL)
{
/* make clients start at the per source burst point on the queue */
client_set_queue (client, source->burst_point);
find_client_start (source, client);
if (client->refbuf == NULL)
return;
return;
}
while (1)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment