[xiph-commits] r7098 - in icecast/branches/kh: . ices ices/conf

j at dactyl.lonelymoon.com j
Sun Jul 11 11:40:09 PDT 2004


ices/src
Message-ID: <20040711184009.C9C109AAAB at dactyl.lonelymoon.com>

Author: j
Date: Sun Jul 11 11:40:09 2004
New Revision: 7098

Added:
icecast/branches/kh/ices/
icecast/branches/kh/ices/NEWS
icecast/branches/kh/ices/conf/ices-fixit.xml
icecast/branches/kh/ices/conf/ices-jack.xml
icecast/branches/kh/ices/conf/ices-pcm.xml
icecast/branches/kh/ices/conf/ices-pcm2.xml
icecast/branches/kh/ices/conf/ices-switch.xml
icecast/branches/kh/ices/src/im_jack.c
icecast/branches/kh/ices/src/im_jack.h
icecast/branches/kh/ices/src/im_pcm.c
icecast/branches/kh/ices/src/im_pcm.h
icecast/branches/kh/ices/src/om_file.c
icecast/branches/kh/ices/src/om_file.h
icecast/branches/kh/ices/src/om_shout.c
icecast/branches/kh/ices/src/om_shout.h
icecast/branches/kh/ices/src/runner.c
icecast/branches/kh/ices/src/runner.h
Modified:
icecast/branches/kh/ices/AUTHORS
icecast/branches/kh/ices/Makefile.am
icecast/branches/kh/ices/README
icecast/branches/kh/ices/TODO
icecast/branches/kh/ices/conf/Makefile.am
icecast/branches/kh/ices/conf/ices-live.xml
icecast/branches/kh/ices/conf/ices-playlist.xml
icecast/branches/kh/ices/configure.in
icecast/branches/kh/ices/src/Makefile.am
icecast/branches/kh/ices/src/audio.c
icecast/branches/kh/ices/src/audio.h
icecast/branches/kh/ices/src/cfgparse.c
icecast/branches/kh/ices/src/cfgparse.h
icecast/branches/kh/ices/src/encode.c
icecast/branches/kh/ices/src/encode.h
icecast/branches/kh/ices/src/ices.c
icecast/branches/kh/ices/src/im_alsa.c
icecast/branches/kh/ices/src/im_alsa.h
icecast/branches/kh/ices/src/im_oss.c
icecast/branches/kh/ices/src/im_oss.h
icecast/branches/kh/ices/src/im_playlist.c
icecast/branches/kh/ices/src/im_playlist.h
icecast/branches/kh/ices/src/im_sun.c
icecast/branches/kh/ices/src/im_sun.h
icecast/branches/kh/ices/src/input.c
icecast/branches/kh/ices/src/inputmodule.h
icecast/branches/kh/ices/src/logging.h
icecast/branches/kh/ices/src/metadata.c
icecast/branches/kh/ices/src/metadata.h
icecast/branches/kh/ices/src/playlist_basic.c
icecast/branches/kh/ices/src/playlist_basic.h
icecast/branches/kh/ices/src/playlist_script.c
icecast/branches/kh/ices/src/reencode.c
icecast/branches/kh/ices/src/reencode.h
icecast/branches/kh/ices/src/resample.c
icecast/branches/kh/ices/src/resample.h
icecast/branches/kh/ices/src/signals.c
icecast/branches/kh/ices/src/signals.h
icecast/branches/kh/ices/src/stream.c
icecast/branches/kh/ices/src/stream.h
Log:
reimport of ices-kh to brances/kh/ices; this time as a branch of trunk/ices


Copied: icecast/branches/kh/ices (from rev 6976, icecast/trunk/ices)


Property changes on: icecast/branches/kh/ices
___________________________________________________________________
Name: branch-point
+ 7096
Name: svn:externals
+ m4              http://svn.xiph.org/icecast/trunk/m4
src/log         http://svn.xiph.org/icecast/trunk/log
src/net         http://svn.xiph.org/icecast/branches/kh/net
src/timing      http://svn.xiph.org/icecast/trunk/timing
src/thread      http://svn.xiph.org/icecast/branches/kh/thread


Modified: icecast/branches/kh/ices/AUTHORS
===================================================================
--- icecast/trunk/ices/AUTHORS	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/AUTHORS	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,3 +1 @@
-Michael Smith	<msmith at labyrinth.net.au>
-Jack Moffitt	<jack at icecast.org>
Karl Heyes      <karl at xiph.org>

Modified: icecast/branches/kh/ices/Makefile.am
===================================================================
--- icecast/trunk/ices/Makefile.am	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/Makefile.am	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,11 +1,11 @@
## Process this file with automake to produce Makefile.in

-AUTOMAKE_OPTIONS = foreign 1.6 dist-bzip2
+AUTOMAKE_OPTIONS = 1.6 foreign dist-bzip2
ACLOCAL_AMFLAGS = -I m4

-SUBDIRS = debian src conf doc
+SUBDIRS = src conf

-EXTRA_DIST = README AUTHORS COPYING TODO m4/shout.m4 m4/vorbis.m4 m4/ogg.m4 m4/xiph_compiler.m4 m4/xiph_xml2.m4
+EXTRA_DIST = README AUTHORS COPYING TODO NEWS m4

# SCCS Definitions (for BitKeeper)
GET = true

Added: icecast/branches/kh/ices/NEWS
===================================================================
--- icecast/trunk/ices/NEWS	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/NEWS	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,427 @@
+1.59
+----
+. fix ALSA silence issue, stupid bug...a one-liner as well
+. Allow downmix to accept >2 channels
+. put back original timing allocation code in im_jack, also
+  base ringbuffer size on buffer_size and sleep time
+
+1.58
+----
+. fix build issue for saved id
+. update jack input module to use ringbuffer
+
+1.57
+----
+. handle "-" in playlist to refer to stdin
+. for live inputs make sure module only fails after 100 buffer allocation
+  failures in succession, instead of in total.
+. report initial failed log file opening
+
+1.56
+----
+. small update to JACK input
+. make sure non-blocking sockets are used in libshout
+
+1.55
+----
+. removed more unused code
+. plug some memory leaks, affected
+. tweaked parameters to input modules, higher max/preloaded buffers and
+  a longer recovery delay for playlist.
+
+1.54
+----
+. Add JACK input, feedback if possible
+. various internal API cleanups
+. buffer allocation messages are handled more gracefully
+. fix quirk with missing metadata filename
+
+1.53
+----
+. fix compile issue on OBSD wrt im_sun module
+. change unknown XML element message, state filename and quote tags in question
+. small C99 cleanups, check for __func__
+. XML call API change, was causing a memory corruption in parsing.
+
+1.52
+----
+. fix up minor compile issue.
+. push shout_t creation/population to connection time, allows instance
+  metadata to after encode section.
+. fixup alsa input code.
+
+1.51
+----
+. YP bitrate is expected to be in kbps
+. added URL to global and per-instance metadata.
+. fixup metadata settings better. string copies were not being done
+
+1.50
+----
+. function rename cleanup for xml parser
+. implement background tag, it can now makes itself daemon
+. only free buffers when there's a surplus. prealloc is minimum.
+. only warn on so many reconnect attempts, keep retrying.
+. supply nominal-bitrate to the shout audio info at connection time.
+. take out logging messages in sig handlers. Functions are not signal safe
+. moved the dead air buffer into global, saves using duplicate buffers
+
+1.49
+----
+. update encode_initialise, allow for setting VBR mode by bitrate, also
+  constrained VBR mode doesn't work as expected, vorbisenc issue.
+. add pidfile option for external handling
+. update im_sun to handle the different endian on sun/sparc sun/x86 and
+  openbsd
+. change buffer allocation routine. preallocation is still done but on a
+  smaller scale and the buffer allocation routine allocs/initialises buffers
+  when needed and below limit.  The free routine will free one buffer every so
+  often to cut down on surplus buffers.
+
+1.48
+----
+. Stupid timer fix, on timing error the reset wasn't working out right
+. fixup im_sun better, should work ok for OBSD now. maybe solaris as well
+. don't try reading metadata file if it isn't stated
+
+1.47
+----
+. changed sched_getparam to pthread_*  to work on OBSD
+
+1.46
+----
+. fix managed mode selection
+. make serial numbers random for each outgoing stream
+
+1.45
+----
+. call shout close even if connection failed. This prevents zombie
+  connections from building up.
+
+1.44
+----
+. increase delay on failed buffer allocations to 100ms, and increase
+  timer to account for it, this should prevent a bursts on recovery.
+. Add logsize xml parameter in kbytes, default is 2048.
+. tweak to make buffer count dependant on number of runners
+
+1.43
+----
+. downmix is now a bool type, so true,yes as well as 1 is valid
+. in playlist loop on buffer allocation failure, this should stop needless
+  logging as the previous message is noted.
+
+1.42
+----
+. dead code removal
+. reset timer if input jumps more than 1 second.
+. when sleeping enabled (normal) and buffer allocation fails then instead
+  of dropping out, log one message, sleep for a short while and retry again
+. implement bool tpye XML tags in config. true. yes are acceptable as well
+  as a numeric.
+
+1.41
+----
+. separate user-derived rate/channels, from input derived ones.
+
+1.40
+----
+. fix granulepos for file fixing, for extra compliance.
+. added "once" tag for handling each input once only. Needed for file fix.
+
+1.39
+----
+. Made playlist honor USR1 USR2 signals again
+. fixup ALSA, but untested until I install the drivers again.
+. minor cleanups, removing unused bits etc
+
+1.38
+----
+. don't share the code_ops structure as it contains stream specific info
+. drop the vorbis array from the buffers, it was a hack
+. ALSA driver not fully changed over yet
+
+1.37
+----
+First 3 could break things as they are fairly substantial changes
+. big input flow control change, callback approach
+. various structure re-arrangement
+. playlist now sends as out as ogg packets instead of ogg pages, also
+  samples are calculated per packet.
+. put quotes around filenames, allows for easy tracking in logs.
+. reencoder takes packets instead of pages
+. make random playlist more random
+
+1.36
+----
+. Fixed bug in playlist script, the rate regulation was disabled.
+. removed odd printf
+
+1.35
+----
+. fix reconnect_delay/attempts parsing issue.
+. Added YP option for advertising the shout stream
+. Updated m4 scripts in line with CVS
+. Added 2Meg log cycle
+
+1.34
+----
+. move various code out of cfgparse into specifics files.
+. elimate passthru in encode.
+. set samplerate/channels on every buffer from the playlist
+  fixes an assert possibility
+
+1.33
+----
+. Add skip option to pcm input, to allow for removal of initial
+  data on certain connections, typically shoutcast
+. avoid segv bugs on flushed shout connections.
+. make file save time-reset work again.
+. fixed a bug on first file created as the first audio page
+  would only contain one packet.
+. updated timing code to be more precise on ogg files with gaps
+  at the beginning.
+. Added sleep param to playlist to not sleep when reading ogg
+  files. not to be used with shout connections but fine for
+  disk writes.
+
+1.32
+----
+. fix-up NULL dereference on exit for passthru vorbis case,
+. do instance cleanup at end of runner.
+. add better timer logic into playlist, so that long sleeps
+  don't occur on ogg files with gaps at the start.
+
+1.31
+----
+. requires libshout kh14 (API change, more inline with CVS)
+. ogg test now check for a ogg v1.0 call
+. om_shout now flushes pages every 2 seconds, if not less
+
+1.30
+----
+. lots of dead code removal
+. place specifc output modules in separate files, more modular
+  this allows for specifying multiple savestreams and shout
+  connections per instance. (note config file change)
+. separate reencode packetin from pagein for later work.
+. various little cleanups, name changes etc
+
+1.29
+----
+. minor cleanups for distributing files.
+. add optional debug for checking pthread flags.
+. a few memory leaks fixes.
+
+1.28
+----
+. Adeed calls to reencode to take pages and produce packets for output,
+  this removes the page build -> spilt -> re-build logic.
+. Add comment tag for reencode for encoder identification
+. push the drop priviledge code to before the log file creation.
+. drop any special privs when reading the config file.
+
+1.27
+----
+. Added encode API based on packets instead of pages
+. Added output API based on packets instead of pages.
+. Changed PCM->vorbis to use the packet-based encoding route, saves
+  needless splitting and recalculation of granulepos. reencode still
+  uses pages.
+. Added realtime option for setting scheduling policy
+. Added support for dropping setuid or change to different user.
+
+1.26
+----
+. added fmask/dmask for file creation permissions (0600/0700)
+. updated auto* build to CVS style
+. added reset-time tag (default 1) in savestream to modify granulepos
+  on file change
+
+1.25
+----
+. made im_pcm do back off when buffers have run out
+. renamed setup.* to cfgparse.* to match CVS
+
+1.24
+----
+. renamed the res_ functions/structure as they can clash with the resolver
+. cleaned up the build a bit more
+. split acinclude in the separate m4 macros
+. another fix for getting the samplerate on reencoding correct
+
+1.23
+----
+. reencoding with resampling/downmixing was having problems
+. im_pcm args support failed for old source parameter.
+
+1.22
+----
+. more playlist work, was not starting the reencoder, and did not provide
+  the sample counts correctly
+. im_oss didn't increase senttime which caused a large catchup occurring
+  for any following modules, leading to buffer starvation.
+. Provide a mechanism for providing args for the command on im_pcm. Useful
+  for ogg123 as it doesn't deal with close output pipes correctly.
+
+1.21
+----
+. The input_sleep routine was working the sleep time wrong
+. playlist needed work for the EOS handling
+
+1.20
+----
+.  improved the save stream naming, so that subdirs are created when needed
+.  change default duration of a file to 1 hour and allow 0 to disable
+.  add <on-metadata> tag in save stream to enable metadata to switch the file.
+
+1.19
+----
+. made input modules handle input module switchover better.
+     Needed EOS handling for input module
+     fix for negative granulepos on switchover.
+. removed debugging printf
+. Allowed for creating sub-directories when writing files.
+. use shout_audio_info to state samplerate and channels at connection start, this
+  probably needs done better as that can change mid connection.
+
+1.18
+----
+. updated config file for stream info
+. various autoconf build updates
+
+1.17
+---
+. removed the metadata section, now you just state the stream info per instance or
+  globally.
+. changed build setup, now checks for pthreads better and supplies much of the
+  build information without much trouble
+
+1.16
+----
+. minor fix for reconnection of streams.
+. CVS alsa input copied but not integrated
+, comment out "increase max buffers" until a better place can be found.
+
+1.15
+----
+. trap for max input buffers in im_oss, reading audio into an unused buffer until
+  either an input buffer is availble or a certain amount of time has elapsed, help
+  with sudden loads.
+. set eos on on shutdown in input thread (oss, pcm)
+. don't do select in pcm module when no time is to be elapsed.
+. add timestamp to input module, and trap from restarting inputs too quickly
+. input thread now closes the first input which causes a chain of pipe closes, a
+  close of the runner pipe acts as the runner shutdown
+. improved debug messages
+. fixed up packet granulepos calculation so that valid ogg pages can be created.
+  This needs more work as some cases still have problems, like input switch
+. bother file and shout streams should start at granulepos 0.
+. timing_sleep need not call select if no sleeping required.
+
+1.14
+----
+. continuing from before, the outgoing libshout stream is now built separately from
+  the internal ogg pages. This applies to all setups. Ther serial numbers for file
+  and libshout streams are incremented each time.
+. im_pcm im_oss now set eos on signal then critical on next buffer.
+. libshout startup is pushed to the output ogg page stage, this simplifies the
+  high end flow control in the runner, and prevents encoder/reencoder initialising
+  on libshout connect.
+. fixed some parsing, show warnings for unknown tags.
+
+1.13
+----
+. Changed what save stream stores, now builds a separate ogg stream
+. default timeout to 5 seconds on im_pcm.
+. allow for no specifying a hostname, eg write to disk only
+
+1.12
+----
+. warn if trying to encode 0 samples.
+. make OSS module uninterleave
+. sort out metadata for OSS and pcm inputs, check the file at start as well.
+. playlist is available now, may need more testing.
+. log read comments from metadata file.
+. re-organise the setup/shutdown on stream change.
+. allow for writing stream to disk, even when the connection to icecast is gone
+. allow for file name expansion (strftime) for file to write
+
+1.11
+----
+. Changes to the autoconf build.
+. pushed PCM uninterleaving into input thread.
+. start to break up re-encoding, currently broken
+. Made iintial OSS input work, uninterleave work still needed.
+. Changed how silence detection worked for PCM input, it had
+  problems with EOS detection.
+. Changed // to /* */
+
+1.10
+----
+. force encoder quality values between -1 and 10 inclusive.
+. Change trigger level for samples per page before flushing.
+. Change pcm module to emit silence buffers when nothing has
+  been read.  Useful for masking latencies of external sources
+. Change resolution of senttime back to uS as the ms count has
+  frational part causing sync drift
+
+1.9
+---
+. fixed stereo bug on playing some tracks.
+. terminate properly on unrunnable scripts.
+
+1.8
+---
+. Allocate encoder/reencoder at instance creation
+. Compile condition for using pipe as oppose to pthread conditionals
+. Change encode parsing
+   - drop samplerate/channels out of encode parsing.
+   - Add don't encodde vorbis.
+   - Handle comments
+   - warn of unrecognised options
+. don't allocate shout structure twice per instance
+. Free up hostname/mount/password from xml after passing to shout
+. Change encoder setup/shutdown routines.
+. Add checks for samples in page. If 0 don't try the vorbis code.
+. Add close stdin in im_pcm for the spawned script, detach keyboard.
+. Update re-encoder routines.
+. Added reencode_clear in flush routine to clear up memory sooner.
+. Added require_critical to stream to act as a per stream critical flag.
+. Removed maxqueuelength
+
+1.7
+---
+. Re-introduce pipes between runners to see if there is any performance
+  difference between these and the signals used by pthreads.
+
+1.6
+---
+. Chenged free buffers check from WARN to ERROR, as it's pretty final.
+. Completely zap MAX_INPUT_BUFFERS and increase the MIN_INPUT_BUFFER
+. Change wait period after the input thread has started runners to 1sec.
+. Don't treat comments as unknown in input parameter list.
+. moved default_len to pcm-specifc structure.
+. Added samples parameter to pcm input, default to 12288.
+. experimental - try quicker uninterleave in encode_data.
+
+
+1.5:
+----
+
+. Replace MAX_INPUT_BUFFER with MIN_INPUT_BUFFERS
+. Get main input code to initialise the buffers, calling a module specific
+function.
+. Changed im_pcm read to loop until a buffer is full instead of just sending
+only what was read.
+. put -march in configure.in.  Needs proper extra args processing for makefile
+. Changed id values to start from 1 instead of 0
+. Changed sample_in_page limit to force a flush.
+. Added buffers tag in input_module to allow for overriding default.
+. Remove im_pcm specific buffers parameter.
+. misc indent cleanups, zap input_fluish_queue.
+. move control structure.
+. reduce sleep after runners started from to 500ms
+. cleanup send_to_runner.
+. LOG messages cleanup
+

Modified: icecast/branches/kh/ices/README
===================================================================
--- icecast/trunk/ices/README	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/README	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,19 +1,3 @@
-This is ices 2.0
+this is ices 2.0

-This is a beta release of ices 2.0.  It's the primary source client
-for icecast 2.0.  It has proven to stable and well featured.
-
-Documentation is in the doc subdirectory, and some sample configuration
-files exist in the conf subdirectory.
-
-To build and install, run
-    ./configure
-    make
-    make install
-
-You WILL need to edit the config file you choose to use before running
-this application.
-
-For help, email us at icecast at xiph.org, icecast-dev at xiph.org, or find us
-on IRC at irc.freenode.net, in #icecast.
-
+it's the primary source client for icecast 2.0

Modified: icecast/branches/kh/ices/TODO
===================================================================
--- icecast/trunk/ices/TODO	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/TODO	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,5 +1,34 @@
-- cue files?
-- stream rewriting for serial number increment, etc.
-- graceful shutdown is broken (at least for resampling/live input, possibly
-  others)
+. loss of audio sync on playlist USR1/USR2 is cause by the timer accounting for the
+page granulepos but not all the packets are extracted and sent to icecast.

+. do the vorbis granulepos calculations in the playlist and send out packets instead
+of pages.  The output is ok now for taking packets.
+
+. playlist needs re-org to do packet sending and possible program spawn for external
+decode like mp3 transcoding.  Use libcurl for direct ogg URL to packet
+sending.
+
+. pcm input needs to drop the silence detection logic, and also default to do no
+sleeping as most uses are externally timed anyway.
+
+. ALSA needs to be worked on to be usable
+
+. make output mechanism more modular, so that multiple output can be done
+
+  <shout>
+     <hostname>....</hostname>
+     <port>....</port>
+  </shout>
+
+. make passthough work better, currently sends pages which are then split up.
+
+. separate cfg parsing to individual modules, with the core left in cfgparser.c
+
+considerations.
+
+libcurl 7.10 needed for no signal support
+
+getting the playlist to produce packets instead of pages will resolve the extended
+sleep check better and may resolve the lost samples on USR1/2.
+
+

Modified: icecast/branches/kh/ices/conf/Makefile.am
===================================================================
--- icecast/trunk/ices/conf/Makefile.am	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/Makefile.am	2004-07-11 18:40:08 UTC (rev 7098)
@@ -2,5 +2,5 @@

AUTOMAKE_OPTIONS = foreign

-dist_pkgdata_DATA = ices-live.xml ices-playlist.xml
+EXTRA_DIST = ices-live.xml ices-playlist.xml ices-pcm.xml ices-pcm2.xml ices-switch.xml ices-fixit.xml


Added: icecast/branches/kh/ices/conf/ices-fixit.xml
===================================================================
--- icecast/trunk/ices/conf/ices-fixit.xml	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-fixit.xml	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<!--
+This config is used to take a list of ogg files from a playlist and
+processes them without any speed restrictions. The sleep param is the
+tag that disables the timing, if set to 0, make sure that an icecast
+server isn't used as that will saturate the service quickly.
+
+The once tag will cause ices to stop after the last input ends, normally
+it would loop back to the first one, in the case below the playlist would
+be restarted again.
+
+The passthru tag prevents ogg vorbis data being re-encoded to the
+default encoder settings (q3)
+-->
+<ices>
+
+    <background>0</background> <!-- run in background? (unimplemented) -->
+    <logpath>/tmp</logpath> <!-- where logs, etc go. -->
+    <logfile>ices.log</logfile>
+    <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+    <logsize>2048</logsize> <!-- the size the log file must be before rotation -->
+    <consolelog>0</consolelog> <!-- logfile is ignored if this is set to 1 -->
+    <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
+
+    <stream>
+        <name>Example stream name</name>
+        <genre>Example genre</genre>
+        <description>A short description of your stream</description>
+
+        <once>1</once>
+        <input>
+            <module>playlist</module>
+            <param name="type">basic</param>
+            <param name="file">playlist.txt</param>
+            <param name="random">0</param>
+            <param name="once">1</param>
+            <param name="sleep">0</param>
+        </input>
+
+        <runner>
+            <instance>
+                <passthru>1</passthru>
+                <savestream>
+                    <!-- this splits the file on each header update -->
+                    <!-- save-1.ogg save-2.ogg .... -->
+                    <on-metadata>1</on-metadata>
+                    <fmask>0644</fmask>
+                    <dmask>0755</dmask>
+               </savestream>
+
+            </instance>
+        </runner>
+
+	</stream>
+</ices>

Added: icecast/branches/kh/ices/conf/ices-jack.xml
===================================================================
--- icecast/trunk/ices/conf/ices-jack.xml	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-jack.xml	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<ices>
+    <background>0</background>  <!-- run in background? (unimplemented) -->
+    <realtime>0</realtime>      <!-- disable realtime, enabled by default -->
+    <user>ices</user>           <!-- user to change to when started as root -->
+    <logpath>/tmp</logpath>     <!-- where log goes. -->
+    <logsize>2048</logsize>     <!-- the size the log has to get to before cycling -->
+    <logfile>ices.log</logfile>
+    <loglevel>4</loglevel>      <!-- 1=error,2=warn,3=info,4=debug -->
+    <consolelog>0</consolelog>  <!-- logfile is ignored if this is set to 1 -->
+    <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
+
+	<stream>
+        <!-- global settings for all streams - optional-->
+        <name>Example stream name</name>
+        <genre>Example genre</genre>
+        <description>A short description of your stream</description>
+
+	<!-- input module -->
+        <!-- This example uses the 'jack' module. It takes input from the -->
+        <!-- jack ports and processes it for live -->
+        <!-- encoding. If metadatafilename is set then at start and on USR1 -->
+        <!-- the file is read and the comments are added into the stream -->
+		<input>
+			<module>jack</module>
+			<param name="channels">2</param>        <!-- number channels that will be available as jack ports-->
+			<param name="clientname">ices</param>   <!-- jackclient name  -->
+			<param name="metadatafilename">metadata</param>
+		</input>
+        <!-- more input section can be stated here, and can be switched manually -->
+        <!-- by USR2 or whenever the previous input finishes. The order is       -->
+        <!-- dictated in here and loops aronnd to the first one listed -->
+
+        <!-- A runner is a thread that applies the input data to each outgoing -->
+        <!-- stream instance defined within it.  Multiple runners can be stated -->
+        <!-- for use on multiple processors. -->
+        <runner>
+            <!-- stream instance,  used to associate a set of encoding settings -->
+            <!-- with output.  At the moment 2 outputs can be used, shout and  -->
+            <!-- savestream. Any number or combination of these outputs can be used -->
+            <instance>
+                <!-- per instance setting, overriding the global settings - optional-->
+                <name>test transmission</name>
+                <genre>various</genre>
+                <description>low bandwidth stream</description>
+
+                <!-- You define hostname and port for the server here, along with -->
+                <!-- the source password and mountpoint.  If you miss them out  -->
+                <!-- then any processing will still occur but it won't be sent to -->
+                <!-- icecast, useful for encode to file only -->
+                <shout>
+                    <hostname>localhost</hostname>
+                    <port>8000</port>
+                    <password>hackme</password>
+                    <mount>/example1.ogg</mount>
+                </shout>
+
+                <!-- resample input to the stated samplerate  - optional
+                     the input can change samplerate so this can be used to fix it
+                     at a certain rate -->
+                <!--
+		<resample>
+                    <out-rate>22050</out-rate>
+                </resample>
+		-->
+
+                <!-- stereo->mono downmixing, enabled by setting this to 1 - optional -->
+		<!--
+                <downmix>1</downmix>
+		-->
+                <!-- Live encoding/reencoding: -->
+                <encode>
+                    <quality>1.1</quality>
+                    <!-- usual options for encoding, except from samplerate and chanels -->
+                    <!-- they are set from input/resample/downmix setting -->
+                </encode>
+            </instance>
+
+            <!-- more instances can be defined -->
+            <instance>
+                <!-- This instance just writes to file, no connection to icecast -->
+                <encode>
+                    <quality>1.1</quality>
+                </encode>
+
+                <!-- writing of files, all but the filename have the defaults-->
+                <!-- listed -->
+                <savestream>
+                    <!-- filename expansion, look at strftime for details -->
+                    <filename>/home/ices/saved-file/%X/stream-%M.ogg</filename>
+                    <!-- file creation mask,  eg 0644 -->
+                    <fmask>0600</fmask>
+                    <!-- directory creation mask -->
+                    <dmask>0700</dmask>
+                    <!-- seconds to record, 0 disables, defaults to 1 hour -->
+                    <duration>7200</duration>
+                    <!-- switch file on stream change -->
+                    <on-metadata>1</on-metadata>
+                    <!-- Normally on switchover from a duration timeout, the
+                         timecode is reset, needed for some players, but disabling
+                         this prevents modification -->
+                    <reset-time>0</reset-time>
+                </savestream>
+
+            </instance>
+
+        </runner>
+
+        <runner>
+            ....
+        </runner>
+
+	</stream>
+</ices>
+

Modified: icecast/branches/kh/ices/conf/ices-live.xml
===================================================================
--- icecast/trunk/ices/conf/ices-live.xml	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-live.xml	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,94 +1,114 @@
<?xml version="1.0"?>
<ices>

-    <!-- run in background  -->
-    <background>0</background>
-    <!-- where logs go. -->
-    <logpath>/var/log/ices</logpath>
-    <logfile>ices.log</logfile>
-    <!-- 1=error, 2=warn, 3=infoa ,4=debug -->
-    <loglevel>4</loglevel>
-    <!-- logfile is ignored if this is set to 1 -->
-    <consolelog>0</consolelog>
+	<background>0</background>  <!-- run in background? (unimplemented) -->
+    <realtime>0</realtime>      <!-- disable realtime, enabled by default -->
+    <user>ices</user>           <!-- user to change to when started as root -->
+	<logpath>/tmp</logpath>     <!-- where log goes. -->
+	<logsize>2048</logsize>     <!-- the size the log has to get to before cycling -->
+	<logfile>ices.log</logfile>
+	<loglevel>4</loglevel>      <!-- 1=error,2=warn,3=info,4=debug -->
+    <consolelog>0</consolelog>  <!-- logfile is ignored if this is set to 1 -->
+    <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->

-    <!-- optional filename to write process id to -->
-    <!-- <pidfile>/home/ices/ices.pid</pidfile> -->
+	<stream>
+        <!-- global settings for all streams - optional-->
+        <name>Example stream name</name>
+        <genre>Example genre</genre>
+        <description>A short description of your stream</description>

-    <stream>
-        <!-- metadata used for stream listing -->
-        <metadata>
-            <name>Example stream name</name>
-            <genre>Example genre</genre>
-            <description>A short description of your stream</description>
-            <url>http://mysite.org</url>
-        </metadata>
+		<!-- input module -->
+        <!-- This example uses the 'oss' module. It takes input from the -->
+        <!-- oss audio device (i.e. line-in), and processes it for live -->
+        <!-- encoding. If metadatafilename is set then at start and on USR1 -->
+        <!-- the file is read and the comments are added into the stream -->
+		<input>
+			<module>oss</module>
+			<param name="rate">44100</param>        <!-- samplerate -->
+			<param name="channels">2</param>        <!-- number of channels -->
+			<param name="device">/dev/dsp</param>   <!-- audio device -->
+			<param name="metadatafilename">metadata</param>
+		</input>
+        <!-- more input section can be stated here, and can be switched manually -->
+        <!-- by USR2 or whenever the previous input finishes. The order is       -->
+        <!-- dictated in here and loops aronnd to the first one listed -->

-        <!--    Input module.
+        <!-- A runner is a thread that applies the input data to each outgoing -->
+        <!-- stream instance defined within it.  Multiple runners can be stated -->
+        <!-- for use on multiple processors. -->
+        <runner>
+            <!-- stream instance,  used to associate a set of encoding settings -->
+            <!-- with output.  At the moment 2 outputs can be used, shout and  -->
+            <!-- savestream. Any number or combination of these outputs can be used -->
+            <instance>
+                <!-- per instance setting, overriding the global settings - optional-->
+                <name>test transmission</name>
+                <genre>various</genre>
+                <description>low bandwidth stream</description>
+
+                <!-- You define hostname and port for the server here, along with -->
+                <!-- the source password and mountpoint.  If you miss them out  -->
+                <!-- then any processing will still occur but it won't be sent to -->
+                <!-- icecast, useful for encode to file only -->
+                <shout>
+                    <hostname>localhost</hostname>
+                    <port>8000</port>
+                    <password>hackme</password>
+                    <mount>/example1.ogg</mount>
+                </shout>

-            This example uses the 'oss' module. It takes input from the
-            OSS audio device (e.g. line-in), and processes it for live
-            encoding.  -->
-        <input>
-            <module>oss</module>
-            <param name="rate">44100</param>
-            <param name="channels">2</param>
-            <param name="device">/dev/dsp</param>
-            <!-- Read metadata (from stdin by default, or -->
-            <!-- filename defined below (if the latter, only on SIGUSR1) -->
-            <param name="metadata">1</param>
-            <param name="metadatafilename">test</param>
-        </input>
+                <!-- resample input to the stated samplerate  - optional
+                     the input can change samplerate so this can be used to fix it
+                     at a certain rate -->
+                <resample>
+                    <out-rate>22050</out-rate>
+                </resample>

-        <!--    Stream instance.
+                <!-- stereo->mono downmixing, enabled by setting this to 1 - optional -->
+                <downmix>1</downmix>

-            You may have one or more instances here.  This allows you to
-            send the same input data to one or more servers (or to different
-            mountpoints on the same server). Each of them can have different
-            parameters. This is primarily useful for a) relaying to multiple
-            independent servers, and b) encoding/reencoding to multiple
-            bitrates.
+                <!-- Live encoding/reencoding: -->
+                <encode>
+                    <quality>0</quality>
+                    <!-- usual options for encoding, except from samplerate and chanels -->
+                    <!-- they are set from input/resample/downmix setting -->
+                </encode>
+            </instance>

-            If one instance fails (for example, the associated server goes
-            down, etc), the others will continue to function correctly.
-            This example defines a single instance doing live encoding at
-            low bitrate.  -->
+            <!-- more instances can be defined -->
+            <instance>
+                <!-- This instance just writes to file, no connection to icecast -->
+                <encode>
+                    <quality>0</quality>
+                </encode>

-        <instance>
-            <!--    Server details.
+                <!-- writing of files, all but the filename have the defaults-->
+                <!-- listed -->
+                <savestream>
+                    <!-- filename expansion, look at strftime for details -->
+                    <filename>/home/ices/saved-file/%X/stream-%M.ogg</filename>
+                    <!-- file creation mask,  eg 0644 -->
+                    <fmask>0600</fmask>
+                    <!-- directory creation mask -->
+                    <dmask>0700</dmask>
+                    <!-- seconds to record, 0 disables, defaults to 1 hour -->
+                    <duration>7200</duration>
+                    <!-- switch file on stream change -->
+                    <on-metadata>1</on-metadata>
+                    <!-- Normally on switchover from a duration timeout, the
+                         timecode is reset, needed for some players, but disabling
+                         this prevents modification -->
+                    <reset-time>0</reset-time>
+                </savestream>
+
+            </instance>

-                You define hostname and port for the server here, along
-                with the source password and mountpoint.  -->
+        </runner>

-            <hostname>localhost</hostname>
-            <port>8000</port>
-            <password>hackme</password>
-            <mount>/example1.ogg</mount>
-            <yp>1</yp>   <!-- allow stream to be advertised on YP, default 0 -->
+        <runner>
+            ....
+        </runner>

-            <!--    Live encoding/reencoding:
+	</stream>
+</ices>

-                channels and samplerate currently MUST match the channels
-                and samplerate given in the parameters to the oss input
-                module above or the remsaple/downmix section below.  -->
-
-            <encode>
-                <quality>0</quality>
-                <samplerate>22050</samplerate>
-                <channels>1</channels>
-            </encode>
-
-            <!-- stereo->mono downmixing, enabled by setting this to 1 -->
-            <downmix>1</downmix>
-
-            <!-- resampling.
-
-                Set to the frequency (in Hz) you wish to resample to, -->
-
-            <resample>
-                <in-rate>44100</in-rate>
-                <out-rate>22050</out-rate>
-            </resample>
-        </instance>
-
-    </stream>
-</ices>

Added: icecast/branches/kh/ices/conf/ices-pcm.xml
===================================================================
--- icecast/trunk/ices/conf/ices-pcm.xml	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-pcm.xml	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<ices>
+
+    <background>0</background> <!-- run in background? (unimplemented) -->
+    <logpath>/tmp</logpath> <!-- where logs, etc go. -->
+    <logfile>ices.log</logfile>
+    <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+    <logsize>2048</logsize> <!-- the size the log file must be before rotation -->
+    <consolelog>0</consolelog> <!-- logfile is ignored if this is set to 1 -->
+    <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
+
+	<stream>
+        <input>
+            <module>pcm</module>
+            <param name="rate">44100</param>
+            <param name="channels">2</param>
+            <param name="metadatafilename">test</param>     <!-- read metadata from file on USR1 -->
+            <!-- pipe for extracting raw pcm, if not defined stdin is assumed -->
+            <param name="command">/home/ices/get_pcm.sh</param>
+            <param name="timeout">5</param>                 <!-- If missing or zero then no timeout on reading PCM -->
+        </input>
+
+        <runner>
+            <instance>
+                <shout>
+                    <hostname>the.hackers.club</hostname>
+                    <port>8000</port>
+                    <password>hackme</password>
+                    <mount>/example1.ogg</mount>
+
+                    <reconnectdelay>5</reconnectdelay>
+                    <reconnectattempts>-1</reconnectattempts>
+                </shout>
+
+                <encode>
+                    <quality>0</quality>
+                </encode>
+            </instance>
+
+            <instance>
+                <shout>
+                    <hostname>localhost</hostname>
+                    <port>8000</port>
+                    <password>hackme</password>
+                    <mount>/example.ogg</mount>
+
+                    <reconnectdelay>5</reconnectdelay>
+                    <reconnectattempts>-1</reconnectattempts>
+                </shout>
+
+                <encode>
+                    <quality>-1</quality>
+                </encode>
+            </instance>
+        </runner>
+
+	</stream>
+</ices>

Added: icecast/branches/kh/ices/conf/ices-pcm2.xml
===================================================================
--- icecast/trunk/ices/conf/ices-pcm2.xml	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-pcm2.xml	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<ices>
+
+    <background>0</background> <!-- run in background? (unimplemented) -->
+    <logpath>/tmp</logpath> <!-- where logs, etc go. -->
+    <logfile>ices.log</logfile>
+    <logsize>2048</logsize> <!-- the size the log file must be before rotation -->
+    <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+    <consolelog>0</consolelog> <!-- logfile is ignored if this is set to 1 -->
+    <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
+
+	<stream>
+
+        <input>
+            <module>pcm</module>
+            <param name="rate">44100</param>
+            <param name="channels">2</param>
+            <param name="metadatafilename">test</param>
+            <param name="command">/usr/bin/ogg123</param>
+            <param name="arg">--device=raw</param>
+            <param name="arg">--quiet</param>
+            <param name="arg">--file=-</param>
+            <param name="arg">/path/or/url.ogg</param>
+            <!-- limit of about 10 args currently, needs to be implemented better -->
+        </input>
+
+        <runner>
+            <instance>
+                <shout>
+                    <hostname>localhost</hostname>
+                    <port>8000</port>
+                    <password>hackme</password>
+                    <mount>/example1.ogg</mount>
+                    <reconnectdelay>5</reconnectdelay>
+                    <reconnectattempts>-1</reconnectattempts>
+                <shout>
+
+                <shout>
+                    <hostname>other.host</hostname>
+                    <port>8000</port>
+                    <password>hackme</password>
+                    <mount>/example1.ogg</mount>
+                </shout>
+
+                <encode>
+                    <quality>0</quality>
+                </encode>
+            </instance>
+
+            <instance>
+                <hostname>localhost</hostname>
+                <port>8000</port>
+                <password>hackme</password>
+                <mount>/example2.ogg</mount>
+
+                <reconnectdelay>5</reconnectdelay>
+                <reconnectattempts>-1</reconnectattempts>
+
+                <encode>
+                    <quality>-1</quality>
+                </encode>
+            </instance>
+        </runner>
+
+	</stream>
+</ices>

Modified: icecast/branches/kh/ices/conf/ices-playlist.xml
===================================================================
--- icecast/trunk/ices/conf/ices-playlist.xml	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-playlist.xml	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,99 +1,47 @@
<?xml version="1.0"?>
<ices>
-    <!-- run in background -->
-    <background>0</background>
-    <!-- where logs, etc go. -->
-    <logpath>/var/log/ices</logpath>
-    <logfile>ices.log</logfile>
-    <!-- 1=error,2=warn,3=info,4=debug -->
-    <loglevel>4</loglevel>
-    <!-- set this to 1 to log to the console instead of to the file above -->
-    <consolelog>0</consolelog>

-    <!-- optional filename to write process id to -->
-    <!-- <pidfile>/home/ices/ices.pid</pidfile> -->
+	<background>0</background> <!-- run in background? (unimplemented) -->
+	<logpath>/tmp</logpath> <!-- where logs, etc go. -->
+	<logfile>ices.log</logfile>
+	<loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+    <logsize>2048</logsize> <!-- the size the log file must be before rotation -->
+    <consolelog>0</consolelog> <!-- set this to 1 to log to the console instead
+                                    of to the file above -->
+    <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->

-    <stream>
-        <!-- metadata used for stream listing (not currently used) -->
-        <metadata>
-            <name>Example stream name</name>
-            <genre>Example genre</genre>
-            <description>A short description of your stream</description>
-        </metadata>
+	<stream>
+
+		<!-- input module -->
+		<input>
+			<module>playlist</module>
+			<param name="type">basic</param>        <!-- Only 'basic' and script implemented -->
+			<param name="file">playlist.txt</param> <!-- be sure this exists -->
+			<param name="random">0</param>          <!-- random play -->
+			<param name="once">0</param>            <!-- if set to 1 , plays once through, then exits. -->
+		</input>

-        <!-- input module
+        <runner>
+            <!-- Stream instance -->
+            <instance>
+                <shout>
+                    <!-- Server details: -->
+                    <hostname>localhost</hostname>
+                    <port>8000</port>
+                    <password>hackme</password>
+                    <mount>/example1.ogg</mount>

-            The module used here is the playlist module - it has
-            'submodules' for different types of playlist. There are
-            two currently implemented, 'basic', which is a simple
-            file-based playlist, and 'script' which invokes a command
-            to returns a filename to start playing. -->
+                    <reconnectdelay>2</reconnectdelay>
+                    <reconnectattempts>5</reconnectattempts>
+                </shout>

-        <input>
-            <module>playlist</module>
-            <param name="type">basic</param>
-            <param name="file">playlist.txt</param>
-            <!-- random play -->
-            <param name="random">0</param>
-            <!-- if the playlist get updated that start at the beginning -->
-            <param name="restart-after-reread">0</param>
-            <!-- if set to 1 , plays once through, then exits. -->
-            <param name="once">0</param>
-        </input>
-
-		<!-- Stream instance
-            You may have one or more instances here. This allows you to
-            send the same input data to one or more servers (or to different
-            mountpoints on the same server). Each of them can have different
-            parameters. This is primarily useful for a) relaying to multiple
-            independent servers, and b) encoding/reencoding to multiple
-            bitrates.
-            If one instance fails (for example, the associated server goes
-            down, etc), the others will continue to function correctly.
-            This example defines two instances as two mountpoints on the
-            same server.  -->
-        <instance>
-            <!-- Server details:
-                You define hostname and port for the server here, along with
-                the source password and mountpoint.  -->
-            <hostname>localhost</hostname>
-            <port>8000</port>
-            <password>hackme</password>
-            <mount>/example1.ogg</mount>
-
-            <!-- Reconnect parameters:
-                When something goes wrong (e.g. the server crashes, or the
-                network drops) and ices disconnects from the server, these
-                control how often it tries to reconnect, and how many times
-                it tries to reconnect. Delay is in seconds.
-                If you set reconnectattempts to -1, it will continue
-                indefinately. Suggest setting reconnectdelay to a large value
-                if you do this.
-            -->
-            <reconnectdelay>2</reconnectdelay>
-            <reconnectattempts>5</reconnectattempts>
-
-            <!-- maxqueuelength:
-                This describes how long the internal data queues may be. This
-                basically lets you control how much data gets buffered before
-                ices decides it can't send to the server fast enough, and
-                either shuts down or flushes the queue (dropping the data)
-                and continues.
-                For advanced users only.
-            -->
-            <maxqueuelength>80</maxqueuelength>
-
-            <!-- Live encoding/reencoding:
-                Currrently, the parameters given here for encoding MUST
-                match the input data for channels and sample rate. That
-                restriction will be relaxed in the future.
-            -->
-            <encode>
-                <nominal-bitrate>64000</nominal-bitrate> <!-- bps. e.g. 64000 for 64 kbps -->
-                <samplerate>44100</samplerate>
-                <channels>2</channels>
-            </encode>
-        </instance>
-
+                <encode>
+                    <!-- VBR mode can be selected via quality or nominal-bitrate -->
+                    <!-- selecting enabling managed has to be done separately as -->
+                    <!-- it's slower, but can be used to limit a high threshold -->
+                    <nominal-bitrate>65536</nominal-bitrate> <!-- bps. e.g. 64 kbps -->
+                </encode>
+            </instance>
+        </runner>
</stream>
</ices>

Added: icecast/branches/kh/ices/conf/ices-switch.xml
===================================================================
--- icecast/trunk/ices/conf/ices-switch.xml	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-switch.xml	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<ices>
+
+    <background>0</background> <!-- run in background? (unimplemented) -->
+    <logpath>/tmp</logpath> <!-- where logs, etc go. -->
+    <logfile>ices.log</logfile>
+    <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+    <logsize>2048</logsize> <!-- the size the log file must be before rotation -->
+    <consolelog>0</consolelog> <!-- logfile is ignored if this is set to 1 -->
+    <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
+
+	<stream>
+
+        <input>
+            <module>oss</module>
+            <param name="rate">44100</param>        <!-- samplerate -->
+            <param name="channels">2</param>        <!-- number of channels -->
+            <param name="device">/dev/dsp</param>   <!-- audio device -->
+            <param name="metadatafilename">metadata</param>
+        </input>
+
+        <input>
+            <module>pcm</module>
+            <param name="rate">44100</param>
+            <param name="channels">2</param>
+            <param name="metadatafilename">test</param>
+            <param name="command">/usr/bin/ogg123</param>
+            <param name="arg">--device=raw</param>
+            <param name="arg">--quiet</param>
+            <param name="arg">--file=-</param>
+            <param name="arg">/path/or/url.ogg</param>
+            <!-- limit of about 10 args currently -->
+        </input>
+
+        <runner>
+            <instance>
+                <shout>
+                    <hostname>localhost</hostname>
+                    <port>8000</port>
+                    <password>hackme</password>
+                    <mount>/example1.ogg</mount>
+
+                    <reconnectdelay>5</reconnectdelay>
+                    <reconnectattempts>-1</reconnectattempts>
+                </shout>
+
+                <encode>
+                    <quality>0</quality>
+                </encode>
+            </instance>
+
+            <instance>
+                <shout>
+                    <hostname>localhost</hostname>
+                    <port>8000</port>
+                    <password>hackme</password>
+                    <mount>/example2.ogg</mount>
+
+                    <reconnectdelay>5</reconnectdelay>
+                    <reconnectattempts>-1</reconnectattempts>
+                </shout>
+
+                <!-- by default passthru is enabled which means that any
+                     vorbis data coming from the input will inot be
+                     re-encoded. Set to 0 if you want to re-encode  tor
+                     the settings in the encode section -->
+                <passthru>0</passthru>
+
+                <encode>
+                    <quality>-1</quality>
+                </encode>
+            </instance>
+        </runner>
+
+	</stream>
+</ices>

Modified: icecast/branches/kh/ices/configure.in
===================================================================
--- icecast/trunk/ices/configure.in	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/configure.in	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script.
-AC_INIT([IceS], 2.0-Beta4, [icecast at xiph.org])
+AC_INIT([IceS], [2.0-kh59], [karl at xiph.org])
AC_PREREQ(2.54)
AC_CONFIG_SRCDIR(src/ices.c)

@@ -8,42 +8,36 @@
AM_MAINTAINER_MODE

AC_PROG_CC
-AM_PROG_LIBTOOL
+AC_PROG_LIBTOOL
+XIPH_C__FUNC__

dnl Set some options based on environment

-dnl BSD headers break when _XOPEN_SOURCE is defined but without it seems
-dnl to be fine
case "$host" in
-   *bsd*)
+   *bsd*|*irix*)
;;
*) AC_DEFINE(_XOPEN_SOURCE, 600, [Define if you have POSIX and XPG specifications])
;;
esac
case "$host" in
*-*-irix*)
-        DEBUG="-g -signed -D_REENTRANT"
-        XIPH_CPPFLAGS="-O2 -w -signed -D_REENTRANT"
-        PROFILE="-p -g3 -O2 -signed -D_REENTRANT"
+        DEBUG="-g -signed"
+        XIPH_CFLAGS="-O2 -w -signed"
+        PROFILE="-p -g3 -O2 -signed"
;;
*-*-solaris*)
-        AC_DEFINE(__EXTENSIONS__, 1, [define to 1 to get resolve header problem on solaris])
-        DEBUG="-v -g -D_REENTRANT"
-        XIPH_CPPFLAGS="-xO4 -fast -w -fsimple -native -xcg92 -D_REENTRANT"
-        PROFILE="-v -xpg -g -xO4 -fast -native -fsimple -xcg92 -Dsuncc -D_REENTRANT"
+        AC_DEFINE(__EXTENSIONS__, 1, [define to 1 for IPv6 functions on solaris])
+        DEBUG="-v -g"
+        XIPH_CFLAGS="-xO4 -fast -w -fsimple -native -xcg92"
+        PROFILE="-v -xpg -g -xO4 -fast -native -fsimple -xcg92 -Dsuncc"
;;
*)
-        DEBUG="-g -D_REENTRANT"
-        XIPH_CPPFLAGS="-O -D_REENTRANT"
-        PROFILE="-g -p -D_REENTRANT"
+        AC_DEFINE(GNU_SOURCE, 1, [define to 1 to enable some GNU extensions])
+        DEBUG="-g"
+        XIPH_CFLAGS="-ffast-math -fsigned-char"
+        PROFILE="-g -p"
;;
esac
-if test -n "$GCC"; then
-        AC_DEFINE(_GNU_SOURCE, ,[Define if you have POSIX and GNU specifications])
-        XIPH_CPPFLAGS="-ffast-math -fsigned-char"
-        DEBUG="-g"
-        PROFILE="-g -pg"
-fi

dnl Checks for programs.

@@ -51,15 +45,16 @@

dnl Checks for header files.
AC_HEADER_STDC
-AC_CHECK_HEADERS([stropts.h])
+AC_HEADER_TIME
+AC_CHECK_HEADERS([grp.h alloca.h stropts.h])

dnl Check for OSS

AC_CHECK_HEADER(sys/soundcard.h, have_oss=yes, have_oss=no)
AC_CHECK_HEADER(machine/soundcard.h, have_oss=yes, )
-AM_CONDITIONAL(HAVE_OSS,test "$have_oss" = yes)
+AM_CONDITIONAL(HAVE_OSS_AUDIO,test "$have_oss" = yes)
if test "$have_oss" = yes; then
-    AC_DEFINE(HAVE_OSS,,[Define to enable OSS input module])
+    AC_DEFINE(HAVE_OSS_AUDIO,,[Define to enable OSS input module])
fi

dnl Check for Sun audio
@@ -82,60 +77,111 @@

dnl Check for ALSA audio

-AC_CHECK_HEADER(alsa/asoundlib.h, have_alsa=yes, have_alsa=no)
-AM_CONDITIONAL(HAVE_ALSA,test "$have_alsa" = yes)
+AC_CHECK_HEADER([alsa/asoundlib.h], ices_have_alsa=yes, ices_have_alsa=no, [
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+])
+AM_CONDITIONAL(HAVE_ALSA_AUDIO,test "$ices_have_alsa" = yes)

-if test "$have_alsa" = yes; then
+if test "$ices_have_alsa" = yes; then
ALSA_LIBS="-lasound"
-   AC_DEFINE(HAVE_ALSA, ,[Define to enable ALSA input module])
+   AC_DEFINE(HAVE_ALSA_AUDIO, ,[Define to enable ALSA input module])
fi

+dnl Check for JACK audio
+
+AC_CHECK_HEADER([jack/jack.h], ices_have_jack=yes, ices_have_jack=no, [
+])
+AM_CONDITIONAL(HAVE_JACK,test "$ices_have_jack" = yes)
+
+if test "$ices_have_jack" = yes; then
+   JACK_LIBS="-ljack"
+   AC_DEFINE(HAVE_JACK, ,[Define to enable JACK input module])
+fi
+
+
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST

+xt_ices_LIBS="$LIBS"
+LIBS=""
+AC_SEARCH_LIBS([nanosleep],[rt],
+        [AC_DEFINE([HAVE_NANOSLEEP],1,[Define if you have the nanosleep function])
+        XIPH_VAR_PREPEND([XIPH_LIBS],[$LIBS])
+        ])
+LIBS="$xt_ices_LIBS"
+
+
dnl Check for types
AC_CHECK_TYPES([uint64_t],,AC_ERROR([could not find a uint64_t type]))

dnl Checks for library functions.
+AC_FUNC_STRERROR_R

-XIPH_PATH_XML
-XIPH_VAR_APPEND([XIPH_CFLAGS], [$XML_CFLAGS])
-XIPH_VAR_PREPEND([XIPH_LIBS], [$XML_LIBS])
+dnl -- configure options --

-XIPH_PATH_SHOUT(, AC_MSG_ERROR([must have libshout installed!]))
-if test "$SHOUT_THREADSAFE" != "yes"
-then
-  AC_MSG_ERROR([This libshout isn't threadsafe])
+dnl deal with xml-config
+AC_MSG_RESULT([checking for XML configuration])
+AC_ARG_VAR([XMLCONFIG],[XML configuration program])
+AC_ARG_WITH(xml-config,
+    [AC_HELP_STRING([--with-xml-config=PATH],
+                    [use xml-config in PATH to find libxml])],
+    [XMLCONFIG="$withval"],
+    [AC_PATH_PROGS(XMLCONFIG, [xml2-config xml-config], "")]
+)
+if test "x$XMLCONFIG" = "x"; then
+    AC_MSG_ERROR([XML configuration could not be found])
fi
+if ! test -x "$XMLCONFIG"; then
+    AC_MSG_ERROR([$XMLCONFIG cannot be executed])
+fi
+XML_LIBS="$($XMLCONFIG --libs)"
+XML_CFLAGS="$($XMLCONFIG --cflags)"
+xt_ices_LIBS="$LIBS"
+xt_ices_CFLAGS="$CFLAGS"
+LIBS="$XML_LIBS"
+CFLAGS="$CFLAGS $XML_CFLAGS"
+AC_CHECK_FUNC(xmlParseFile,, [AC_MSG_ERROR([There was a problem linking with libxml])])
+LIBS="$xt_ices_LIBS"
+CFLAGS="$xt_ices_CFLAGS"
+XIPH_VAR_PREPEND([XIPH_LIBS],[$XML_LIBS])
+XIPH_VAR_APPEND([XIPH_CFLAGS],[$XML_CFLAGS])

+XIPH_PATH_SHOUT(,AC_MSG_ERROR([Need libshout-kh release]))
XIPH_VAR_APPEND([XIPH_CPPFLAGS], [$SHOUT_CPPFLAGS])
XIPH_VAR_APPEND([XIPH_CFLAGS], [$SHOUT_CFLAGS])
-XIPH_VAR_PREPEND([XIPH_LIBS], [$SHOUT_LIBS])
+XIPH_VAR_PREPEND([XIPH_LIBS], [-lvorbisenc $SHOUT_LIBS])

-XIPH_PATH_VORBIS(, AC_MSG_ERROR([must have Ogg Vorbis v1.0 installed!]))
-XIPH_VAR_APPEND([XIPH_CPPFLAGS], [$VORBIS_CFLAGS $VORBISENC_CFLAGS])
-XIPH_VAR_PREPEND([XIPH_LIBS], [$VORBISENC_LIBS $VORBIS_LIBS])
+xt_save_LIBS=$LIBS
+xt_save_CFLAGS=$CFLAGS
+LIBS="$SHOUT_LIBS $LIBS"
+CFLAGS="$CFLAGS $SHOUT_CFLAGS"
+AC_CHECK_FUNCS(sched_get_priority_max)
+CFLAGS=$xt_save_CFLAGS
+LIBS=$xt_save_LIBS

dnl Make substitutions

AC_SUBST(ALSA_LIBS)
+AC_SUBST(JACK_LIBS)
AC_SUBST(XML_LIBS)
AC_SUBST(XML_CFLAGS)
AC_SUBST(LIBTOOL_DEPS)
AC_SUBST(DEBUG)
AC_SUBST(PROFILE)
+AC_SUBST(XIPH_LIBS)
AC_SUBST(XIPH_CFLAGS)
AC_SUBST(XIPH_CPPFLAGS)
-AC_SUBST(XIPH_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(LIBS)
+AC_SUBST(LDFLAGS)

-AC_OUTPUT(
-  Makefile
-  conf/Makefile
-  doc/Makefile
-  debian/Makefile
-  src/Makefile
-   src/log/Makefile
-   src/timing/Makefile
-   src/thread/Makefile
-   src/avl/Makefile
-)
+AC_OUTPUT(Makefile conf/Makefile src/Makefile src/log/Makefile)

Modified: icecast/branches/kh/ices/src/Makefile.am
===================================================================
--- icecast/trunk/ices/src/Makefile.am	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/Makefile.am	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,16 +1,18 @@
## Process this with automake to create Makefile.in

-AUTOMAKE_OPTIONS = foreign 1.6
+AUTOMAKE_OPTIONS = foreign

-SUBDIRS = log timing thread avl
+SUBDIRS = log

bin_PROGRAMS = ices
+AM_CFLAGS = @XIPH_CFLAGS@
AM_CPPFLAGS = @XIPH_CPPFLAGS@
-AM_CFLAGS = @XIPH_CFLAGS@
+CFLAGS= -g -O20

+EXTRA_DIST = thread net timing avl
EXTRA_ices_SOURCES = im_oss.c im_sun.c im_alsa.c

-if HAVE_OSS
+if HAVE_OSS_AUDIO
oss = im_oss.c
endif

@@ -18,20 +20,24 @@
sun = im_sun.c
endif

-if HAVE_ALSA
+if HAVE_ALSA_AUDIO
alsa = im_alsa.c
endif

-dist_noinst_HEADERS = cfgparse.h input.h inputmodule.h im_playlist.h signals.h stream.h reencode.h encode.h playlist_basic.h logging.h im_stdinpcm.h event.h stream_shared.h metadata.h audio.h resample.h im_sun.h im_oss.h im_alsa.h
+if HAVE_JACK
+jack = im_jack.c
+endif

-ices_SOURCES = input.c cfgparse.c stream.c ices.c signals.c im_playlist.c reencode.c encode.c playlist_basic.c im_stdinpcm.c stream_shared.c metadata.c playlist_script.c audio.c resample.c $(oss) $(sun) $(alsa)
+input_hdrs = im_pcm.h im_sun.h im_oss.h im_alsa.h im_jack.h im_playlist.h playlist_basic.h
+output_hdrs = om_shout.h om_file.h
+dist_noinst_HEADERS = cfgparse.h inputmodule.h signals.h runner.h reencode.h encode.h logging.h stream.h metadata.h audio.h resample.h $(input_hdrs) $(output_hdrs)

-ices_LDADD = log/libicelog.la \
-             timing/libicetiming.la \
-             thread/libicethread.la \
-             avl/libiceavl.la \
-             @ALSA_LIBS@ @XIPH_LIBS@
+input_srcs = im_pcm.c im_playlist.c playlist_basic.c playlist_script.c $(oss) $(sun) $(alsa) $(jack)
+output_srcs = om_shout.c om_file.c
+ices_SOURCES = input.c cfgparse.c runner.c ices.c signals.c reencode.c encode.c stream.c metadata.c audio.c resample.c $(input_srcs) $(output_srcs)

+ices_LDADD = log/libicelog.la @ALSA_LIBS@ @JACK_LIBS@ @XIPH_LIBS@
+
debug:
$(MAKE) all CFLAGS="@DEBUG@"


Modified: icecast/branches/kh/ices/src/audio.c
===================================================================
--- icecast/trunk/ices/src/audio.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/audio.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -2,7 +2,7 @@
* stereo->mono downmixing
* resampling
*
- * $Id: audio.c,v 1.10 2003/08/01 22:38:04 karl Exp $
+ * $Id: audio.c,v 1.6 2003/03/15 02:24:18 karl Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -11,9 +11,8 @@
* it under the terms of this license. A copy should be included
* with this source.
*/
-
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include <config.h>
#endif

#include <stdio.h>
@@ -28,15 +27,15 @@
#define MODULE "audio/"
#include "logging.h"

-downmix_state *downmix_initialise(void) {
-    downmix_state *state = calloc(1, sizeof(downmix_state));
+struct downmix *downmix_initialise(void) {
+    struct downmix *state = calloc(1, sizeof(struct downmix));

LOG_INFO0("Enabling stereo->mono downmixing");

return state;
}

-void downmix_clear(downmix_state *s) {
+void downmix_clear(struct downmix *s) {
if(s) {
if (s->buffer)
free(s->buffer);
@@ -44,7 +43,7 @@
}
}

-void downmix_buffer_float(downmix_state *s, float **buf, int samples)
+void downmix_buffer_float(struct downmix *s, float **buf, int samples, unsigned channels)
{
int i;

@@ -57,13 +56,18 @@
}

for(i=0; i < samples; i++) {
-        s->buffer[i] = (buf[0][i] + buf[1][i])*0.5;
+        unsigned count = 0;
+        float res = 0.0;
+        for (count = 0; count < channels; count++)
+            res += buf [count][i];
+        s->buffer[i] = res/channels;
}

}


-void downmix_buffer(downmix_state *s, signed char *buf, int len, int be)
+#if 0
+void downmix_buffer(struct downmix *s, signed char *buf, int len, int be)
{
int samples = len/4;
int i;
@@ -89,10 +93,11 @@
}
}
}
+#endif

-resample_state *resample_initialise(int channels, int infreq, int outfreq)
+struct resample *resample_initialise(int channels, int infreq, int outfreq)
{
-    resample_state *state = calloc(1, sizeof(resample_state));
+    struct resample *state = calloc(1, sizeof(struct resample));
int failed = 1;

do
@@ -126,7 +131,7 @@
return state;
}

-void resample_clear(resample_state *s)
+void resample_clear(struct resample *s)
{
int c;

@@ -148,7 +153,7 @@
}
}

-void resample_buffer(resample_state *s, signed char *buf, int buflen, int be)
+void resample_buffer(struct resample *s, signed char *buf, int buflen, int be)
{
int c,i;
buflen /= 2*s->channels; /* bytes -> samples conversion */
@@ -181,7 +186,7 @@
resample_buffer_float(s, s->convbuf, buflen);
}

-void resample_buffer_float(resample_state *s, float **buf, int buflen)
+void resample_buffer_float(struct resample *s, float **buf, int buflen)
{
int c;
int res;
@@ -208,7 +213,7 @@

}

-void resample_finish(resample_state *s)
+void resample_finish(struct resample *s)
{
int ret;


Modified: icecast/branches/kh/ices/src/audio.h
===================================================================
--- icecast/trunk/ices/src/audio.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/audio.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -17,13 +17,15 @@

#include "resample.h"

-typedef struct {
+struct downmix
+{
float *buffer;
int buflen;
-} downmix_state;
+};

-typedef struct {
-    resampler_state resampler;
+struct resample
+{
+    struct resampler resampler;
int channels;

float **buffers;
@@ -32,18 +34,18 @@

float **convbuf;
int convbuflen;
-} resample_state;
+};

-downmix_state *downmix_initialise(void);
-void downmix_clear(downmix_state *s);
-void downmix_buffer(downmix_state *s, signed char *buf, int len, int be);
-void downmix_buffer_float(downmix_state *s, float **buf, int samples);
+struct downmix *downmix_initialise(void);
+void downmix_clear(struct downmix *s);
+/* void downmix_buffer(struct downmix *s, signed char *buf, int len, int be); */
+void downmix_buffer_float(struct downmix *s, float **buf, int samples,  unsigned channels);

-resample_state *resample_initialise(int channels, int infreq, int outfreq);
-void resample_clear(resample_state *s);
-void resample_buffer(resample_state *s, signed char *buf, int buflen, int be);
-void resample_buffer_float(resample_state *s, float **buf, int buflen);
-void resample_finish(resample_state *s);
+struct resample *resample_initialise(int channels, int infreq, int outfreq);
+void resample_clear(struct resample *s);
+void resample_buffer(struct resample *s, signed char *buf, int buflen, int be);
+void resample_buffer_float(struct resample *s, float **buf, int buflen);
+void resample_finish(struct resample *s);

#endif


Modified: icecast/branches/kh/ices/src/cfgparse.c
===================================================================
--- icecast/trunk/ices/src/cfgparse.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/cfgparse.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,8 +1,7 @@
/* cfgparse.c
- * - cfgparse file reading code, plus default settings.
+ * - setup file reading code, plus default settings.
*
- * $Id: cfgparse.c,v 1.10 2004/03/11 17:16:08 karl Exp $
- *
+ * Copyright (c) 2002-4 Karl Heyes <karl at xiph.org>
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
* This program is distributed under the terms of the GNU General
@@ -12,495 +11,430 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>

+#ifdef HAVE_STRINGS_H
+ #include <strings.h>
+#endif
+
+#include <ogg/ogg.h>
+
/* these might need tweaking for other systems */
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>

-#include <thread/thread.h>
+#include "thread/thread.h"

#include "cfgparse.h"
-#include "stream.h"
+#include "thread/thread.h"
+#include "inputmodule.h"
+#include "encode.h"
+#include "runner.h"
+#include "om_file.h"
+#include "om_shout.h"

-#define DEFAULT_BACKGROUND 0
+#define DEFAULT_PLAYLIST_MODULE "playlist"
+#define DEFAULT_STREAM_NAME "unnamed ices stream"
+#define DEFAULT_STREAM_GENRE "ices unset"
+#define DEFAULT_STREAM_DESCRIPTION "no description set"
#define DEFAULT_LOGPATH "/tmp"
#define DEFAULT_LOGFILE "ices.log"
#define DEFAULT_LOGLEVEL 1
#define DEFAULT_LOGSIZE 2048
#define DEFAULT_LOG_STDERR 1
-#define DEFAULT_STREAM_NAME "unnamed ices stream"
-#define DEFAULT_STREAM_GENRE "ices unset"
-#define DEFAULT_STREAM_DESCRIPTION "no description set"
-#define DEFAULT_PLAYLIST_MODULE "playlist"
-#define DEFAULT_HOSTNAME "localhost"
-#define DEFAULT_PORT 8000
-#define DEFAULT_PASSWORD "password"
-#define DEFAULT_USERNAME NULL
-#define DEFAULT_MOUNT "/stream.ogg"
-#define DEFAULT_MANAGED 0
-#define DEFAULT_MIN_BITRATE -1
-#define DEFAULT_NOM_BITRATE -1
-#define DEFAULT_MAX_BITRATE -1
-#define DEFAULT_QUALITY 3
-#define DEFAULT_REENCODE 0
-#define DEFAULT_DOWNMIX 0
-#define DEFAULT_RESAMPLE 0
-#define DEFAULT_RECONN_DELAY 2
-#define DEFAULT_RECONN_ATTEMPTS 10
-#define DEFAULT_MAXQUEUELENGTH 100 /* Make it _BIG_ by default */
-#define DEFAULT_SAVEFILENAME NULL /* NULL == don't save */
+#define DEFAULT_BACKGROUND 0

-/* helper macros so we don't have to write the same
-** stupid code over and over
-*/
-#define SET_STRING(x) \
-    do {\
-        if (x) free(x);\
-        (x) = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);\
-    } while (0)
+#ifdef DEBUG_CFG
+#define dprintf(...)      printf(__VA_ARGS__)
+#else
+#define dprintf(...)
+#endif

-#define SET_INT(x) \
-    do {\
-        char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);\
-        (x) = atoi(tmp);\
-        if (tmp) free(tmp);\
-    } while (0)
+/* this is the global config variable */
+config_t *ices_config;

-#define SET_FLOAT(x) \
-    do {\
-        char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);\
-        (x) = atof(tmp);\
-        if (tmp) free(tmp);\
-    } while (0)

-#define SET_PARM_STRING(p,x) \
-        do {\
-                if (x) free(x);\
-                (x) = (char *)xmlGetProp(node, p);\
-    } while (0)
+int get_xml_float (xmlNodePtr node, void *x)
+{
+    char *tmp = (char *)xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
+    if (tmp == NULL)
+        return -1;
+    *(float*)x = atof (tmp);
+    xmlFree(tmp);
+    return 0;
+}


-/* this is the global config variable */
-config_t *ices_config;
+int get_xml_bool (xmlNodePtr node, void *x)
+{
+    char *str = (char *)xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
+    if (str == NULL)
+        return -1;
+    if (strcasecmp (str, "true") == 0)
+        *(int*)x = 1;
+    else
+        if (strcasecmp (str, "yes") == 0)
+            *(int*)x = 1;
+        else
+            *(int*)x = strtol (str, NULL, 0)==0 ? 0 : 1;
+    xmlFree (str);
+    xmlMemoryDump();
+    return 0;
+}

-static int _using_default_instance = 1;

-static void _free_instances(instance_t *instance)
+int get_xml_int (xmlNodePtr node, void *x)
{
-    instance_t *next;
-
-    next = NULL;
-    do
-    {
-        config_free_instance(instance);
-        next = instance->next;
-        free(instance);
-
-        instance = next;
-    } while (next != NULL);
+    char *tmp = (char *)xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
+    if (tmp == NULL)
+        return -1;
+    *(int*)x = strtol(tmp, NULL, 0);
+    xmlFree(tmp);
+    xmlMemoryDump();
+    return 0;
}

-void config_free_instance(instance_t *instance)
+
+int get_xml_string (xmlNodePtr node, void *x)
{
-    if (instance->hostname) free(instance->hostname);
-    if (instance->password) free(instance->password);
-    if (instance->user) free(instance->user);
-    if (instance->mount) free(instance->mount);
-    if (instance->queue)
+    char *str = (char *)xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
+    char *p = *(char**)x;
+    if (str == NULL)
+        return -1;
+    if (p)
{
-        thread_mutex_destroy(&instance->queue->lock);
-        free(instance->queue);
+        dprintf ("freeing \"%s\" (%p) alloc \"%s\"\n", p, p, str);
+        xmlMemoryDump();
+        xmlFree (p);
}
+    *(char **)x = str;
+    xmlMemoryDump();
+    // xmlMemoryStrdup (str);
+    return 0;
}

-static void _set_instance_defaults(instance_t *instance)
+
+int parse_xml_tags (const char *id, xmlNodePtr node, const struct cfg_tag *args)
{
-    instance->hostname = strdup(DEFAULT_HOSTNAME);
-    instance->port = DEFAULT_PORT;
-    instance->password = strdup(DEFAULT_PASSWORD);
-    instance->user = DEFAULT_USERNAME;
-    instance->mount = strdup(DEFAULT_MOUNT);
-    instance->managed = DEFAULT_MANAGED;
-    instance->min_br = DEFAULT_MIN_BITRATE;
-    instance->nom_br = DEFAULT_NOM_BITRATE;
-    instance->max_br = DEFAULT_MAX_BITRATE;
-    instance->quality = DEFAULT_QUALITY;
-    instance->encode = DEFAULT_REENCODE;
-    instance->downmix = DEFAULT_DOWNMIX;
-    instance->resampleinrate = DEFAULT_RESAMPLE;
-    instance->resampleoutrate = DEFAULT_RESAMPLE;
-    instance->reconnect_delay = DEFAULT_RECONN_DELAY;
-    instance->reconnect_attempts = DEFAULT_RECONN_ATTEMPTS;
-    instance->max_queue_length = DEFAULT_MAXQUEUELENGTH;
-    instance->savefilename = DEFAULT_SAVEFILENAME;
+    int ret = 0;

-    instance->queue = calloc(1, sizeof(buffer_queue));
-    thread_mutex_create(&instance->queue->lock);
+    for (; node != NULL && ret == 0; node = node->next)
+    {
+        const struct cfg_tag *argp;

-    instance->next = NULL;
+        if (xmlIsBlankNode (node) || strcmp ((char*)node->name, "comment") == 0 ||
+                strcmp ((char*)node->name, "text") == 0)
+            continue;
+        argp = args;
+        dprintf ("Checking \"%s\"\n", node->name);
+        while (argp->name)
+        {
+            dprintf ("  against \"%s\"\n", argp->name);
+            if (strcmp ((const char*)node->name, argp->name) == 0)
+            {
+                ret = argp->retrieve (node, argp->storage);
+                break;
+            }
+            argp++;
+        }
+        if (argp->name == NULL)
+            fprintf (stderr, "%s:  unknown element \"%s\" parsing \"%s\"\n", ices_config->cfgfile, node->name, id);
+    }
+    dprintf (" Ret is %d\n", ret);
+    return ret;
}

-static void _parse_resample(instance_t *instance,xmlDocPtr doc, xmlNodePtr node)
+
+
+
+static void _free_params(module_param_t *param)
{
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
+	while (param != NULL)
+	{
+        module_param_t *next;

-        if(strcmp(node->name, "in-rate") == 0)
-            SET_INT(instance->resampleinrate);
-        else if(strcmp(node->name, "out-rate") == 0)
-            SET_INT(instance->resampleoutrate);
-    } while((node = node->next));
+		if (param->name) xmlFree(param->name);
+		if (param->value) xmlFree(param->value);
+		next = param->next;
+		free(param);
+		param = next;
+    }
}

-static void _parse_encode(instance_t *instance,xmlDocPtr doc, xmlNodePtr node)
+
+static input_module_t *_free_input_module (input_module_t *mod)
{
-    instance->encode = 1;
-    do {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
+    input_module_t *next = mod->next;

-        if (strcmp(node->name, "nominal-bitrate") == 0)
-            SET_INT(instance->nom_br);
-        else if (strcmp(node->name, "minimum-bitrate") == 0)
-            SET_INT(instance->min_br);
-        else if (strcmp(node->name, "maximum-bitrate") == 0)
-            SET_INT(instance->max_br);
-        else if (strcmp(node->name, "quality") == 0)
-            SET_FLOAT(instance->quality);
-        else if (strcmp(node->name, "samplerate") == 0)
-            SET_INT(instance->samplerate);
-        else if (strcmp(node->name, "channels") == 0)
-            SET_INT(instance->channels);
-        else if (strcmp(node->name, "managed") == 0)
-            SET_INT(instance->managed);
-    } while ((node = node->next));
+    if (mod->module != DEFAULT_PLAYLIST_MODULE)
+        xmlFree (mod->module);

+    if (mod->module_params)
+        _free_params(mod->module_params);
+
+    free (mod);
+    return next;
}

-static void _parse_metadata(instance_t *instance, config_t *config,
-        xmlDocPtr doc, xmlNodePtr node)
+
+
+static int _parse_input_param (xmlNodePtr node, void *arg)
{
-    do
-    {
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
+    module_param_t *param, *p;
+    input_module_t *inp = arg;

-        if (strcmp(node->name, "name") == 0) {
-            if(instance)
-                SET_STRING(instance->stream_name);
-            else
-                SET_STRING(config->stream_name);
+    param = (module_param_t *)calloc(1, sizeof(module_param_t));
+    if (param)
+    {
+        if (get_xml_param_name (node, "name", &param->name) < 0)
+        {
+            free (param);
+            return -1;
}
-        else if (strcmp(node->name, "genre") == 0) {
-            if(instance)
-                SET_STRING(instance->stream_genre);
-            else
-                SET_STRING(config->stream_genre);
+        if (get_xml_string (node, &param->value) < 0)
+        {
+            xmlFree (param->name);
+            free (param);
+            return -1;
}
-        else if (strcmp(node->name, "description") == 0) {
-            if(instance)
-                SET_STRING(instance->stream_description);
-            else
-                SET_STRING(config->stream_description);
+        if (inp->module_params == NULL)
+            inp->module_params = param;
+        else
+        {
+            p = inp->module_params;
+            while (p->next != NULL) p = p->next;
+            p->next = param;
}
-	else if (strcmp(node->name, "url") == 0) {
-	    if(instance)
-		SET_STRING(instance->stream_url);
-	    else
-		SET_STRING(config->stream_url);
-	}
-    } while ((node = node->next));
+        return 0;
+    }
+    return -1;
}

-static void _parse_instance(config_t *config, xmlDocPtr doc, xmlNodePtr node)
+
+static int _known_module (xmlNodePtr node, void *arg)
{
-    instance_t *instance, *i;
+    int current_module = 0, ret = -1;
+    input_module_t *inp = arg;

-    instance = (instance_t *)calloc(1, sizeof(instance_t));
-    _set_instance_defaults(instance);
+    if (get_xml_string (node, &inp->module) < 0)
+        return -1;

-    do
+    while (modules[current_module].name)
{
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "hostname") == 0)
-            SET_STRING(instance->hostname);
-        else if (strcmp(node->name, "port") == 0)
-            SET_INT(instance->port);
-        else if (strcmp(node->name, "password") == 0)
-            SET_STRING(instance->password);
-        else if (strcmp(node->name, "username") == 0)
-            SET_STRING(instance->user);
-        else if (strcmp(node->name, "yp") == 0)
-            SET_INT(instance->public_stream);
-        else if (strcmp(node->name, "savefile") == 0)
-            SET_STRING(instance->savefilename);
-        else if (strcmp(node->name, "mount") == 0)
-            SET_STRING(instance->mount);
-        else if(strcmp(node->name, "reconnectdelay") == 0)
-            SET_INT(instance->reconnect_delay);
-        else if(strcmp(node->name, "reconnectattempts") == 0)
-            SET_INT(instance->reconnect_attempts);
-        else if(strcmp(node->name, "maxqueuelength") == 0)
-            SET_INT(instance->max_queue_length);
-        else if(strcmp(node->name, "downmix") == 0)
-            SET_INT(instance->downmix);
-        else if(strcmp(node->name, "resample") == 0)
-            _parse_resample(instance, doc, node->xmlChildrenNode);
-        else if (strcmp(node->name, "encode") == 0)
-            _parse_encode(instance, doc, node->xmlChildrenNode);
-        else if (strcmp(node->name, "metadata") == 0)
-            _parse_metadata(instance, config, doc, node->xmlChildrenNode);
-    } while ((node = node->next));
-
-    instance->next = NULL;
-
-    if (_using_default_instance)
-    {
-        _using_default_instance = 0;
-        _free_instances(config->instances);
-        config->instances = NULL;
+        if(!strcmp(inp->module, modules[current_module].name))
+        {
+            inp->initialise_module = modules[current_module].initialise;
+            inp->open_module = modules[current_module].open;
+            inp->close_module = modules[current_module].close;
+            inp->shutdown_module = modules[current_module].shutdown;
+            ret = 0;
+            break;
+        }
+        current_module++;
}
-
-    if (config->instances == NULL)
-    {
-        config->instances = instance;
-    }
-    else
-    {
-        i = config->instances;
-        while (i->next != NULL) i = i->next;
-        i->next = instance;
-    }
+    return ret;
}

-static void _parse_input(config_t *config, xmlDocPtr doc, xmlNodePtr node)
+static int _parse_input(xmlNodePtr node, void *arg)
{
-    module_param_t *param, *p;
+    config_t *config = arg;
+    int save = 1;
+    input_module_t *mod = calloc (1, sizeof (input_module_t));
+    static unsigned input_id = 0;

-    do
+    while (mod)
{
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "module") == 0)
-            SET_STRING(config->playlist_module);
-        else if (strcmp(node->name, "param") == 0) {
-            param = (module_param_t *)calloc(1, sizeof(module_param_t));
-            SET_PARM_STRING("name", param->name);
-            SET_STRING(param->value);
-            param->next = NULL;
-
-            if (config->module_params == NULL)
-            {
-                config->module_params = param;
-            }
-            else
-            {
-                p = config->module_params;
-                while (p->next != NULL) p = p->next;
-                p->next = param;
-            }
+        struct cfg_tag input_tag[] =
+        {
+            { "module",     _known_module,      mod },
+            { "param",      _parse_input_param, mod },
+            { "buffers",    get_xml_int,        &mod->buffer_count },
+            { "prealloc",   get_xml_int,        &mod->prealloc_count },
+            { "save",       get_xml_int,        &save },
+            { NULL, NULL, NULL }
+        };
+        if (parse_xml_tags ("input", node->xmlChildrenNode, input_tag))
+            break;
+        mod->id = ++input_id;
+        /* mod->save = save; */
+        if (config->inputs == NULL)
+            config->inputs = mod;
+        else
+        {
+            input_module_t *i = config->inputs;
+            while (i->next != NULL) i = i->next;
+            i->next = mod;
}
-    } while ((node = node->next));
+        return 0;
+    }
+    if (mod)  free (mod);
+    return -1;
}

-static void _parse_stream(config_t *config, xmlDocPtr doc, xmlNodePtr node)
+
+
+
+static int _parse_stream (xmlNodePtr node, void *arg)
{
-    do
+    config_t *config = arg;
+    struct cfg_tag stream_tag[] =
{
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
+        { "name",           get_xml_string,     &config->stream_name },
+        { "genre",          get_xml_string,     &config->stream_genre },
+        { "description",    get_xml_string,     &config->stream_description },
+        { "url",            get_xml_string,     &config->stream_url },
+        { "once",           get_xml_bool,       &config->input_once_thru },
+        { "input",          _parse_input,       config },
+        { "runner",         parse_runner,       config },
+        { NULL, NULL, NULL }
+    };

-        if (strcmp(node->name, "metadata") == 0)
-            _parse_metadata(NULL, config, doc, node->xmlChildrenNode);
-        else if (strcmp(node->name, "input") == 0)
-            _parse_input(config, doc, node->xmlChildrenNode);
-        else if (strcmp(node->name, "instance") == 0)
-            _parse_instance(config, doc, node->xmlChildrenNode);
-    } while ((node = node->next));
+    return parse_xml_tags ("stream", node->xmlChildrenNode, stream_tag);
}

-static void _parse_root(config_t *config, xmlDocPtr doc, xmlNodePtr node)
+
+static int _parse_root (xmlNodePtr node, void *arg)
{
-    do
+    config_t *config = arg;
+    char *user = NULL;
+
+    if (config && node && strcmp((char*)node->name, "ices") == 0)
{
-        if (node == NULL) break;
-        if (xmlIsBlankNode(node)) continue;
-
-        if (strcmp(node->name, "background") == 0)
-            SET_INT(config->background);
-        else if (strcmp(node->name, "logpath") == 0)
-            SET_STRING(config->logpath);
-        else if (strcmp(node->name, "logfile") == 0)
-            SET_STRING(config->logfile);
-        else if (strcmp(node->name, "loglevel") == 0)
-            SET_INT(config->loglevel);
-        else if (strcmp(node->name, "logsize") == 0)
-            SET_INT(config->logsize);
-        else if (strcmp(node->name, "consolelog") == 0)
-            SET_INT(config->log_stderr);
-        else if (strcmp(node->name, "pidfile") == 0)
-            SET_STRING(config->pidfile);
-        else if (strcmp(node->name, "stream") == 0)
-            _parse_stream(config, doc, node->xmlChildrenNode);
-    } while ((node = node->next));
+        int realtime = 1;
+        struct cfg_tag ices_tag[] =
+        {
+            { "background", get_xml_bool,       &config->background },
+            { "realtime",   get_xml_bool,       &realtime },
+            { "user",       get_xml_string,     &user },
+            { "logpath",    get_xml_string,     &config->logpath },
+            { "logfile",    get_xml_string,     &config->logfile },
+            { "loglevel",   get_xml_int,        &config->loglevel },
+            { "logsize",    get_xml_int,        &config->logsize },
+            { "consolelog", get_xml_bool,       &config->log_stderr },
+            { "pidfile",    get_xml_string,     &config->pidfile },
+            { "stream",     _parse_stream,      config },
+            { NULL, NULL, NULL }
+        };
+        if (parse_xml_tags ("ices", node->xmlChildrenNode, ices_tag))
+            return -1;
+
+        config->realtime = realtime;
+        config->user = user;
+        return 0;
+    }
+    return -1;
}

-static void _set_defaults(config_t *c)
+static config_t *allocate_config(void)
{
-    instance_t *instance;
+	config_t *c = (config_t *)calloc(1, sizeof(config_t));

-    c->background = DEFAULT_BACKGROUND;
-    c->logpath = strdup(DEFAULT_LOGPATH);
-    c->logfile = strdup(DEFAULT_LOGFILE);
-    c->logsize = DEFAULT_LOGSIZE;
-    c->loglevel = DEFAULT_LOGLEVEL;
+    if (c == NULL)
+        return NULL;
+	c->background = DEFAULT_BACKGROUND;
+	c->logpath = xmlStrdup (DEFAULT_LOGPATH);
+	c->logfile = xmlStrdup (DEFAULT_LOGFILE);
+	c->logsize = DEFAULT_LOGSIZE;
+	c->loglevel = DEFAULT_LOGLEVEL;
c->log_stderr = DEFAULT_LOG_STDERR;
+	c->stream_name = xmlStrdup (DEFAULT_STREAM_NAME);
+	c->stream_genre = xmlStrdup (DEFAULT_STREAM_GENRE);
+	c->stream_description = xmlStrdup (DEFAULT_STREAM_DESCRIPTION);
+    dprintf ("name initially at  %p \"%s\"\n", c->stream_name, c->stream_name);
+    dprintf ("desc initially at  %p \"%s\"\n", c->stream_description, c->stream_description);
+    dprintf ("genre initially at %p \"%s\"\n", c->stream_genre, c->stream_genre);

-    c->stream_name = strdup(DEFAULT_STREAM_NAME);
-    c->stream_genre = strdup(DEFAULT_STREAM_GENRE);
-    c->stream_description = strdup(DEFAULT_STREAM_DESCRIPTION);
-    c->stream_url = NULL;
-
-    c->playlist_module = strdup(DEFAULT_PLAYLIST_MODULE);
-
-    c->module_params = NULL;
-
-    instance = (instance_t *)malloc(sizeof(instance_t));
-    _set_instance_defaults(instance);
-    c->instances = instance;
+    return c;
}

-static void _free_params(module_param_t *param)
-{
-    module_param_t *next;
-    next = NULL;
-    do
-    {
-        if (param->name) free(param->name);
-        if (param->value) free(param->value);
-        next = param->next;
-        free(param);
-
-        param = next;
-    } while (next != NULL);
-}
-
void config_initialize(void)
{
-    ices_config = (config_t *)calloc(1, sizeof(config_t));
-    _set_defaults(ices_config);
-    srand(time(NULL));
-    xmlInitParser();
+	srand(time(NULL));
}

void config_shutdown(void)
{
-    if (ices_config == NULL) return;
+    struct runner *run;
+    input_module_t *mod;

-    if (ices_config->module_params != NULL)
-    {
-        _free_params(ices_config->module_params);
-        ices_config->module_params = NULL;
-    }
+	if (ices_config == NULL) return;

-    if (ices_config->instances != NULL)
+    mod = ices_config->inputs;
+    while (mod)
{
-        _free_instances(ices_config->instances);
-        ices_config->instances = NULL;
+        mod->shutdown_module (mod);
+        mod = _free_input_module (mod);
}

-    free(ices_config);
-    ices_config = NULL;
-    xmlCleanupParser();
-}
+    if (ices_config->logpath != DEFAULT_LOGPATH)
+        xmlFree (ices_config->logpath);

-int config_read(const char *fn)
-{
-    xmlDocPtr doc;
-    xmlNodePtr node;
+    if (ices_config->logfile != DEFAULT_LOGFILE)
+        xmlFree (ices_config->logfile);

-    if (fn == NULL || strcmp(fn, "") == 0) return -1;
+    if (ices_config->stream_name != DEFAULT_STREAM_NAME)
+        xmlFree (ices_config->stream_name);

-    doc = xmlParseFile(fn);
-    if (doc == NULL) return -1;
+    if (ices_config->stream_genre !=  DEFAULT_STREAM_GENRE)
+        xmlFree (ices_config->stream_genre);

-    node = xmlDocGetRootElement(doc);
-    if (node == NULL || strcmp(node->name, "ices") != 0)
-    {
-        xmlFreeDoc(doc);
-        return 0;
-    }
+    if (ices_config->stream_description != DEFAULT_STREAM_DESCRIPTION)
+        xmlFree (ices_config->stream_description);

-    _parse_root(ices_config, doc, node->xmlChildrenNode);
+    if (ices_config->user)
+        xmlFree (ices_config->user);

-    xmlFreeDoc(doc);
-
-    return 1;
+    run = ices_config->runners;
+    while (run)
+        run = config_free_runner (run);
+
+	free(ices_config);
+	ices_config = NULL;
}

-void config_dump(void)
+int config_read(const char *fn)
{
-    config_t *c = ices_config;
-    module_param_t *param;
-    instance_t *i;
+    xmlDocPtr doc = NULL;
+    int ret = 0;
+    uid_t euid = geteuid();

-    fprintf(stderr, "ices config dump:\n");
-    fprintf(stderr, "background = %d\n", c->background);
-    fprintf(stderr, "logpath = %s\n", c->logpath);
-    fprintf(stderr, "logfile = %s\n", c->logfile);
-    fprintf(stderr, "loglevel = %d\n", c->loglevel);
-    fprintf(stderr, "\n");
-    fprintf(stderr, "stream_name = %s\n", c->stream_name);
-    fprintf(stderr, "stream_genre = %s\n", c->stream_genre);
-    fprintf(stderr, "stream_description = %s\n", c->stream_description);
-    fprintf(stderr, "stream_url = %s\n", c->stream_url ? c->stream_url : "");
-    fprintf(stderr, "\n");
-    fprintf(stderr, "playlist_module = %s\n", c->playlist_module);
-    param = c->module_params;
-    while(param)
+#ifdef _POSIX_SAVED_IDS
+    if (seteuid (getuid()) < 0)
+#else
+    uid_t ruid = getuid();
+    if (setreuid (euid, ruid) < 0)
+#endif
{
-        fprintf(stderr, "module_param: %s = %s\n", param->name, param->value);
-        param = param->next;
+        fprintf (stderr, "failed to drop priviledges for reading config file\n");
}
-    fprintf(stderr, "\ninstances:\n\n");
+    xmlInitParser ();
+    doc = xmlParseFile(fn);
+    xmlCleanupParser ();

-    i = c->instances;
-    while (i)
-    {
-        fprintf(stderr, "hostname = %s\n", i->hostname);
-        fprintf(stderr, "port = %d\n", i->port);
-        fprintf(stderr, "password = %s\n", i->password);
-        fprintf(stderr, "mount = %s\n", i->mount);
-        fprintf(stderr, "minimum bitrate = %d\n", i->min_br);
-        fprintf(stderr, "nominal bitrate = %d\n", i->nom_br);
-        fprintf(stderr, "maximum bitrate = %d\n", i->max_br);
-        fprintf(stderr, "quality = %f\n", i->quality);
-        fprintf(stderr, "managed = %d\n", i->managed);
-        fprintf(stderr, "reencode = %d\n", i->encode);
-        fprintf(stderr, "reconnect: %d times at %d second intervals\n",
-                i->reconnect_attempts, i->reconnect_delay);
-        fprintf(stderr, "maxqueuelength = %d\n", i->max_queue_length);
-        fprintf(stderr, "\n");
+    ices_config = allocate_config();
+    ices_config->cfgfile = fn;
+    if (_parse_root (xmlDocGetRootElement (doc), ices_config))
+        ret = -1;

-        i = i->next;
-    }
-
-    fprintf(stderr, "\n");
+    xmlFreeDoc (doc);
+#ifdef _POSIX_SAVED_IDS
+    if (seteuid (euid) < 0)
+#else
+    if (setreuid (ruid, euid) < 0)
+#endif
+        fprintf (stderr, "failed to reset privilidges after reading config file\n");
+
+    return ret;
}


-
-
-
-

Modified: icecast/branches/kh/ices/src/cfgparse.h
===================================================================
--- icecast/trunk/ices/src/cfgparse.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/cfgparse.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,8 @@
-/* config.h
- * - configuration, and global structures built from config
+/* cfgparse.h
+ * - setup, and global structures built from setup
*
- * $Id: cfgparse.h,v 1.7 2004/03/11 17:16:08 karl Exp $
- *
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2002 Karl Heyes <karl at pts.tele2.co.uk>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -11,106 +10,134 @@
* with this source.
*/

-#ifndef __CONFIG_H__
-#define __CONFIG_H__
+#ifndef __CFGPARSE_H__
+#define __CFGPARSE_H__

-#include "stream.h"
+#define USE_PIPES
+
+typedef struct _config_tag config_t;
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include <shout/shout.h>
+#include "thread/thread.h"
+
+
#include "inputmodule.h"

-typedef struct _module_param_tag
+extern int realtime_enabled;
+
+struct cfg_tag
{
-    char *name;
-    char *value;
+    const char *name;
+    int (*retrieve) (xmlNodePtr node, void *x);
+    void *storage;
+};

-    struct _module_param_tag *next;
-} module_param_t;

-/* FIXME: orward declaraction because my headers are a mess. */
-struct buffer_queue;

-typedef struct _instance_tag
+struct output_module
{
-    char *hostname;
-    int port;
-    char *password;
-    char *user;
-    char *mount;
-    int reconnect_delay;
-    int reconnect_attempts;
-    int encode;
-    int downmix;
-    int resampleinrate;
-    int resampleoutrate;
-    int max_queue_length;
-    char *savefilename;
+    int (*output_send) (struct output_module *mod, ogg_packet *op, unsigned samples);
+    void (*output_clear) (struct output_module *mod);
+    int disabled;
+    struct output_state *parent;
+    struct output_module *next;
+    void *specific;
+    int need_headers;
+    ogg_stream_state os;
+    long serial;
+    int in_use;
+    long packetno;
+    ogg_int64_t granule, start_pos;
+    int initial_packets;
+    int reset;
+};

-    /* local metadata */
-    char *stream_name;
-    char *stream_genre;
-    char *stream_description;
-    char *stream_url;

-    /* Parameters for re-encoding */
-    int managed;
-    int min_br, nom_br, max_br;
-    float quality;
-    int samplerate;
-    int channels;
-
-    /* private */
-    FILE *savefile;
-    int buffer_failures;
-    int died;
-    int kill;
-    int skip;
-    int public_stream;
-    int wait_for_critical;

-    struct buffer_queue *queue;
+struct output_state
+{
+    struct output_module *head;

-    struct _instance_tag *next;
-} instance_t;
+    long serial;
+    int in_use;
+    int headers;
+    ogg_packet packets[3];
+    int new_headers;
+    ogg_int64_t granules, start_pos;
+    unsigned granule_overlap;

-typedef struct _config_tag
+    char *name;
+    char *genre;
+    char *description;
+    char *url;
+
+    ogg_stream_state in_os;
+    vorbis_info  vi;
+    vorbis_comment  vc;
+    int info_in_use;
+};
+
+
+
+struct _config_tag
{
-    int background;
+    int  background;
+    int  realtime;
+    char *pidfile;
char *logpath;
char *logfile;
+    int  loglevel;
unsigned logsize;
-    char *pidfile;
-    int loglevel;
-    int log_stderr;
+    int  log_stderr;
+    char *user;
+    const char *cfgfile;

-    /* <stream> */
-
/* <metadata> */
-
char *stream_name;
char *stream_genre;
char *stream_description;
char *stream_url;
-
-    /* <playlist> */
-
-    char *playlist_module;
-    module_param_t *module_params;

-    /* <instance> */
+    input_module_t *inputs;

-    instance_t *instances;
+    unsigned runner_count;
+    struct runner *runners;

/* private */
int log_id;
int shutdown;
-    char *metadata_filename;
-    cond_t queue_cond;
-    cond_t event_pending_cond;
-    mutex_t refcount_lock;
-    mutex_t flush_lock;
-    input_module_t *inmod;
+    int next_track;
+    int has_encoding;
+    int input_once_thru;
+
struct _config_tag *next;
-} config_t;
+};

+
+
+extern int get_xml_string (xmlNodePtr node, void *x);
+extern int get_xml_int (xmlNodePtr node, void *x);
+extern int get_xml_float (xmlNodePtr node, void *x);
+extern int get_xml_bool (xmlNodePtr node, void *x);
+
+int parse_xml_tags (const char *id, xmlNodePtr node, const struct cfg_tag *args);
+
+static __inline__ int get_xml_param_name (xmlNodePtr node, char *p, char **where)
+{
+    char *tmp = (char *)xmlGetProp(node, (void*)p);
+    if (tmp == NULL)
+        return -1;
+    *where = tmp;
+    return 0;
+}
+
+
extern config_t *ices_config;

void config_initialize(void);
@@ -119,9 +146,8 @@
int config_read(const char *filename);
void config_dump(void);

-void config_free_instance(instance_t *instance);

-#endif /* __CONFIG_H__ */
+#endif /* __CFGPARSE_H__ */




Modified: icecast/branches/kh/ices/src/encode.c
===================================================================
--- icecast/trunk/ices/src/encode.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/encode.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,10 @@
/* encode.c
* - runtime encoding of PCM data.
*
- * $Id: encode.c,v 1.19 2003/12/22 14:01:09 karl Exp $
+ * $Id: encode.c,v 1.12 2002/08/17 05:17:57 karl Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2002-3 Karl Heyes <karl at xiph.org>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -11,9 +12,7 @@
* with this source.
*/

-#ifdef HAVE_CONFIG_H
- #include <config.h>
-#endif
+#include <config.h>

#include <stdio.h>
#include <stdlib.h>
@@ -29,59 +28,103 @@
#define MODULE "encode/"
#include "logging.h"

-static mutex_t _serial_lock;

-static long _get_serial()
+struct encoder
{
-    static long prev_serial = 0;
-    long serial;
+    long magic;
+    vorbis_info vi;
+    vorbis_comment vc;
+    vorbis_dsp_state vd;
+    vorbis_block vb;
+
+    unsigned samplerate;
+    int in_use;
+    int in_header;
+
+    int flushed;
+    ogg_packet headers[3];
+};

-    thread_mutex_lock (&_serial_lock);
-    serial = prev_serial;
-    while (serial == prev_serial)
-        serial = rand();
-    prev_serial = serial;
-    thread_mutex_unlock (&_serial_lock);

-    return serial;
+
+void encode_free (struct encoder *s)
+{
+    if (s)
+    {
+        LOG_DEBUG0("Freeing encoder engine");
+
+        vorbis_block_clear (&s->vb);
+        vorbis_dsp_clear (&s->vd);
+        vorbis_comment_clear (&s->vc);
+        vorbis_info_clear (&s->vi);
+        free (s);
+    }
}

-void encode_clear(encoder_state *s)
+
+struct encoder *encode_create (void)
{
-    if(s)
+    struct encoder *s = calloc(1, sizeof(struct encoder));
+    if (s == NULL)
+        return NULL;
+    s->magic=1;
+    vorbis_comment_init (&s->vc);
+    return s;
+}
+
+
+void encode_comment (struct encoder *s, char *str)
+{
+    if (s)
{
-        LOG_DEBUG0("Clearing encoder engine");
-        ogg_stream_clear(&s->os);
-        vorbis_block_clear(&s->vb);
-        vorbis_dsp_clear(&s->vd);
-        vorbis_info_clear(&s->vi);
-        free(s);
+        vorbis_comment_add (&s->vc, str);
}
}


-encoder_state *encode_initialise(int channels, int rate, int managed,
-        int min_br, int nom_br, int max_br, float quality, vorbis_comment *vc)
+int encode_setup (struct encoder *s, struct encoder_settings *settings)
{
-    encoder_state *s = calloc(1, sizeof(encoder_state));
-    ogg_packet h1,h2,h3;
+    float quality;
+    long nom_br, max_br, min_br, rate, channels;

-    /* Have vorbisenc choose a mode for us */
-    vorbis_info_init(&s->vi);
+    /* do some sanity check */
+    if (settings->quality < -1)
+    {
+        LOG_WARN1 ("Quality setting of %f is too low, setting to -1.0", settings->quality);
+        settings->quality = -1.0;
+    }
+    if (settings->quality > 10.0)
+    {
+        LOG_WARN1 ("Quality setting of %f is too high, setting to 10.0", settings->quality);
+        settings->quality = 10.0;
+    }

-    if (max_br < 0 && nom_br < 0 && min_br < 0)
-       managed = 0;
-    if (managed == 0 && nom_br >= 0)
+    nom_br = settings->nom_br;
+    min_br = settings->min_br;
+    max_br = settings->max_br;
+    rate = settings->encode_rate;
+    channels = settings->encode_channels;
+
+    /* If none of these are set, it's obviously not supposed to be managed */
+    if (settings->nom_br < 0 && min_br < 0 && max_br < 0)
+        settings->managed = 0;
+
+    if (settings->managed == 0 && nom_br >= 0)
if (min_br >= 0 || max_br >= 0)
-            managed = 1;
+            settings->managed = 1;
+
+    quality = settings->quality;
+
+    /* Have vorbisenc choose a mode for us */
+    vorbis_info_init (&s->vi);
+
do
{
-        if (managed)
+        if (settings->managed)
{
LOG_INFO5("Encoder initialising with bitrate management: %d "
"channels, %d Hz, minimum bitrate %d, nominal %d, "
"maximum %d", channels, rate, min_br, nom_br, max_br);
-
if (vorbis_encode_setup_managed (&s->vi, channels,
rate, max_br, nom_br, min_br))
break;
@@ -90,31 +133,17 @@
{
if (nom_br < 0)
{
-                LOG_INFO3("Encoder initialising in VBR mode: %d channel(s), "
+                LOG_INFO3 ("Encoder initialising in VBR mode: %d channel(s), "
"%d Hz, quality %f", channels, rate, quality);
if (min_br > 0 || max_br > 0)
-                    LOG_INFO0("ignoring min/max bitrate, not supported in VBR "
+                    LOG_WARN0 ("ignoring min/max bitrate, not supported in VBR "
"mode, use nominal-bitrate instead");
-                if (vorbis_encode_setup_vbr(&s->vi, channels, rate, quality*0.1))
+                if (vorbis_encode_setup_vbr (&s->vi, channels, rate, quality*0.1))
break;
-
-#if 0
-                if (max_br > 0 || min_br > 0)
-                {
-                    struct ovectl_ratemanage_arg ai;
-                    if (vorbis_encode_ctl(&s->vi, OV_ECTL_RATEMANAGE_GET, &ai))
-                        break;
-                    ai.bitrate_hard_min = min_br;
-                    ai.bitrate_hard_max = max_br;
-                    ai.management_active = 1;
-                    if (vorbis_encode_ctl(&s->vi, OV_ECTL_RATEMANAGE_SET, &ai))
-                        break;
-                }
-#endif
}
else
{
-                LOG_INFO3("Encoder initialising in VBR mode: %d "
+                LOG_INFO3 ("Encoder initialising in VBR mode: %d "
"channels, %d Hz, nominal %d", channels, rate, nom_br);
if (vorbis_encode_setup_managed (&s->vi, channels,
rate, max_br, nom_br, max_br))
@@ -123,181 +152,111 @@
break;
}
}
-        if (vorbis_encode_setup_init(&s->vi))
+
+        if (vorbis_encode_setup_init (&s->vi))
break;

-        vorbis_analysis_init(&s->vd, &s->vi);
-        vorbis_block_init(&s->vd, &s->vb);
+        vorbis_analysis_init (&s->vd, &s->vi);
+        vorbis_block_init (&s->vd, &s->vb);

-        ogg_stream_init(&s->os, _get_serial());
+        vorbis_comment_add (&s->vc, "EncodedBy=" PACKAGE_STRING);
+        vorbis_analysis_headerout (&s->vd, &s->vc, &s->headers[0],&s->headers[1],&s->headers[2]);

-        vorbis_analysis_headerout(&s->vd, vc, &h1,&h2,&h3);
-        ogg_stream_packetin(&s->os, &h1);
-        ogg_stream_packetin(&s->os, &h2);
-        ogg_stream_packetin(&s->os, &h3);
+        s->in_header = 3;
+        s->samplerate = settings->samplerate;
+        s->in_use = 1;

-        s->in_header = 1;
-        s->samplerate = rate;
-        s->samples_in_current_page = 0;
-        s->prevgranulepos = 0;
-
-        return s;
+        return 0;
} while (0);

LOG_INFO0("Failed to configure encoder, verify settings");
vorbis_info_clear(&s->vi);
-    free (s);
-    return NULL;
+
+    return -1;
}

-void encode_data_float(encoder_state *s, float **pcm, int samples)
+
+void encode_data_float(struct encoder *s, float **pcm, size_t samples)
{
-    float **buf;
+    float **buf, **src, **dest;
int i;
+    unsigned size;

-    buf = vorbis_analysis_buffer(&s->vd, samples);
-
-    for(i=0; i < s->vi.channels; i++)
+    if (samples == 0)
{
-        memcpy(buf[i], pcm[i], samples*sizeof(float));
+        LOG_DEBUG0 ("request for encoding 0 samples");
+        return;
}
+    if (s->magic != 1) printf ("structure has gone bad\n");

-    vorbis_analysis_wrote(&s->vd, samples);
+    buf = vorbis_analysis_buffer(&s->vd, samples);

-    s->samples_in_current_page += samples;
-}
-
-/* Requires little endian data (currently) */
-void encode_data(encoder_state *s, signed char *buf, int bytes, int bigendian)
-{
-    float **buffer;
-    int i,j;
-    int channels = s->vi.channels;
-    int samples = bytes/(2*channels);
-
-    buffer = vorbis_analysis_buffer(&s->vd, samples);
-
-    if(bigendian)
+    i=s->vi.channels;
+    src = pcm;
+    dest = buf;
+    size = samples*sizeof(float);
+    for(; i ; i--)
{
-        for(i=0; i < samples; i++)
-        {
-            for(j=0; j < channels; j++)
-            {
-                buffer[j][i]=((buf[2*(i*channels + j)]<<8) |
-                              (0x00ff&(int)buf[2*(i*channels + j)+1]))/32768.f;
-            }
-        }
+        memcpy(*dest, *src, size);
+        dest++;
+        src++;
}
-    else
-    {
-        for(i=0; i < samples; i++)
-        {
-            for(j=0; j < channels; j++)
-            {
-                buffer[j][i]=((buf[2*(i*channels + j) + 1]<<8) |
-                              (0x00ff&(int)buf[2*(i*channels + j)]))/32768.f;
-            }
-        }
-    }

vorbis_analysis_wrote(&s->vd, samples);
-
-    s->samples_in_current_page += samples;
}


-/* Returns:
- *   0     No output at this time
- *   >0    Page produced
- *
- * Caller should loop over this to ensure that we don't end up with
- * excessive buffering in libvorbis.
- */
-int encode_dataout(encoder_state *s, ogg_page *og)
-{
-    ogg_packet op;
-    int result;

-    if(s->in_header)
+int encode_packetout(struct encoder *s, ogg_packet *op)
+{
+    if (s->in_header)
{
-        result = ogg_stream_flush(&s->os, og);
-        if(result==0)
-        {
-            s->in_header = 0;
-            return encode_dataout(s,og);
-        }
-        else
-            return 1;
+        memcpy (op, &s->headers[3-s->in_header], sizeof (*op));
+#if 0
+        if ((op->packet = malloc (op->bytes)) == NULL)
+            return 0;
+        memcpy (op->packet, s->headers[3-s->in_header].packet, op->bytes);
+#endif
+        s->in_header--;
+        return 1;
}
-    else
+    while (vorbis_bitrate_flushpacket (&s->vd, op) == 0)
{
-        while(vorbis_analysis_blockout(&s->vd, &s->vb)==1)
-        {
-            vorbis_analysis(&s->vb, NULL);
-            vorbis_bitrate_addblock(&s->vb);
-
-            while(vorbis_bitrate_flushpacket(&s->vd, &op))
-                ogg_stream_packetin(&s->os, &op);
-        }
-
-        /* FIXME: Make this threshold configurable.
-         * We don't want to buffer too many samples in one page when doing
-         * live encoding - that's fine for non-live encoding, but breaks
-         * badly when doing things live.
-         * So, we flush the stream if we have too many samples buffered
-         */
-        if(s->samples_in_current_page > s->samplerate * 2)
-        {
-            /*LOG_DEBUG1("Forcing flush: Too many samples in current page (%d)",
-                    s->samples_in_current_page); */
-            result = ogg_stream_flush(&s->os, og);
-        }
-        else
-            result = ogg_stream_pageout(&s->os, og);
-
-        if(result==0)
+        if (vorbis_analysis_blockout (&s->vd, &s->vb) == 0)
return 0;
-        else /* Page found! */
-        {
-            s->samples_in_current_page -= ogg_page_granulepos(og) -
-                    s->prevgranulepos;
-            s->prevgranulepos = ogg_page_granulepos(og);
-            return 1;
-        }
+
+        vorbis_analysis (&s->vb, NULL);
+        vorbis_bitrate_addblock (&s->vb);
}
+    return 1;
}

-void encode_finish(encoder_state *s)
+
+int encode_endstream (struct encoder *s)
{
-    ogg_packet op;
vorbis_analysis_wrote(&s->vd, 0);
-
-    while(vorbis_analysis_blockout(&s->vd, &s->vb)==1)
-    {
-        vorbis_analysis(&s->vb, NULL);
-        vorbis_bitrate_addblock(&s->vb);
-        while(vorbis_bitrate_flushpacket(&s->vd, &op))
-            ogg_stream_packetin(&s->os, &op);
-    }
-
+    return 1;
}

-int encode_flush(encoder_state *s, ogg_page *og)
+
+int parse_encode (xmlNodePtr node, void *x)
{
-    int result = ogg_stream_pageout(&s->os, og);
+    struct encoder_settings *enc = x;
+    struct cfg_tag encode_tags[] =
+    {
+        { "nominal-bitrate", get_xml_int,   &enc->nom_br },
+        { "quality",         get_xml_float, &enc->quality },
+        { "minimum-bitrate", get_xml_int,   &enc->min_br },
+        { "maximum-bitrate", get_xml_int,   &enc->max_br },
+        { "managed",         get_xml_bool,  &enc->managed },
+        { NULL , NULL, NULL }
+    };

-    if(result<=0)
-        return 0;
-    else
-        return 1;
-}
+    enc->min_br = -1;
+    enc->max_br = -1;
+    enc->nom_br = -1;

-void encode_init()
-{
-    thread_mutex_create (&_serial_lock);
+    return parse_xml_tags ("encode", node->xmlChildrenNode, encode_tags);
}

-void encode_close()
-{
-    thread_mutex_destroy (&_serial_lock);
-}
+

Modified: icecast/branches/kh/ices/src/encode.h
===================================================================
--- icecast/trunk/ices/src/encode.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/encode.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,10 @@
/* encode.h
* - encoding functions
*
- * $Id: encode.h,v 1.5 2003/12/22 14:01:09 karl Exp $
+ * $Id: encode.h,v 1.3 2002/01/28 00:19:15 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2002-3 Karl Heyes <karl at karl@pts.tele2.co.uk>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -17,29 +18,46 @@
#include <ogg/ogg.h>
#include <vorbis/codec.h>

-typedef struct {
-    ogg_stream_state os;
-    vorbis_block vb;
-    vorbis_dsp_state vd;
-    vorbis_info vi;
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>

-    int samples_in_current_page;
-    int samplerate;
-    ogg_int64_t prevgranulepos;
-    int in_header;
-} encoder_state;

-encoder_state *encode_initialise(int channels, int rate, int managed,
-    int min_br, int nom_br, int max_br, float quality, vorbis_comment *vc);
-void encode_clear(encoder_state *s);
-void encode_data_float(encoder_state *s, float **pcm, int samples);
-void encode_data(encoder_state *s, signed char *buf, int bytes, int bigendian);
-int encode_dataout(encoder_state *s, ogg_page *og);
-void encode_finish(encoder_state *s);
-int encode_flush(encoder_state *s, ogg_page *og);
-void encode_init();
-void encode_close();
+struct encoder_settings
+{
+    int managed;
+    int min_br;
+    int max_br;
+    int nom_br;
+    float quality;
+    unsigned samplerate;
+    unsigned channels;
+    unsigned encode_rate;
+    unsigned encode_channels;
+};


+int parse_encode (xmlNodePtr node, void *x);
+
+struct encoder *encode_initialise (int channels, int rate, int managed,
+    int min_br, int nom_br, int max_br, float quality,
+	int serial, vorbis_comment *vc);
+
+int encode_setup (struct encoder *, struct encoder_settings *);
+struct encoder *encode_create (void);
+
+void encode_free (struct encoder *s);
+void encode_clear (struct encoder *s);
+void encode_comment (struct encoder *s, char *);
+
+int encode_endstream (struct encoder *s);
+void encode_data_float (struct encoder *s, float **pcm, size_t samples);
+int encode_packetout(struct encoder *s, ogg_packet *op);
+
+int encode_dataout (struct encoder *s, ogg_page *og);
+int encode_pageout (struct encoder *s, ogg_page *og);
+void encode_finish (struct encoder *s);
+int encode_flush (struct encoder *s, ogg_page *og);
+
+
#endif


Modified: icecast/branches/kh/ices/src/ices.c
===================================================================
--- icecast/trunk/ices/src/ices.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/ices.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* ices.c
* - Main startup, thread launching, and cleanup code.
*
- * $Id: ices.c,v 1.19 2004/03/11 17:16:08 karl Exp $
+ * $Id: ices.c,v 1.4 2002/01/29 09:20:27 msmith Exp $
*
* Copyright (c) 2001-2002 Michael Smith <msmith at labyrinth.net.au>
*
@@ -12,47 +12,105 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>

-#include <thread/thread.h>
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif

+#include "net/resolver.h"
+#include "thread/thread.h"
+
#include "cfgparse.h"
#include "stream.h"
#include "signals.h"
-#include "input.h"
+#include "inputmodule.h"

#define MODULE "ices-core/"
#include "logging.h"

-int main(int argc, char **argv)
+void start_processing ()
{
-    char logpath[FILENAME_MAX];
-    int log;
+    thread_type *thread;

-    if (argc != 2)
+    thread = thread_create("input", input_loop, NULL, 0);
+    if (thread == NULL)
+        return;
+    thread_join (thread);
+}
+
+int realtime_enabled = 0;
+
+
+static void drop_priviledges()
+{
+    struct passwd *pw = NULL;
+
+    if (ices_config->realtime)
{
-        fprintf(stderr, PACKAGE_STRING "\n"
-                "  (c) Copyright 2001-2004 The IceS Development Team <team at icecast.org>\n"
-                "        Michael Smith <msmith at icecast.org>\n"
-                "        Karl Heyes    <karl at xiph.org>\n"
-                "        and others\n"
-                "\n"
-                "Usage: \"ices config.xml\"\n");
-        return 1;
+        struct sched_param param;
+        int policy;
+
+        pthread_getschedparam (pthread_self(), &policy, &param);
+#ifdef HAVE_SCHED_GET_PRIORITY_MAX
+        param . sched_priority = sched_get_priority_max (SCHED_RR);
+#endif
+
+        if (pthread_setschedparam (pthread_self(), SCHED_RR, &param))
+            realtime_enabled = 0;
+        else
+            realtime_enabled = 1;
}

-    config_initialize();
+    if (ices_config->user)
+        pw = getpwnam (ices_config->user);

-    if (config_read(argv[1]) <= 0)
+    if (pw)
{
-        fprintf(stderr, "Failed to read config file \"%s\"\n", argv[1]);
-        goto fail;
+        setgid (pw->pw_gid);
+        setgroups (0, NULL);
+        setuid (pw->pw_uid);
}
+    else
+    {
+        setgid (getgid());
+        /* setgroups (0, NULL); */
+        setuid (getuid());
+    }
+}
+
+static char logpath[FILENAME_MAX];
+
+int main(int argc, char **argv)
+{
+	int log;
+
+	if (argc != 2)
+	{
+		fprintf(stderr, PACKAGE_STRING "\n"
+				"  (c) Copyright 2002-2004 Karl Heyes <karl at xiph.org>\n\n"
+				"Usage: \"ices config.xml\"\n");
+		return 1;
+	}
+
+	config_initialize();
+
+	/* right now you must have a config file, but we should probably
+	** make it so you can specify all parameters on the commandline
+	** too.
+	*/
+	if (config_read(argv[1]) < 0)
+	{
+		fprintf(stderr, "Failed to read config file \"%s\"\n", argv[1]);
+		goto fail;
+	}
if (ices_config->background)
{
int ret = 0;
@@ -61,11 +119,11 @@
{
case 0: break; /* child continues */
case -1: perror ("fork"); ret = -1;
-        default:
+        default:
exit (ret);
}

-        /* Disassociate process group and controlling terminal */
+        /* Disassociate process group and controlling terminal */
setsid();

/* Become a NON-session leader so that a */
@@ -74,35 +132,41 @@
{
case 0: break; /* child continues */
case -1: perror ("fork"); ret = -1;
-        default:
+        default:
exit (ret);
}
}
-
-    log_initialize();
+
+	log_initialize();
thread_initialize();
shout_init();
-    encode_init();
-    signals_setup();
+	signals_setup();
+    drop_priviledges();

-    snprintf(logpath, FILENAME_MAX, "%s/%s", ices_config->logpath,
-            ices_config->logfile);
-    if(ices_config->log_stderr)
+	snprintf (logpath, FILENAME_MAX, "%s/%s", ices_config->logpath,
+			ices_config->logfile);
+    if (ices_config->log_stderr)
log = log_open_file(stderr);
else
{
-        log = log_open(logpath);
+	    log = log_open (logpath);
if (log < 0)
-            fprintf (stderr, "unable to open log %s\n", logpath);
+            fprintf (stderr, "Failed to open log file %s\n", logpath);
log_set_trigger (log, ices_config->logsize);
}
-    /* Set the log level, if requested - defaults to 2 (WARN) otherwise */
-    if (ices_config->loglevel)
-        log_set_level(log, ices_config->loglevel);
+	/* Set the log level, if requested - defaults to 2 (WARN) otherwise */
+	if (ices_config->loglevel)
+		log_set_level(log, ices_config->loglevel);

-    ices_config->log_id = log;
+	ices_config->log_id = log;

-    LOG_INFO0(PACKAGE_STRING " started...");
+	LOG_INFO0("Streamer version " PACKAGE_STRING);
+	LOG_INFO1("libshout version %s ", shout_version(NULL, NULL,NULL));
+    if (realtime_enabled)
+        LOG_INFO0("realtime scheduling has been enabled");
+    else
+        LOG_INFO0("Unable to set realtime scheduling");
+
if (ices_config->pidfile != NULL)
{
FILE *f = fopen (ices_config->pidfile, "w");
@@ -111,32 +175,24 @@
fprintf (f, "%i", getpid());
fclose (f);
}
-        else
-        {
-            LOG_WARN1("pidfile \"%s\" cannot be written to", ices_config->pidfile);
-            xmlFree (ices_config->pidfile);
-            ices_config->pidfile = NULL;
-        }
}

-    /* Start the core streaming loop */
-    input_loop();
+    start_processing ();

+	LOG_INFO0("Shutdown in progress");
+
if (ices_config->pidfile)
remove (ices_config->pidfile);

-    LOG_INFO0("Shutdown complete");
+	log_close(log);

-    log_close(log);
-
fail:
-    encode_close();
shout_shutdown();
-    config_shutdown();
-    thread_shutdown();
-    log_shutdown();
+	config_shutdown();
+	thread_shutdown();
+	log_shutdown();

-    return 0;
+	return 0;
}



Modified: icecast/branches/kh/ices/src/im_alsa.c
===================================================================
--- icecast/trunk/ices/src/im_alsa.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_alsa.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_alsa.c
* - Raw PCM input from ALSA devices
*
- * $Id: im_alsa.c,v 1.8 2004/03/01 20:58:02 karl Exp $
+ * $Id: im_alsa.c,v 1.1 2002/12/29 10:28:30 msmith Exp $
*
* by Jason Chu <jchu at uvic.ca>, based
* on im_oss.c which is...
@@ -14,7 +14,7 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include <config.h>
#endif

#include <stdio.h>
@@ -27,13 +27,14 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
+#include <signals.h>

+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif

-#include <thread/thread.h>
#include "cfgparse.h"
-#include "stream.h"
#include "metadata.h"
-#include "inputmodule.h"

#define ALSA_PCM_NEW_HW_PARAMS_API
#include "im_alsa.h"
@@ -43,194 +44,234 @@

#define SAMPLES 8192

-static void close_module(input_module_t *mod)
+
+static int alsa_initialise_buffer (input_module_t *mod, input_buffer *ib)
{
-    if(mod)
-    {
-        if(mod->internal)
-        {
-            im_alsa_state *s = mod->internal;
-            if(s->fd != NULL)
-                snd_pcm_close(s->fd);
-            thread_mutex_destroy(&s->metadatalock);
-            free(s);
-        }
-        free(mod);
-    }
-}
-static int event_handler(input_module_t *mod, enum event_type ev, void *param)
-{
-    im_alsa_state *s = mod->internal;
+	im_alsa_state *s = mod->internal;
+    int i;
+    float **ptr;

-    switch(ev)
+    if ((ib->buf = calloc (s->channels, sizeof (float*))) == NULL)
+        return -1;
+    ptr = ib->buf;
+    for (i=s->channels ; i; i--)
{
-        case EVENT_SHUTDOWN:
-            close_module(mod);
-            break;
-        case EVENT_NEXTTRACK:
-            s->newtrack = 1;
-            break;
-        case EVENT_METADATAUPDATE:
-            thread_mutex_lock(&s->metadatalock);
-            if(s->metadata)
-            {
-                char **md = s->metadata;
-                while(*md)
-                    free(*md++);
-                free(s->metadata);
-            }
-            s->metadata = (char **)param;
-            s->newtrack = 1;
-            thread_mutex_unlock(&s->metadatalock);
-            break;
-        default:
-            LOG_WARN1("Unhandled event %d", ev);
+        if ((*ptr = calloc (sizeof(float), s->samples)) == NULL)
return -1;
+        ptr++;
}
-
+    ib->type = ICES_INPUT_PCM;
+    ib->subtype = INPUT_PCM_LE_16;
+    ib->critical = 0;
+    ib->channels = s->channels;
+    ib->samplerate = s->rate;
+    ib->mod = mod;
return 0;
}

-static void metadata_update(void *self, vorbis_comment *vc)
+
+void alsa_close_module(input_module_t *mod)
{
-    im_alsa_state *s = self;
-    char **md;
+	if (mod)
+	{
+		if (mod->internal)
+		{
+			im_alsa_state *s = mod->internal;
+			if (s->fd != NULL)
+				snd_pcm_close(s->fd);
+			free(s);
+		}
+	}
+}

-    thread_mutex_lock(&s->metadatalock);

-    md = s->metadata;
+void alsa_shutdown_module (input_module_t *mod)
+{
+    im_alsa_state *s = mod->internal;

-    if(md)
-    {
-        while(*md)
-            vorbis_comment_add(vc, *md++);
-    }
-
-    thread_mutex_unlock(&s->metadatalock);
+    LOG_INFO0 ("Shutdown ALSA module");
+    free (s);
+    mod->internal = NULL;
}

+
/* Core streaming function for this module
* This is what actually produces the data which gets streamed.
*
- * returns:  >0  Number of bytes read
- *            0  Non-fatal error.
- *           <0  Fatal error.
*/
-static int alsa_read(void *self, ref_buffer *rb)
+static int alsa_read(input_module_t *mod)
{
-    int result;
-    im_alsa_state *s = self;
+	int result;
+	im_alsa_state *s = mod->internal;
+    int dead_air;
+    input_buffer *ib;

-    rb->buf = malloc(SAMPLES*2*s->channels);
-    if(!rb->buf)
-        return -1;
-    result = snd_pcm_readi(s->fd, rb->buf, SAMPLES);
+    while (1)
+    {
+        if (s->user_terminated)
+        {
+            s->user_terminated = 0;
+            return 0;
+        }

-    rb->len = result*4;
-    rb->aux_data = s->rate*s->channels*2;
+        dead_air = 100;
+        while ((ib = input_alloc_buffer (mod)) == NULL)
+        {
+            if (dead_air == 100)
+                LOG_WARN0 ("will skip input for a short time");
+            result = snd_pcm_readi(s->fd, dead_audio, DEAD_AIR_BYTES/(2*s->channels));
+            if (--dead_air == 0)
+            {
+                mod->failures++;
+                return 0;
+            }
+        }

-    if(s->newtrack)
-    {
-        rb->critical = 1;
-        s->newtrack = 0;
-    }
+        result = snd_pcm_readi (s->fd, s->read_buffer, s->samples);

-    if (result == -EPIPE)
-    {
-        snd_pcm_prepare(s->fd);
-        return 0;
+        /* check what this condition means */
+        if (result == -EPIPE)
+        {
+            snd_pcm_prepare(s->fd);
+            input_free_buffer (ib);
+            return 0;
+        }
+        if (result == -EBADFD)
+        {
+            LOG_ERROR0("Bad descriptor passed to snd_pcm_readi");
+            input_free_buffer (ib);
+            return 0;
+        }
+        if (s->newtrack)
+        {
+            LOG_DEBUG0("setting buffer critical");
+            metadata_thread_signal (mod, ib);
+            ib->critical = 1;
+            s->newtrack = 0;
+        }
+        if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+        {
+            LOG_DEBUG0("Sending EOS");
+            s->newtrack = 1;
+            ib->eos = 1;
+            if (move_to_next_input || ices_config->shutdown)
+                s->user_terminated = 1;
+        }
+
+        ib->samples = result;
+        uninterleave_pcm_le ((signed char*)s->read_buffer, ib->channels, ib->samples, ib->buf);
+
+        input_adv_sleep ((uint64_t)ib->samples * 1000000 / s->rate);
+        send_for_processing (mod, ib);
+        input_sleep ();
}
-    else if (result == -EBADFD)
-    {
-        LOG_ERROR0("Bad descriptor passed to snd_pcm_readi");
-        free(rb->buf);
-        return -1;
-    }

-    return rb->len;
+	return 0;
}

-input_module_t *alsa_open_module(module_param_t *params)
+int alsa_init_module(input_module_t *mod)
{
-    input_module_t *mod = calloc(1, sizeof(input_module_t));
-    im_alsa_state *s;
-    module_param_t *current;
-    char *device = "plughw:0,0"; /* default device */
-    int format = AFMT_S16_LE;
-    int channels, rate;
-    int use_metadata = 1; /* Default to on */
-    unsigned int buffered_time, exact_rate;
-    int dir;
+	im_alsa_state *s;
+	module_param_t *current;
+	char *device = "plughw:0,0"; /* default device */
+	int channels, rate;
+    unsigned int samples;

-    snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
-    snd_pcm_hw_params_t *hwparams;
+    mod->name = "ALSA";
+	mod->type = ICES_INPUT_PCM;
+	mod->subtype = INPUT_PCM_LE_16;
+	mod->getdata = alsa_read;
+    /* mod->release_input_buffer = alsa_return_buffer; */
+    mod->initialise_buffer = alsa_initialise_buffer;
+    mod->buffer_count = ices_config->runner_count*10 + 5;
+    mod->prealloc_count = ices_config->runner_count * 4;

-    int err;
+	mod->internal = calloc(1, sizeof(im_alsa_state));
+    if (mod->internal == NULL)
+        return -1;
+	s = mod->internal;

-    mod->type = ICES_INPUT_PCM;
-    mod->subtype = INPUT_PCM_LE_16;
-    mod->getdata = alsa_read;
-    mod->handle_event = event_handler;
-    mod->metadata_update = metadata_update;
+	s->fd = NULL; /* Set it to something invalid, for now */
+	rate = 44100; /* Defaults */
+	channels = 2;
+    samples = SAMPLES;
+    s->periods = 2;
+    s->buffer_time = 500000;

-    mod->internal = calloc(1, sizeof(im_alsa_state));
-    s = mod->internal;
+	current = mod->module_params;

-    s->fd = NULL; /* Set it to something invalid, for now */
-    s->rate = 44100; /* Defaults */
-    s->channels = 2;
-    s->buffer_time = 500000;
-    s->periods = 2;
+	while(current)
+	{
+		if(!strcmp(current->name, "rate"))
+			rate = atoi(current->value);
+		else if(!strcmp(current->name, "channels"))
+			channels = atoi(current->value);
+		else if(!strcmp(current->name, "device"))
+			device = current->value;
+		else if(!strcmp(current->name, "samples"))
+			samples = atoi(current->value);
+		else if(!strcmp(current->name, "periods"))
+			s->periods = atoi(current->value);
+		else if(!strcmp(current->name, "buffer-time"))
+			s->buffer_time = atoi(current->value);
+		else if(!strcmp(current->name, "metadatafilename"))
+			mod->metadata_filename = current->value;
+		else
+			LOG_WARN1("Unknown parameter %s for alsa module", current->name);

-    thread_mutex_create(&s->metadatalock);
+		current = current->next;
+	}
+    s->rate = rate;
+    s->channels = channels;
+    s->samples = samples;
+    s->device = device;

-    current = params;
+    s->read_buffer_len = s->samples*2*s->channels;
+    s->read_buffer = malloc (s->read_buffer_len);

-    while(current)
-    {
-        if(!strcmp(current->name, "rate"))
-            s->rate = atoi(current->value);
-        else if(!strcmp(current->name, "channels"))
-            s->channels = atoi(current->value);
-        else if(!strcmp(current->name, "device"))
-            device = current->value;
-        else if(!strcmp(current->name, "metadata"))
-            use_metadata = atoi(current->value);
-        else if(!strcmp(current->name, "metadatafilename"))
-            ices_config->metadata_filename = current->value;
-        else if(!strcmp(current->name, "buffer-time"))
-            s->buffer_time = atoi (current->value) * 1000;
-        else if(!strcmp(current->name, "periods"))
-            s->periods = atoi (current->value);
-        else
-            LOG_WARN1("Unknown parameter %s for alsa module", current->name);
+    LOG_INFO0("ALSA driver initialised");

-        current = current->next;
-    }
+    return 0;
+}

-    snd_pcm_hw_params_alloca(&hwparams);
+int alsa_open_module(input_module_t *mod)
+{
+	snd_pcm_hw_params_t *hwparams;
+	snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
+	im_alsa_state *s = mod->internal;
+    int err;
+    unsigned int exact_rate;
+    int dir = 1;

-    if ((err = snd_pcm_open(&s->fd, device, stream, 0)) < 0)
-    {
-        LOG_ERROR2("Failed to open audio device %s: %s", device, snd_strerror(err));
-        goto fail;
-    }
+	snd_pcm_hw_params_alloca(&hwparams);

-    if ((err = snd_pcm_hw_params_any(s->fd, hwparams)) < 0)
-    {
-        LOG_ERROR1("Failed to initialize hwparams: %s", snd_strerror(err));
-        goto fail;
-    }
-    if ((err = snd_pcm_hw_params_set_access(s->fd, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
-    {
-        LOG_ERROR1("Error setting access: %s", snd_strerror(err));
-        goto fail;
-    }
-    if ((err = snd_pcm_hw_params_set_format(s->fd, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
-    {
-        LOG_ERROR1("Couldn't set sample format to SND_PCM_FORMAT_S16_LE: %s", snd_strerror(err));
-        goto fail;
-    }
+	if ((err = snd_pcm_open(&s->fd, s->device, stream, 0)) < 0)
+	{
+		LOG_ERROR2("Failed to open audio device %s: %s", s->device, snd_strerror(err));
+		goto fail;
+	}
+
+	if ((err = snd_pcm_hw_params_any(s->fd, hwparams)) < 0)
+	{
+		LOG_ERROR1("Failed to initialize hwparams: %s", snd_strerror(err));
+		goto fail;
+	}
+	if ((err = snd_pcm_hw_params_set_access(s->fd, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+	{
+		LOG_ERROR1("Error setting access: %s", snd_strerror(err));
+		goto fail;
+	}
+	if ((err = snd_pcm_hw_params_set_format(s->fd, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
+	{
+		LOG_ERROR1("Couldn't set sample format to SND_PCM_FORMAT_S16_LE: %s", snd_strerror(err));
+		goto fail;
+	}
+	if ((err = snd_pcm_hw_params_set_channels(s->fd, hwparams, s->channels)) < 0)
+	{
+		LOG_ERROR1("Error setting channels: %s", snd_strerror(err));
+		goto fail;
+	}
+
exact_rate = s->rate;
err = snd_pcm_hw_params_set_rate_near(s->fd, hwparams, &exact_rate, 0);
if (err < 0)
@@ -244,11 +285,7 @@
"%d instead", s->rate, exact_rate);
goto fail;
}
-    if ((err = snd_pcm_hw_params_set_channels(s->fd, hwparams, s->channels)) < 0)
-    {
-        LOG_ERROR1("Error setting channels: %s", snd_strerror(err));
-        goto fail;
-    }
+
if ((err = snd_pcm_hw_params_set_buffer_time_near(s->fd, hwparams, &s->buffer_time, &dir)) < 0)
{
LOG_ERROR2("Error setting buffer time %u: %s", s->buffer_time, snd_strerror(err));
@@ -259,31 +296,24 @@
LOG_ERROR2("Error setting %u periods: %s", s->periods, snd_strerror(err));
goto fail;
}
-    if ((err = snd_pcm_hw_params(s->fd, hwparams)) < 0)
-    {
-        LOG_ERROR1("Error setting HW params: %s", snd_strerror(err));
-        goto fail;
-    }

-    /* We're done, and we didn't fail! */
-    LOG_INFO1 ("Opened audio device %s", device);
-    LOG_INFO4 ("using %d channel(s), %d Hz, buffer %u ms (%u periods)",
-            s->channels, s->rate, s->buffer_time/1000, s->periods);
+	if ((err = snd_pcm_hw_params(s->fd, hwparams)) < 0)
+	{
+		LOG_ERROR1("Error setting HW params: %s", snd_strerror(err));
+		goto fail;
+	}
+    s->newtrack = 1;

-    if(use_metadata)
-    {
-        LOG_INFO0("Starting metadata update thread");
-        if(ices_config->metadata_filename)
-            thread_create("im_alsa-metadata", metadata_thread_signal, mod, 1);
-        else
-            thread_create("im_alsa-metadata", metadata_thread_stdin, mod, 1);
-    }
+	/* We're done, and we didn't fail! */
+	LOG_INFO1("Opened audio device %s", s->device);
+    LOG_INFO4("with %d channel(s), %d Hz, buffer %u ms (%u periods)",
+			s->channels, s->rate, s->buffer_time/1000, s->periods);

-    return mod;
+	return 0;

fail:
-    close_module(mod); /* safe, this checks for valid contents */
-    return NULL;
+	alsa_shutdown_module(mod); /* safe, this checks for valid contents */
+	return -1;
}



Modified: icecast/branches/kh/ices/src/im_alsa.h
===================================================================
--- icecast/trunk/ices/src/im_alsa.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_alsa.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_alsa.h
* - read pcm data from oss devices
*
- * $Id: im_alsa.h,v 1.5 2004/03/01 20:58:02 karl Exp $
+ * $Id: im_alsa.h,v 1.1 2002/12/29 10:28:30 msmith Exp $
*
* by Jason Chu  <jchu at uvic.ca>, based
* on im_oss.c which is...
@@ -16,24 +16,42 @@
#ifndef __IM_ALSA_H__
#define __IM_ALSA_H__

+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <ogg/ogg.h>
#include <alsa/asoundlib.h>
-#include <thread/thread.h>
-#include <ogg/ogg.h>
#include "inputmodule.h"

typedef struct
{
-    unsigned int rate;
-    int channels;
+	int rate;
+	int channels;
unsigned buffer_time;
unsigned periods;

-    snd_pcm_t *fd;
-    char **metadata;
-    int newtrack;
-    mutex_t metadatalock;
+	snd_pcm_t *fd;
+    const char *device;
+	int newtrack;
+    int user_terminated;
+    int samples;
+    void *read_buffer;
+    unsigned read_buffer_len;
} im_alsa_state;

-input_module_t *alsa_open_module(module_param_t *params);

-#endif  /* __IM_ALSA */
+int  alsa_init_module(input_module_t *mod);
+int  alsa_open_module (input_module_t *mod);
+void alsa_shutdown_module (input_module_t *mod);
+void alsa_close_module (input_module_t *mod);
+
+
+#endif  /* __IM_ALSA_H__ */

Added: icecast/branches/kh/ices/src/im_jack.c
===================================================================
--- icecast/trunk/ices/src/im_jack.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_jack.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,362 @@
+/* im_jack.c
+ * - input from JACK applications
+ *
+ * $Id: im_jack.c,v 0.7.0 2004/03/06 18:30:30 j Exp $
+ *
+ *
+ * (c) 2004 jan gerber <j at thing.net>,
+ * based on im_alsa.c which is...
+ * by Jason Chu <jchu at uvic.ca>, based
+ * on im_oss.c which is...
+ * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ogg/ogg.h>
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signals.h>
+
+#include "cfgparse.h"
+#include "metadata.h"
+
+#include "im_jack.h"
+
+#define MODULE "input-jack/"
+#include "logging.h"
+
+#define DEFAULT_RB_SIZE 32*1024
+
+int jack_callback_process (jack_nframes_t nframes, void *arg)
+{
+    int chn;
+    input_module_t *mod=arg;
+    im_jack_state *s = mod->internal;
+    unsigned to_write = sizeof (jack_default_audio_sample_t) * nframes;
+    int problem = 0;
+
+    /* Do nothing until we're ready to begin. Stop doing on exit. */
+    if ((!s->can_process) || s->user_terminated)
+        return 0;
+
+    /* copy data to ringbuffer; one per channel */
+    for (chn = 0; chn < (s->channels); chn++)
+    {
+        int len;
+        len = jack_ringbuffer_write (s->rb[chn],
+                jack_port_get_buffer (s->jack_ports[chn], nframes), to_write);
+        if (len < to_write)
+        {
+            problem = 1;
+            break;
+        }
+    }
+    if (problem)
+    {
+        s->jack_shutdown = 1;
+        LOG_ERROR0 ("ringbuffer full");
+    }
+    /* input_adv_sleep ((uint64_t)nframes * 1000000 / s->rate); */
+    return 0;
+}
+
+static int jack_initialise_buffer (input_module_t *mod, input_buffer *ib)
+{
+    im_jack_state *s = mod->internal;
+    int i;
+    float **ptr;
+
+    if ((ib->buf = calloc (s->channels, sizeof (float*))) == NULL)
+        return -1;
+    ptr = ib->buf;
+    for (i=s->channels ; i; i--)
+    {
+        if ((*ptr = calloc (sizeof(float), s->samples)) == NULL)
+            return -1;
+        ptr++;
+    }
+    ib->type = ICES_INPUT_PCM;
+    ib->subtype = INPUT_PCM_LE_16;
+    ib->critical = 0;
+    ib->channels = s->channels;
+    ib->samplerate = s->rate;
+    ib->mod = mod;
+    return 0;
+}
+
+
+void jack_close_module(input_module_t *mod)
+{
+    int i;
+    if (mod)
+    {
+        LOG_DEBUG0 ("closing JACK module");
+        /* input_sleep (); */
+        if (mod->internal)
+        {
+            im_jack_state *s = mod->internal;
+            if (s->client != NULL)
+                jack_client_close(s->client);
+            if(s->jack_ports)
+                free(s->jack_ports);
+            if(s->rb){
+                for(i=0;i<s->channels;i++){
+                    jack_ringbuffer_free(s->rb[i]);
+                }
+                free(s->rb);
+                s->rb = NULL;
+            }
+        }
+    }
+}
+
+
+void jack_shutdown_module (input_module_t *mod)
+{
+    im_jack_state *s = mod->internal;
+
+    LOG_INFO0 ("Shutdown JACK module");
+    free (s);
+    mod->internal = NULL;
+}
+
+void jack_shutdown (void *arg)
+{
+    LOG_ERROR0("killed by JACK server");
+    input_module_t *mod=arg;
+    im_jack_state *s = mod->internal;
+    s->jack_shutdown = 1;
+}
+
+
+/*
+   do the expensive stuff here so that
+   jack_callback_process is not stopped by jack
+ */
+static int jack_read(input_module_t *mod)
+{
+    im_jack_state *s = mod->internal;
+
+    size_t i;
+    int chn;
+    input_buffer *ib;
+    jack_default_audio_sample_t **pcms;
+    jack_nframes_t nframes=s->samples;
+    size_t framebuf_size = sizeof (jack_default_audio_sample_t) * nframes;
+
+    while (1)
+    {
+        /* read and process from ringbuffer has to go here. */
+        if (jack_ringbuffer_read_space (s->rb[0]) > framebuf_size)
+        {
+            if ((ib = input_alloc_buffer (mod)) == NULL)
+            {
+                LOG_ERROR0 ("Failed buffer allocation");
+                return 0;
+            }
+            pcms = ib->buf;
+
+            for (chn = 0; chn < (s->channels); chn++)
+            {
+                size_t len;
+                len = jack_ringbuffer_read (s->rb[chn], (char*)pcms[chn], framebuf_size);
+                if (len < framebuf_size)
+                    framebuf_size = len;
+            }
+
+            ib->samples = framebuf_size/sizeof(jack_default_audio_sample_t);
+            ib->samplerate = s->rate;
+            ib->channels = s->channels;
+
+            if (s->newtrack)
+            {
+                LOG_DEBUG0 ("metadata updates flagged");
+                metadata_thread_signal (mod, ib);
+                ib->critical = 1;
+                s->newtrack = 0;
+            }
+
+            if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+            {
+                LOG_DEBUG0("marking buffer eos");
+                ib->eos = 1;
+            }
+            input_adv_sleep ((uint64_t)ib->samples * 1000000 / s->rate);
+            send_for_processing (mod, ib);
+        }
+        else
+        {
+            thread_sleep (s->sleep);
+        }
+        if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+        {
+            s->newtrack = 1;
+            if (move_to_next_input || ices_config->shutdown)
+                s->user_terminated = 1;
+        }
+
+        if (s->jack_shutdown)
+        {
+            s->jack_shutdown = 0;
+            break;
+        }
+        if (s->user_terminated)
+        {
+            s->user_terminated = 0;
+            break;
+        }
+    }
+    return 0;
+}
+
+static void jack_return_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
+{
+    ib->critical = 0;
+    ib->eos = 0;
+    return;
+}
+
+int jack_init_module(input_module_t *mod)
+{
+    im_jack_state *s;
+    module_param_t *current;
+    char *clientname = "ices"; /* default clientname */
+    int channels, rate;
+    unsigned int samples;
+    unsigned sleep_time;
+
+    mod->name = "JACK";
+    mod->type = ICES_INPUT_PCM;
+    mod->subtype = INPUT_PCM_LE_16;
+    mod->getdata = jack_read;
+    mod->release_input_buffer = jack_return_buffer;
+    mod->initialise_buffer = jack_initialise_buffer;
+
+    mod->internal = calloc(1, sizeof(im_jack_state));
+    if (mod->internal == NULL)
+        return -1;
+    s = mod->internal;
+
+    s->client = NULL; /* Set it to something invalid, for now */
+
+    /* Defaults */
+    rate = 48000;
+    channels = 2;
+    samples = 4096;
+    sleep_time = 10000;
+
+    current = mod->module_params;
+
+    while(current)
+    {
+        if(!strcmp(current->name, "channels"))
+            channels = atoi(current->value);
+        else if(!strcmp(current->name, "clientname"))
+            clientname = current->value;
+        else if(!strcmp(current->name, "metadatafilename"))
+            mod->metadata_filename = current->value;
+        else if(!strcmp(current->name, "sleep"))
+            sleep_time = atoi (current->value);
+        else
+            LOG_WARN1("Unknown parameter %s for jack module", current->name);
+
+        current = current->next;
+    }
+    s->channels = channels;
+    s->clientname = clientname;
+
+    s->rate = rate;
+    s->samples = samples;
+    if (sleep_time > 0)
+        s->sleep = sleep_time;
+
+    /* allocate a few, so that mallocs don't kick in */
+    mod->prealloc_count = 20 + (ices_config->runner_count * 5);
+    mod->buffer_count = mod->prealloc_count;
+
+    s->can_process=0;
+
+    LOG_INFO0("JACK driver initialised");
+
+    return 0;
+
+}
+
+int jack_open_module(input_module_t *mod)
+{
+    im_jack_state *s = mod->internal;
+    int i,j;
+    char port_name[32];
+    size_t rb_size;
+
+    if ((s->client = jack_client_new(s->clientname)) == 0)
+    {
+        LOG_ERROR0("jack server not running");
+        goto fail;
+    }
+    LOG_INFO2("Registering as %s:in_1,%s:in_2\n", s->clientname,s->clientname);
+
+    /* do some memory management */
+    s->jack_ports =(jack_port_t **)malloc(sizeof (jack_port_t *) * (s->channels));
+
+    /* create the ringbuffers; one per channel, figures may need tweaking */
+    rb_size = (size_t)((s->sleep / 2000.0) *
+        jack_get_buffer_size(s->client) * sizeof(jack_default_audio_sample_t));
+    LOG_DEBUG2("creating %d ringbuffers, one per channel, of "
+            "%d bytes each", s->channels, rb_size);
+    s->rb =(jack_ringbuffer_t **)malloc(sizeof (jack_ringbuffer_t *) * (s->channels));
+    for(i=0;i<s->channels;i++){
+        s->rb[i]=jack_ringbuffer_create(DEFAULT_RB_SIZE);
+    }
+
+    /* start the callback process */
+    s->can_process=0; /* let the callback wait until all is running */
+    jack_set_process_callback(s->client, jack_callback_process, mod);
+
+    jack_on_shutdown (s->client, jack_shutdown, mod);
+
+    /* set samplerate and samples */
+    if(jack_get_sample_rate(s->client)>0)
+    {
+        s->rate = jack_get_sample_rate(s->client);
+    }
+    if (jack_activate(s->client))
+    {
+        LOG_ERROR0("cannot activate client");
+        goto fail;
+    }
+
+    /* Create and connect the jack ports */
+    for (i = 0; i < s->channels; i++)
+    {
+        sprintf(port_name, "in_%d", i+1);
+        s->jack_ports[i] = jack_port_register(s->client, port_name,
+                JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput,0);
+    }
+
+    s->newtrack = 1;
+
+    LOG_INFO2("Channels %d / Samplerate %d", s->channels, s->rate);
+    s->can_process=1;
+    return 0;
+
+fail:
+    jack_shutdown_module(mod); /* safe, this checks for valid contents */
+    return -1;
+}

Added: icecast/branches/kh/ices/src/im_jack.h
===================================================================
--- icecast/trunk/ices/src/im_jack.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_jack.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,62 @@
+/* im_jack.h
+ * - input from JACK applications
+ *
+ * $Id: im_jack.h,v 0.5 2004/01/22 22:53:30 j Exp $
+ *
+ *
+ * (c) 2004 jan gerber <j at thing.net>,
+ * based on im_alsa.c which is...
+ * by Jason Chu  <jchu at uvic.ca>, based
+ * on im_oss.c which is...
+ * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifndef __IM_JACK_H__
+#define __IM_JACK_H__
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <ogg/ogg.h>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include "inputmodule.h"
+
+typedef struct
+{
+	int rate;
+	int channels;
+	int samples;
+
+	int newtrack;
+    int user_terminated;
+    unsigned sleep;
+
+	int jack_shutdown;
+    const char *clientname;
+	jack_client_t *client;
+	jack_port_t **jack_ports;
+    jack_ringbuffer_t **rb;
+	volatile int can_process;
+
+} im_jack_state;
+
+int  jack_init_module(input_module_t *mod);
+int  jack_open_module (input_module_t *mod);
+void jack_shutdown_module (input_module_t *mod);
+void jack_close_module (input_module_t *mod);
+
+#endif  /* __IM_JACK_H__ */

Modified: icecast/branches/kh/ices/src/im_oss.c
===================================================================
--- icecast/trunk/ices/src/im_oss.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_oss.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_oss.c
* - Raw PCM input from OSS devices
*
- * $Id: im_oss.c,v 1.14 2004/01/17 04:24:10 karl Exp $
+ * $Id: im_oss.c,v 1.7 2002/08/09 13:52:56 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -9,10 +9,14 @@
* Public License, version 2. You may use, modify, and redistribute
* it under the terms of this license. A copy should be included
* with this source.
+
+
+ * Modified to work with non-blocking ices.
+
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif

#include <stdio.h>
@@ -26,248 +30,313 @@
#include <sys/types.h>
#include <fcntl.h>

-
-#include <thread/thread.h>
+#include "thread/thread.h"
#include "cfgparse.h"
#include "stream.h"
#include "metadata.h"
#include "inputmodule.h"

#include "im_oss.h"
+#include "signals.h"

#define MODULE "input-oss/"
#include "logging.h"

-#define BUFSIZE 8192
+#define SAMPLES    8192

-/* Some platforms (freebsd) don't define this, so just define it to something
- * that should be treated the same
- */
-#ifndef ERESTART
-#define ERESTART EINTR
-#endif
+static void oss_return_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
+{
+    ib->critical = 0;
+    ib->eos = 0;
+    return;
+}

-static void close_module(input_module_t *mod)
+
+static void oss_free_buffer (input_module_t *mod, input_buffer *ib)
{
-    if(mod)
+    im_oss_state *s = mod->internal;
+    float **ptr;
+    int i;
+
+    ptr = ib->buf;
+    for (i=s->channels; i; i--)
{
-        if(mod->internal)
+        if (ptr)
{
-            im_oss_state *s = mod->internal;
-            if(s->fd >= 0)
-                close(s->fd);
-            thread_mutex_destroy(&s->metadatalock);
-            free(s);
+            free (*ptr);
+            *ptr = NULL;
}
-        free(mod);
+        ptr++;
}
+    free (ib->buf);
+    ib->buf = NULL;
}
-static int event_handler(input_module_t *mod, enum event_type ev, void *param)
+
+
+static int oss_initialise_buffer (input_module_t *mod, input_buffer *ib)
{
im_oss_state *s = mod->internal;
+    float **ptr;
+    int i;

-    switch(ev)
+    if ((ib->buf = calloc (1, s->default_len)) == NULL)
+        return -1;
+
+    if ((ib->buf = calloc (s->channels, sizeof (float*))) == NULL)
+        return -1;
+    ptr = ib->buf;
+    for (i=s->channels ; i; i--)
{
-        case EVENT_SHUTDOWN:
-            close_module(mod);
-            break;
-        case EVENT_NEXTTRACK:
-            s->newtrack = 1;
-            break;
-        case EVENT_METADATAUPDATE:
-            thread_mutex_lock(&s->metadatalock);
-            if(s->metadata)
-            {
-                char **md = s->metadata;
-                while(*md)
-                    free(*md++);
-                free(s->metadata);
-            }
-            s->metadata = (char **)param;
-            s->newtrack = 1;
-            thread_mutex_unlock(&s->metadatalock);
-            break;
-        default:
-            LOG_WARN1("Unhandled event %d", ev);
+        if ((*ptr = calloc (sizeof(float), s->samples)) == NULL)
return -1;
+        ptr++;
}

+    ib->type = ICES_INPUT_PCM;
+    ib->subtype = INPUT_PCM_LE_16;
+    ib->critical = 0;
+    ib->channels = s->channels;
+    ib->samplerate = s->samplerate;
+    ib->mod = mod;
+
return 0;
}

-static void metadata_update(void *self, vorbis_comment *vc)
-{
-    im_oss_state *s = self;
-    char **md;

-    thread_mutex_lock(&s->metadatalock);
-
-    md = s->metadata;
-
-    if(md)
-    {
-        while(*md)
-            vorbis_comment_add(vc, *md++);
-    }
-
-    thread_mutex_unlock(&s->metadatalock);
-}
-
/* Core streaming function for this module
* This is what actually produces the data which gets streamed.
*
- * returns:  >0  Number of bytes read
- *            0  Non-fatal error.
- *           <0  Fatal error.
*/
-static int oss_read(void *self, ref_buffer *rb)
+static int oss_read(input_module_t *mod)
{
-    int result;
-    im_oss_state *s = self;
+	im_oss_state *s = mod->internal;
+    input_buffer *ib;
+    int len;
+    int dead_air;
+
+    while (1)
+    {
+        if (s->user_terminated)
+        {
+            s->user_terminated = 0;
+            return 0;
+        }

-    rb->buf = malloc(BUFSIZE*2*s->channels);
-    if(!rb->buf)
-        return -1;
-    result = read(s->fd, rb->buf, BUFSIZE*2*s->channels);
+        dead_air = 100;
+        while ((ib = input_alloc_buffer (mod)) == NULL)
+        {
+            if (dead_air == 100)
+                LOG_WARN0 ("will skip input for a short time");
+            read (s->fd, dead_audio, DEAD_AIR_BYTES);
+            if (--dead_air == 0)
+            {
+                mod->failures++;
+                return 0;
+            }
+        }

-    rb->len = result;
-    rb->aux_data = s->rate*s->channels*2;
+        len = read (s->fd, s->read_buffer, s->read_buffer_len);

-    if(s->newtrack)
-    {
-        rb->critical = 1;
-        s->newtrack = 0;
+        if (len == -1)
+        {
+            LOG_ERROR1("Error reading from audio device: %s", strerror(errno));
+            input_free_buffer (ib);
+            return 0;
+        }
+        if ((unsigned)len < s->read_buffer_len)
+            LOG_DEBUG1 ("Read return short length %d", len);
+
+        ib->samples = len/(ib->channels*2);
+        uninterleave_pcm_le ((signed char*)s->read_buffer, ib->channels, ib->samples, ib->buf);
+
+        if (s->newtrack)
+        {
+            LOG_DEBUG0 ("metadata updates flagged");
+            metadata_thread_signal (mod, ib);
+            ib->critical = 1;
+            s->newtrack = 0;
+        }
+
+        if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+        {
+            LOG_DEBUG0("marking buffer eos");
+            s->newtrack = 1;
+            ib->eos = 1;
+            if (move_to_next_input || ices_config->shutdown)
+                s->user_terminated = 1;
+        }
+        input_adv_sleep ((uint64_t)ib->samples * 1000000 / s->samplerate);
+        send_for_processing (mod, ib);
}
+
+    return 0;
+}

-    if(result == -1 && (errno == EINTR || errno == ERESTART))
+
+
+void oss_close_module (input_module_t *mod)
+{
+	im_oss_state *s = mod->internal;
+
+    LOG_INFO0("Closing OSS module");
+    if (s->fd > -1)
{
-        return 0; /* Non-fatal error */
+       close (s->fd);
+       s->fd = -1;
}
-    else if(result <= 0)
-    {
-        if(result == 0)
-            LOG_INFO0("Reached EOF, no more data available");
-        else
-            LOG_ERROR1("Error reading from audio device: %s", strerror(errno));
-        free(rb->buf);
-        return -1;
-    }
+}

-    return rb->len;
+void oss_shutdown_module (input_module_t *mod)
+{
+	im_oss_state *s = mod->internal;
+
+    LOG_INFO0 ("Shutdown OSS module");
+    free (s);
+    mod->internal = NULL;
}

-input_module_t *oss_open_module(module_param_t *params)
+
+
+int oss_init_module(input_module_t *mod)
{
-    input_module_t *mod = calloc(1, sizeof(input_module_t));
im_oss_state *s;
module_param_t *current;
-    char *device = "/dev/dsp"; /* default device */
-    int format = AFMT_S16_LE;
-    int channels, rate;
-    int use_metadata = 1; /* Default to on */

+    mod->name = "OSS";
mod->type = ICES_INPUT_PCM;
-    mod->subtype = INPUT_PCM_LE_16;
mod->getdata = oss_read;
-    mod->handle_event = event_handler;
-    mod->metadata_update = metadata_update;
+    mod->release_input_buffer = oss_return_buffer;
+    mod->initialise_buffer = oss_initialise_buffer;
+    mod->free_input_buffer = oss_free_buffer;
+    mod->buffer_count = ices_config->runner_count*5 + 25;
+    mod->prealloc_count = 2 + ices_config->runner_count*2;

mod->internal = calloc(1, sizeof(im_oss_state));
-    s = mod->internal;
+    if (mod->internal == NULL)
+        return -1;

-    s->fd = -1; /* Set it to something invalid, for now */
-    s->rate = 44100; /* Defaults */
-    s->channels = 2;
+	s = mod->internal;

-    thread_mutex_create(&s->metadatalock);
+	s->fd = -1; /* Set it to something invalid, for now */
+	s->samplerate = 44100; /* Defaults */
+	s->channels = 2;

-    current = params;
+	current = mod->module_params;

-    while(current)
-    {
-        if(!strcmp(current->name, "rate"))
-            s->rate = atoi(current->value);
-        else if(!strcmp(current->name, "channels"))
-            s->channels = atoi(current->value);
-        else if(!strcmp(current->name, "device"))
-            device = current->value;
-        else if(!strcmp(current->name, "metadata"))
-            use_metadata = atoi(current->value);
-        else if(!strcmp(current->name, "metadatafilename"))
-            ices_config->metadata_filename = current->value;
+	while(current)
+	{
+		if(!strcmp(current->name, "rate"))
+			s->samplerate = atoi(current->value);
+		else if(!strcmp(current->name, "channels"))
+			s->channels = atoi(current->value);
+		else if(!strcmp(current->name, "device"))
+			s->devicename = current->value;
+		else if(!strcmp(current->name, "samples"))
+			s->samples = atoi(current->value);
+		else if(!strcmp(current->name, "metadatafilename"))
+			mod->metadata_filename = current->value;
+		else if (!strcmp(current->name, "comment"))
+                ;
else
-            LOG_WARN1("Unknown parameter %s for oss module", current->name);
+			LOG_WARN1("Unknown parameter %s for oss module", current->name);

-        current = current->next;
-    }
+		current = current->next;
+	}
+    if (s->samples == 0)
+        s->samples = s->samplerate/5;
+
+    s->aux_data = s->samplerate * s->channels * 2;
+    s->default_len = s->samples * s->channels * 2;
+    s->read_buffer_len = s->samples*2*s->channels;
+    s->read_buffer = malloc (s->read_buffer_len);

-    /* First up, lets open the audio device */
-    if((s->fd = open(device, O_RDONLY, 0)) == -1)
-    {
-        /* Open failed */
-        LOG_ERROR2("Failed to open audio device %s: %s",
-                device, strerror(errno));
-        goto fail;
-    }
+    LOG_INFO0 ("Module OSS initialised");
+    return 0;
+}

-    /* Now, set the required parameters on that device */

-    if(ioctl(s->fd, SNDCTL_DSP_SETFMT, &format) == -1)
-    {
-        LOG_ERROR2("Failed to set sample format on audio device %s: %s",
-                device, strerror(errno));
-        goto fail;
-    }
-    if(format != AFMT_S16_LE)
-    {
-        LOG_ERROR0("Couldn't set sample format to AFMT_S16_LE");
-        goto fail;
-    }
+int oss_open_module(input_module_t *mod)
+{
+    im_oss_state *s = mod->internal;
+	int format = AFMT_S16_LE;
+	int channels, rate;
+    int flags;

+	/* First up, lets open the audio device */
channels = s->channels;
-    if(ioctl(s->fd, SNDCTL_DSP_CHANNELS, &channels) == -1)
-    {
-        LOG_ERROR2("Failed to set number of channels on audio device %s: %s",
-                device, strerror(errno));
-        goto fail;
-    }
-    if(channels != s->channels)
-    {
-        LOG_ERROR0("Couldn't set number of channels");
-        goto fail;
-    }
+    rate = s->samplerate;

-    rate = s->rate;
-    if(ioctl(s->fd, SNDCTL_DSP_SPEED, &rate) == -1)
+    if (ices_config->shutdown)
+        return -1;
+
+	if((s->fd = open(s->devicename, O_RDONLY, 0)) == -1)
+	{
+		LOG_ERROR2("Failed to open audio device %s: %s", s->devicename, strerror(errno));
+		goto fail;
+	}
+    flags = fcntl (s->fd, F_GETFL);
+    if (flags == -1)
{
-        LOG_ERROR2("Failed to set sampling rate on audio device %s: %s",
-                device, strerror(errno));
+        LOG_ERROR0 ("Cannot get file state");
goto fail;
}
-    if(rate != s->rate)
+    flags &= ~O_NONBLOCK;
+    if (fcntl(s->fd, F_SETFL, flags) == -1)
{
-        LOG_ERROR0("Couldn't set sampling rate");
+        LOG_ERROR0 ("Cannot set dsp file state");
goto fail;
}
+	/* Now, set the required parameters on that device */

-    /* We're done, and we didn't fail! */
-    LOG_INFO3("Opened audio device %s at %d channel(s), %d Hz",
-            device, channels, rate);
+	if(ioctl(s->fd, SNDCTL_DSP_SETFMT, &format) == -1)
+	{
+		LOG_ERROR2("Failed to set sample format on audio device %s: %s",
+				s->devicename, strerror(errno));
+		goto fail;
+	}
+	if(format != AFMT_S16_LE)
+	{
+		LOG_ERROR0("Couldn't set sample format to AFMT_S16_LE");
+		goto fail;
+	}

-    if(use_metadata)
-    {
-        LOG_INFO0("Starting metadata update thread");
-        if(ices_config->metadata_filename)
-            thread_create("im_oss-metadata", metadata_thread_signal, mod, 1);
-        else
-            thread_create("im_oss-metadata", metadata_thread_stdin, mod, 1);
-    }
+	channels = s->channels;
+	if(ioctl(s->fd, SNDCTL_DSP_CHANNELS, &channels) == -1)
+	{
+		LOG_ERROR2("Failed to set number of channels on audio device %s: %s",
+				s->devicename, strerror(errno));
+		goto fail;
+	}
+	if(channels != s->channels)
+	{
+		LOG_ERROR0("Couldn't set number of channels");
+		goto fail;
+	}

-    return mod;
+	rate = s->samplerate;
+	if(ioctl(s->fd, SNDCTL_DSP_SPEED, &rate) == -1)
+	{
+		LOG_ERROR2("Failed to set sampling rate on audio device %s: %s",
+				s->devicename, strerror(errno));
+		goto fail;
+	}
+	if(rate != s->samplerate)
+	{
+		LOG_ERROR0("Couldn't set sampling rate");
+		goto fail;
+	}

+	/* We're done, and we didn't fail! */
+	LOG_INFO3("Opened audio device %s at %d channel(s), %d Hz",
+			s->devicename, channels, rate);
+
+    s->newtrack = 1;
+	return 0;
fail:
-    close_module(mod); /* safe, this checks for valid contents */
-    return NULL;
+	oss_close_module(mod); /* safe, this checks for valid contents */
+	return -1;
}


+
+

Modified: icecast/branches/kh/ices/src/im_oss.h
===================================================================
--- icecast/trunk/ices/src/im_oss.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_oss.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_oss.h
* - read pcm data from oss devices
*
- * $Id: im_oss.h,v 1.4 2003/03/28 01:07:37 karl Exp $
+ * $Id: im_oss.h,v 1.2 2001/09/25 12:04:21 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -14,21 +14,30 @@
#ifndef __IM_OSS_H__
#define __IM_OSS_H__

-#include <thread/thread.h>
+#include "inputmodule.h"
#include <ogg/ogg.h>
-#include "inputmodule.h"

typedef struct
{
-    int rate;
-    int channels;
+	int channels;
+	int samplerate;
+	int aux_data;
+    int samples;
+    int default_len;
+    int user_terminated;

-    int fd;
-    char **metadata;
-    int newtrack;
-    mutex_t metadatalock;
+	int fd;
+    char *devicename;
+	int newtrack;
+    void *read_buffer;
+    unsigned read_buffer_len;
} im_oss_state;

-input_module_t *oss_open_module(module_param_t *params);
+int oss_open_module(input_module_t *);
+void oss_close_module (input_module_t *);

+int oss_init_module(input_module_t *mod);
+void oss_shutdown_module(input_module_t *mod);
+
+
#endif  /* __IM_OSS_H__ */

Added: icecast/branches/kh/ices/src/im_pcm.c
===================================================================
--- icecast/trunk/ices/src/im_pcm.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_pcm.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,416 @@
+/* im_pcm.c
+ * - Raw PCM input from a pipe, based on the stdinpcm module.
+ *
+ * Copyright (c) 2002 Karl Heyes <karl at xiph.org>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ogg/ogg.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include "thread/thread.h"
+
+#include "cfgparse.h"
+#include "stream.h"
+#include "signals.h"
+
+#include "inputmodule.h"
+
+
+#include "metadata.h"
+#include "im_pcm.h"
+#include "timing/timing.h"
+
+#define MODULE "input-pcm/"
+#include "logging.h"
+
+#define SAMPLES 4096
+
+/* #define MAX_DEAD_AUDIO_BYTES 48000*2*2
+static char dead_audio[MAX_DEAD_AUDIO_BYTES]; */
+static uint64_t skip_bytes;
+
+static int run_script (char *script, im_pcm_state *s)
+{
+    int fd [2];
+
+    if (pipe (fd) < 0)
+    {
+        LOG_ERROR0 ("Pipe creation error");
+        return -1;
+    }
+    switch (s->pid = fork ())
+    {
+    case -1:
+        LOG_ERROR2 ("Unable to fork %s (%s)", script, strerror (errno));
+        return -1;
+    case 0:  /* child */
+        LOG_DEBUG1 ("Starting command %s", script);
+        close (1);
+        dup2 (fd[1], 1);
+        close (fd[0]);
+        close (fd[1]);
+        close (0);     /* We want to detach the keyboard from the script */
+        /* execl (script, script, NULL); */
+        execve (s->args[0], s->args, NULL);
+        LOG_ERROR2 ("Unable to run command %s (%s)", script, strerror (errno));
+        close (1);
+        _exit (-1);
+    default: /* parent */
+        s->fd = fd[0];
+        close (fd[1]);
+        break;
+    }
+    return 0;
+}
+
+static void kill_script (im_pcm_state *s)
+{
+    LOG_DEBUG1 ("Sending SIGTERM to pid %d", s->pid);
+    kill (s->pid, SIGTERM);
+    close (s->fd);
+    waitpid (s->pid, NULL, 0);
+    s->fd = -1;
+}
+
+
+static int wait_for_pcm (im_pcm_state *s)
+{
+    fd_set fds;
+    struct timeval tv;
+    int ret;
+
+    FD_ZERO (&fds);
+    FD_SET (s->fd, &fds);
+    tv.tv_sec = s->timeout;
+    tv.tv_usec = 0;
+    ret = select (s->fd+1, &fds, NULL, NULL, &tv);
+    if (ret == 0)
+    {
+        LOG_WARN0 ("Timeout reading from input");
+        s->no_stream = 1;
+        return -1;
+    }
+    if (ret == -1)
+    {
+        LOG_ERROR1("select failed error %s", strerror (errno));
+        s->no_stream = 1;
+        return -1;
+    }
+    /* LOG_DEBUG1 ("data availble %d", sleep_to); */
+    return 0;
+}
+
+
+static int pcm_read(input_module_t *mod)
+{
+	im_pcm_state *s = mod->internal;
+    input_buffer *ib = NULL;
+    unsigned char *buf;
+    unsigned remaining, len;
+    int dead_air = 100;
+
+    while (1)
+    {
+        if (s->terminate)
+        {
+            s->terminate = 0;
+            return 0;
+        }
+        if (s->error)
+        {
+            LOG_INFO0("Error reading from stream, switching to next input");
+            mod->failures++;
+            return 0;
+        }
+        if (s->no_stream)
+        {
+            LOG_INFO0("End of input has been reached, switching to next input");
+            return 0;
+        }
+        input_sleep();
+
+        while ((ib = input_alloc_buffer (mod)) == NULL)
+        {
+            if (dead_air == 100)
+                LOG_WARN0 ("will skip input for a short time");
+            read (s->fd, dead_audio, DEAD_AIR_BYTES);
+            if (--dead_air == 0)
+                return 0;
+        }
+
+        buf = s->read_buffer;
+        remaining = s->read_buffer_len;
+        len = 0;
+        if(s->newtrack)
+        {
+            LOG_DEBUG0 ("metadata updates flagged");
+            metadata_thread_signal (mod, ib);
+            ib->critical = 1;
+            s->newtrack = 0;
+        }
+        if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+        {
+            LOG_DEBUG0("signal or shutdown received");
+            s->newtrack = 1;
+            ib->eos = 1;
+            if (ices_config->shutdown || move_to_next_input)
+                s->terminate = 1;
+        }
+
+        while (remaining)
+        {
+            int sent;
+
+            sent = read (s->fd, buf, remaining); /* non blocking */
+            if (sent < 0)
+            {
+                if (errno == EAGAIN && wait_for_pcm (s) == 0)
+                    continue;
+                s->error = 1;
+                break;
+            }
+            if (sent == 0)
+            {
+                s->no_stream = 1;
+                break;
+            }
+            remaining -= sent;
+            buf += sent;
+        }
+        len = s->read_buffer_len - remaining;
+        if (remaining)
+        {
+            LOG_INFO0("No more data available");
+            input_free_buffer (ib);
+            break;
+        }
+
+        ib->samples = len/(ib->channels*2);
+
+        uninterleave_pcm_le ((signed char*)s->read_buffer, ib->channels, ib->samples, ib->buf);
+
+        input_adv_sleep ((uint64_t)ib->samples * 1000000 / ib->samplerate);
+        send_for_processing (mod, ib);
+    }
+
+    return 0;
+}
+
+
+void pcm_close_module(input_module_t *mod)
+{
+    LOG_INFO0 ("Closing PCM input module");
+    if(mod && mod->internal)
+    {
+        im_pcm_state *s = mod->internal;
+        if (s->command && s->fd != -1)
+        {
+            kill_script (s);
+        }
+    }
+}
+
+
+void pcm_shutdown_module(input_module_t *mod)
+{
+    LOG_INFO0 ("shutdown PCM input module");
+    if (mod && mod->internal)
+    {
+        input_buffer *ib;
+
+        while (1)
+        {
+            ib = mod->free_list;
+            if (ib == NULL)
+                break;
+            mod->free_list = ib->next;
+            free (ib->buf);
+            free (ib);
+        }
+        free (mod->internal);
+        mod->internal = NULL;
+    }
+}
+
+
+static int pcm_initialise_buffer (input_module_t *mod, input_buffer *ib)
+{
+    im_pcm_state *s = mod->internal;
+    float **ptr;
+    int i;
+
+    /* allocate memory for vorbis data */
+    if ((ib->buf = calloc (s->channels, sizeof (float*))) == NULL)
+        return -1;
+    ptr = ib->buf;
+    for (i=s->channels ; i; i--)
+    {
+        if ((*ptr = calloc (sizeof(float), s->samples)) == NULL)
+            return -1;
+        ptr++;
+    }
+
+    ib->type = ICES_INPUT_PCM;
+    ib->subtype = INPUT_PCM_LE_16;
+    ib->critical = 0;
+    ib->channels = s->channels;
+    ib->samplerate = s->samplerate;
+    ib->mod = mod;
+
+    return 0;
+}
+
+
+int pcm_initialise_module(input_module_t *mod)
+{
+    im_pcm_state *s;
+    module_param_t *current;
+
+    LOG_INFO0 ("Initialising PCM input module");
+
+    mod->name = "pcm";
+    mod->type = ICES_INPUT_PCM;
+    mod->getdata = pcm_read;
+    mod->initialise_buffer = pcm_initialise_buffer;
+    mod->buffer_count = ices_config->runner_count*15 + 5;
+    mod->prealloc_count = ices_config->runner_count * 4;
+
+    mod->internal = calloc(1, sizeof(im_pcm_state));
+    if (mod->internal == NULL)
+        return -1;
+
+    s = mod->internal;
+
+    /* Defaults */
+    s->channels = 2;
+    s->samplerate = 44100;
+    s->samples = SAMPLES;
+    s->timeout = 5;
+    s->arg_count = 1;
+
+    current = mod->module_params;
+
+    while(current)
+    {
+        if(!strcmp(current->name, "rate"))
+            s->samplerate = atoi(current->value);
+        else if(!strcmp(current->name, "channels"))
+            s->channels = atoi(current->value);
+        else if(!strcmp(current->name, "metadatafilename"))
+            mod->metadata_filename = current->value;
+        else if (!strcmp(current->name, "source") || !strcmp(current->name, "command"))
+        {
+            s->command = current->value;
+            s->args[0] = s->command;
+        }
+        else if (!strcmp(current->name, "arg"))
+            if (s->arg_count < MAX_ARGS)
+                s->args[s->arg_count++] = current->value;
+            else
+            {
+                LOG_WARN0("too many args, disabling module");
+                return -1;
+            }
+        else if (!strcmp(current->name, "comment"))
+            s->command = current->value;
+        else if (!strcmp(current->name, "timeout"))
+            s->timeout = atoi (current->value);
+        else if (!strcmp(current->name, "samples"))
+            s->samples = atoi (current->value);
+        /* else if (!strcmp(current->name, "lossy"))
+            s->sleep = _sleep_add_silence; */
+        else if (!strcmp(current->name, "skip"))
+            s->skip = atoi (current->value);
+        else
+            LOG_WARN1("Unknown parameter %s for pcm module", current->name);
+
+        current = current->next;
+    }
+    s->args[s->arg_count] = NULL;
+    s->aux_data = s->samplerate * s->channels * 2;
+    s->default_duration = (s->default_len*1000)/s->aux_data;
+    s->read_buffer_len = s->samples*2*s->channels;
+    s->read_buffer = malloc (s->read_buffer_len);
+    LOG_DEBUG1 ("Module PCM using buffers of %d samples", s->samples);
+
+    return 0;
+}
+
+
+
+int pcm_open_module(input_module_t *mod)
+{
+    im_pcm_state *s = mod->internal;
+    const char *label = "standard input";
+
+    s->fd = 0;
+    if (s->command)
+    {
+        if (run_script (s->command, s) < 0)
+        {
+            s->fd = -1;
+            LOG_ERROR1 ("Failed to start script %s", s->command);
+            return -1;
+        }
+        label = s->command;
+    }
+    s->newtrack = 1;
+    s->silence_start = 0;
+    s->error = 0;
+    s->no_stream = 0;
+    fcntl (s->fd, F_SETFL, O_NONBLOCK);
+    skip_bytes = s->skip;
+    LOG_INFO0("Retrieving PCM to skip");
+    while (skip_bytes)
+    {
+        unsigned len;
+        int ret;
+
+        if (wait_for_pcm (s) < 0)
+        {
+            s->terminate = 1;
+            break;
+        }
+        if (skip_bytes > DEAD_AIR_BYTES)
+            len = DEAD_AIR_BYTES;
+        else
+            len = skip_bytes;
+        ret = read (s->fd, dead_audio, len);
+        if (ret ==0)
+            break;
+        if (ret > 0)
+            skip_bytes -= ret;
+        else
+        {
+            s->terminate = 1;
+            break;
+        }
+    }
+    LOG_INFO1("Retrieving PCM from %s", label);
+
+    return 0;
+}
+
+

Added: icecast/branches/kh/ices/src/im_pcm.h
===================================================================
--- icecast/trunk/ices/src/im_pcm.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_pcm.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,58 @@
+/* im_stdinpcm.h
+ * - stdin reading
+ *
+ * $Id: im_stdinpcm.h,v 1.2 2001/09/25 12:04:21 msmith Exp $
+ *
+ * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifndef __IM_PCM_H__
+#define __IM_PCM_H__
+
+#include "inputmodule.h"
+#include <ogg/ogg.h>
+
+
+#define MAX_ARGS 12
+
+typedef struct _im_pcm_state im_pcm_state;
+
+struct _im_pcm_state
+{
+    int (*sleep) (im_pcm_state *s, void  *buf, unsigned remaining, timing_control *control);
+	int samplerate;
+	int channels;
+	int newtrack;
+    int timeout;
+    char *command;
+    FILE *file;
+    pid_t pid;
+    int fd;
+    int max_buffers;
+    unsigned samples;
+    unsigned default_len;
+    float default_duration;
+    size_t aux_data;
+    time_t silence_start;
+    int error;
+    int terminate;
+    int no_stream;
+    uint64_t skip;
+    int arg_count;
+    char *args[MAX_ARGS+1];   /* allow for NULL */
+    void *read_buffer;
+    unsigned read_buffer_len;
+};
+
+int pcm_initialise_module(input_module_t *);
+int pcm_open_module(input_module_t *);
+void pcm_close_module(input_module_t *mod);
+void pcm_shutdown_module(input_module_t *mod);
+
+
+#endif  /* __IM_PCM_H__ */

Modified: icecast/branches/kh/ices/src/im_playlist.c
===================================================================
--- icecast/trunk/ices/src/im_playlist.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_playlist.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,10 @@
/* playlist.c
* - Basic playlist functionality
*
- * $Id: im_playlist.c,v 1.15 2004/01/12 23:39:39 karl Exp $
+ * $Id: im_playlist.c,v 1.6 2002/08/03 15:05:38 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2003 Karl Heyes <karl at xiph.org>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -12,7 +13,7 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif

#include <stdio.h>
@@ -25,266 +26,441 @@

#include "cfgparse.h"
#include "stream.h"
-#include "event.h"

#include "inputmodule.h"
-#include "input.h"
#include "im_playlist.h"
+#include "timing/timing.h"

#include "playlist_basic.h"
+#include "metadata.h"
+#include "signals.h"

#define MODULE "playlist-builtin/"
#include "logging.h"

-#define BUFSIZE 4096
+#define OGG_BUFSIZE 4096
+#define ALLOCATION_DELAY 500*1000  /* uS */

-typedef struct _module
+typedef struct _playlist_module
{
char *name;
-    int (*init)(module_param_t *, playlist_state_t *);
-} module;
+    int (*init)(module_param_t *, struct playlist_state *);
+} playlist_module_t;

-static module modules[] = {
+static playlist_module_t pmodules[] = {
{ "basic", playlist_basic_initialise},
{ "script", playlist_script_initialise},
{NULL,NULL}
};

-static void close_module(input_module_t *mod)
+static void playlist_clear_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
{
-    if (mod == NULL) return;
-
-    if (mod->internal)
+    if (ib)
{
-        playlist_state_t *pl = (playlist_state_t *)mod->internal;
-        pl->clear(pl->data);
-        ogg_sync_clear(&pl->oy);
-        free(pl);
+        ib->critical = 0;
+        if (ib->buf)
+        {
+            ogg_packet *op = ib->buf;
+            free (op->packet);
+            free (op);
+            ib->buf = NULL;
+        }
}
-    free(mod);
}

-static int event_handler(input_module_t *mod, enum event_type ev, void *param)
+int playlist_open_module(input_module_t *mod)
{
-    switch(ev)
+    module_param_t *current;
+
+    LOG_INFO0("open playlist module");
+
+    current = mod->module_params;
+    while (current)
{
-        case EVENT_SHUTDOWN:
-            close_module(mod);
+        if (!strcmp(current->name, "type"))
+        {
+            int current_module = 0;
+
+            while(pmodules[current_module].init)
+            {
+                if(!strcmp(current->value, pmodules[current_module].name))
+                {
+                    struct playlist_state *pl = (struct playlist_state *)mod->internal;
+
+                    if (pmodules[current_module].init (mod->module_params, pl))
+                    {
+                        LOG_ERROR0("Playlist initialisation failed");
+                        return -1;
+                    }
+                    pl->filename = NULL;
+                    pl->next_track = 1;
+                    pl->terminate = 0;
+                    pl->prev_op = NULL;
+                    LOG_DEBUG0 ("initialised module");
+                    return 0;
+                }
+                current_module++;
+            }
break;
-        case EVENT_NEXTTRACK:
-            LOG_INFO0("Moving to next file in playlist.");
-            ((playlist_state_t *)mod->internal)->nexttrack = 1;
-            break;
-        default:
-            LOG_WARN1("Unhandled event %d", ev);
-            return -1;
+        }
+        current = current->next;
}
+    return -1;
+}

-    return 0;
+
+static void close_file (struct playlist_state *pl)
+{
+    if (pl->current_file)
+    {
+        fclose (pl->current_file);
+        pl->current_file = NULL;
+        /* Reinit sync, so that dead data from previous file is discarded */
+        ogg_stream_clear(&pl->os);
+        ogg_sync_clear(&pl->oy);
+    }
}

-/* Core streaming function for this module
- * This is what actually produces the data which gets streamed.
- *
- * returns:  >0  Number of bytes read
- *            0  Non-fatal error.
- *           <0  Fatal error.
- */
-static int playlist_read(void *self, ref_buffer *rb)
+
+void playlist_close_module(input_module_t *mod)
{
-    playlist_state_t *pl = (playlist_state_t *)self;
-    int bytes;
-    unsigned char *buf;
-    char *newfn;
-    int result;
-    ogg_page og;
+    LOG_INFO0 ("Close playlist module");
+    if (mod->internal)
+    {
+        struct playlist_state *pl = (struct playlist_state *)mod->internal;
+        pl->free_filename (pl->data, pl->filename);
+        pl->clear (pl->data);
+        pl->data = NULL;
+        close_file (pl);
+    }
+}

-    if (pl->errors > 5)
+void playlist_shutdown_module(input_module_t *mod)
+{
+    input_buffer *ib;
+
+    LOG_INFO0 ("Shutdown playlist module");
+
+    if (mod->internal)
{
-        LOG_WARN0("Too many consecutive errors - exiting");
-        return -1;
+        struct playlist_state *pl = (struct playlist_state *)mod->internal;
+        if (pl->data) pl->clear(pl->data);
+        ogg_sync_clear(&pl->oy);
+        free(pl);
}
+    while (1)
+    {
+        ib = mod->free_list;
+        if (ib == NULL)
+            break;
+        mod->free_list = ib->next;
+        free (ib);
+    }
+}

-    if (!pl->current_file || pl->nexttrack)
+
+static int find_next_entry (struct playlist_state *pl)
+{
+    char *newfn;
+
+    pl->next_track = 0;
+
+    if (ices_config->shutdown || pl->terminate)
+        return 0;
+
+    while (1)
{
-        pl->nexttrack = 0;
-
-        if (pl->current_file && strcmp (pl->filename, "-"))
+        if (pl->errors > 5)
{
-            fclose(pl->current_file);
-            pl->current_file = NULL;
+            LOG_WARN0("Too many consecutive errors - exiting");
+            return 0;
}

newfn = pl->get_filename(pl->data);
-        if (!newfn)
+        if (newfn == NULL)
{
-            LOG_INFO0("No more filenames available, end of playlist");
-            return -1; /* No more files available */
+            LOG_DEBUG0 ("no filename returned");
+            return 0;  /* No more files available */
}

if (strcmp (newfn, "-"))
{
-            if (!pl->allow_repeat && pl->filename && !strcmp(pl->filename, newfn))
+            pl->current_file = fopen(newfn, "rb");
+            if (pl->current_file == NULL)
{
-                LOG_ERROR0("Cannot play same file twice in a row, skipping");
-                pl->errors++;
+                LOG_WARN2("Error opening file \"%s\": %s", newfn, strerror(errno));
pl->free_filename (pl->data, newfn);
-                return 0;
-            }
-            pl->free_filename(pl->data, pl->filename);
-            pl->filename = newfn;
-
-            pl->current_file = fopen(pl->filename, "rb");
-            if (!pl->current_file)
-            {
-                LOG_WARN2("Error opening file \"%s\": %s",pl->filename, strerror(errno));
pl->errors++;
-                return 0;
+                continue;
}
-            LOG_INFO1("Currently playing \"%s\"", pl->filename);
}
else
{
-            LOG_INFO0("Currently playing from stdin");
pl->current_file = stdin;
-            pl->free_filename(pl->data, pl->filename);
-            pl->filename = newfn;
}
+        pl->errors = 0;

-        /* Reinit sync, so that dead data from previous file is discarded */
-        ogg_sync_clear(&pl->oy);
-        ogg_sync_init(&pl->oy);
+        break;
}
-    input_sleep ();
+    pl->free_filename(pl->data, pl->filename);
+    pl->filename = newfn;

-    while(1)
+    LOG_INFO1("Currently playing \"%s\"", newfn);
+
+    ogg_sync_init(&pl->oy);
+    return 1;
+}
+
+
+static ogg_packet *copy_ogg_packet (ogg_packet *packet)
+{
+    ogg_packet *next;
+    do
{
-        result = ogg_sync_pageout(&pl->oy, &og);
-        if(result < 0)
-            LOG_WARN1("Corrupt or missing data in file (%s)", pl->filename);
-        else if(result > 0)
-        {
-            if (ogg_page_bos (&og))
-            {
-               if (ogg_page_serialno (&og) == pl->current_serial)
-               {
-                   LOG_WARN1 ("Skipping \"%s\" as the serial number is the same as previous", pl->filename);
-                   pl->nexttrack = 1;
-                   pl->errors++;
-                   return 0;
-               }
-               pl->current_serial = ogg_page_serialno (&og);
-            }
-            if (input_calculate_ogg_sleep (&og) < 0)
-            {
-                pl->nexttrack = 1;
-                return 0;
-            }
-            rb->len = og.header_len + og.body_len;
-            rb->buf = malloc(rb->len);
-            rb->aux_data = og.header_len;
-
-            memcpy(rb->buf, og.header, og.header_len);
-            memcpy(rb->buf+og.header_len, og.body, og.body_len);
-            if(ogg_page_granulepos(&og)==0)
-                rb->critical = 1;
+        next = malloc (sizeof (ogg_packet));
+        if (next == NULL)
break;
-        }
+        memcpy (next, packet, sizeof (ogg_packet));
+        next->packet = malloc (next->bytes);
+        if (next->packet == NULL)
+            break;
+        memcpy (next->packet, packet->packet, next->bytes);
+        return next;
+    } while (0);

-        /* If we got to here, we didn't have enough data. */
-        buf = ogg_sync_buffer(&pl->oy, BUFSIZE);
-        bytes = fread(buf,1, BUFSIZE, pl->current_file);
-        if (bytes <= 0)
+    if (next)
+        free (next);
+    return NULL;
+}
+
+
+static void *alloc_ogg_buffer (input_module_t *mod, unsigned len)
+{
+    struct playlist_state *pl = (struct playlist_state *)mod->internal;
+    return ogg_sync_buffer (&pl->oy, len);
+}
+
+
+static void prepare_buffer (input_buffer *ib, struct playlist_state *pl, int force_eos)
+{
+    ogg_packet *op = pl->prev_packet;
+
+    ib->buf = op;
+    if (op->b_o_s)
+    {
+        ib->critical = 1;
+        vorbis_info_init (&pl->vi);
+        vorbis_comment_init (&pl->vc);
+        pl->more_headers = 3;
+    }
+    if (pl->more_headers)
+    {
+        if (vorbis_synthesis_headerin (&pl->vi, &pl->vc, op) < 0)
{
-            if (feof(pl->current_file))
-            {
-                pl->nexttrack = 1;
-                return playlist_read(pl,rb);
-            }
-            else
-            {
-                LOG_ERROR2("Read error from \"%s\": %s", pl->filename, strerror(errno));
-                fclose(pl->current_file);
-                pl->current_file=NULL;
-                pl->errors++;
-                return 0;
-            }
+            LOG_ERROR1("Problem with vorbis header %d", 4-pl->more_headers);
+            metadata_update_signalled = 1;
+            pl->prev_packet = NULL;
}
+        pl->more_headers--;
+        pl->prev_window = 0;
+        ib->samples = 0;
+    }
+    else
+    {
+        int window = vorbis_packet_blocksize (&pl->vi, op) / 4;
+        if (pl->prev_window)
+        {
+            pl->granulepos += pl->prev_window + window;
+            ib->samples = pl->prev_window + window;
+        }
else
-            ogg_sync_wrote(&pl->oy, bytes);
+        {
+            pl->granulepos = 0;
+            ib->samples = 0;
+        }
+        pl->prev_window = window;
+        op->granulepos = pl->granulepos;
+        ib->samplerate = pl->vi.rate;
+        ib->channels = pl->vi.channels;
}
+    ib->type = ICES_INPUT_VORBIS_PACKET;
+    if (force_eos || op->e_o_s)
+    {
+        ib->eos = 1;
+        op->e_o_s = 1;
+        vorbis_comment_clear (&pl->vc);
+        vorbis_info_clear (&pl->vi);
+    }
+    /* printf ("packet has %ld granulepos\n", op->granulepos); */
+    input_adv_sleep ((unsigned long)(ib->samples * 1000000.0 / ib->samplerate));
+}

-    pl->errors=0;

-    return rb->len;
-}
-
-input_module_t *playlist_open_module(module_param_t *params)
+static void flush_file (input_module_t *mod)
{
-    input_module_t *mod = calloc(1, sizeof(input_module_t));
-    playlist_state_t *pl;
-    module_param_t *current;
-    int (*init)(module_param_t *, playlist_state_t *)=NULL;
+    struct playlist_state *pl = (struct playlist_state *)mod->internal;

-    mod->type = ICES_INPUT_VORBIS;
-    mod->getdata = playlist_read;
-    mod->handle_event = event_handler;
-    mod->metadata_update = NULL; /* Not used for playlists */
+    /* flush final EOS packet */
+    if (pl->prev_packet)
+    {
+        input_buffer *ib = input_alloc_buffer (mod);
+        if (ib)
+        {
+            LOG_DEBUG0("Flushing EOS packet");
+            prepare_buffer (ib, pl, 1);
+            send_for_processing (mod, ib);
+            pl->prev_packet = NULL;
+        }
+    }
+    close_file(pl);
+}

-    mod->internal = calloc(1, sizeof(playlist_state_t));
-    pl = (playlist_state_t *)mod->internal;

-    current = params;
-    while(current)
+static int write_ogg_data (input_module_t *mod, void *buffer, unsigned len)
+{
+    struct playlist_state *pl = (struct playlist_state *)mod->internal;
+    ogg_page page;
+
+    ogg_sync_wrote (&pl->oy, len);
+    /* data now in the stream, lets see about getting packets */
+    while (1)
{
-        if (!strcmp(current->name, "type"))
+        ogg_packet packet;
+        int result;
+
+        while (ogg_stream_packetpeek (&pl->os, &packet) > 0)
{
-            int current_module = 0;
+            /* we cache one packet so that EOS can be marked even if it's missing */
+            if (pl->prev_packet)
+            {
+                input_buffer *ib = NULL;
+                int buffers_ok = 1;

-            while(modules[current_module].init)
-            {
-                if(!strcmp(current->value, modules[current_module].name))
+                while (1)
{
-                    init = modules[current_module].init;
-                    break;
+                    if (metadata_update_signalled)
+                    {
+                        LOG_INFO0("switching to next entry in playlist");
+                        metadata_update_signalled = 0;
+                        return 0;
+                    }
+                    if (ices_config->shutdown || move_to_next_input)
+                    {
+                        LOG_INFO0("module shutdown requested");
+                        pl->terminate = 1;
+                        return 0;
+                    }
+                    ib = input_alloc_buffer (mod);
+                    if (ib)
+                        break;
+
+                    if (pl->sleep && buffers_ok)
+                    {
+                        LOG_ERROR0 ("failed buffer allocation");
+                        buffers_ok = 0;
+                    }
+
+                    input_adv_sleep (ALLOCATION_DELAY);
+                    input_sleep ();
}
-                current_module++;
+                /* pass BOS just in case EOS is missing in the input stream */
+                prepare_buffer (ib, pl, packet.b_o_s);
+                send_for_processing (mod, ib);
+                if (metadata_update_signalled)
+                    return 0;
}
-
-            if(!init)
-            {
-                LOG_ERROR1("Unknown playlist type \"%s\"", current->value);
-                goto fail;
-            }
+            /* copy the packet */
+            pl->prev_packet = copy_ogg_packet (&packet);
+            ogg_stream_packetout (&pl->os, NULL);
+            if (pl->sleep)
+                input_sleep();
}
-        current = current->next;
-    }
-
-    if(init)
-    {
-        if(init(params, pl))
+        result = ogg_sync_pageout (&pl->oy, &page);
+        if (result < 0)
{
-            LOG_ERROR0("Playlist initialisation failed");
-            goto fail;
+            LOG_WARN1("Corrupt or missing data in stream from %s", pl->filename);
+            break;
}
-        else
+        if (result == 0)
+            break;
+        /* ok, we have a page, now do we initialise the stream */
+        if (ogg_page_bos (&page))
{
-            ogg_sync_init(&pl->oy);
-            return mod; /* Success. Finished initialising */
+            if (pl->os_init)
+                ogg_stream_clear (&pl->os);
+            ogg_stream_init (&pl->os, ogg_page_serialno (&page));
+            pl->os_init = 1;
}
+        ogg_stream_pagein (&pl->os, &page);
}
-    else
-        LOG_ERROR0("No playlist type given, cannot initialise playlist module");
+    return len;
+}

-fail:
-    if (mod)
+
+/* Core streaming function for this module
+ * This is what actually produces the data which gets streamed.
+ *
+ */
+
+static int playlist_read(input_module_t *mod)
+{
+    struct playlist_state *pl = (struct playlist_state *)mod->internal;
+
+    /* setup callbacks */
+    pl->write_func = write_ogg_data;
+    pl->alloc_buffer = alloc_ogg_buffer;
+    pl->flush_func = flush_file;
+    mod->release_input_buffer = playlist_clear_buffer;
+
+    /* start processing */
+    while (find_next_entry(pl))
{
-        if (mod->internal)
-            free(mod->internal);
-        free(mod);
+        while (1)
+        {
+            void *buffer;
+            int len;
+
+            buffer = pl->alloc_buffer (mod, OGG_BUFSIZE);
+            len = fread (buffer, 1, OGG_BUFSIZE, pl->current_file);
+            if (len == 0)
+                break;
+
+            if (pl->write_func (mod , buffer, len) < len)
+                break;
+        }
+        LOG_DEBUG0 ("End of file");
+        /* callback for flush */
+        pl->flush_func (mod);
}
+    return 0;
+}

-    return NULL;
+
+static int playlist_init_buffer (input_module_t *mod, input_buffer *ib)
+{
+    ib->type = ICES_INPUT_VORBIS;
+    ib->subtype = INPUT_SUBTYPE_INVALID;
+    ib->critical = 0;
+    ib->mod = mod;
+    return 0;
}


+int playlist_init_module (input_module_t *mod)
+{
+    LOG_INFO0 ("Initialise playlist module");
+    mod->type = ICES_INPUT_VORBIS;
+    mod->name = "playlist";
+    mod->getdata = playlist_read;
+    mod->initialise_buffer = playlist_init_buffer;
+    mod->buffer_count = ices_config->runner_count*100 + 200;
+    mod->prealloc_count = ices_config->runner_count * 30 + 40;
+
+    mod->internal = calloc(1, sizeof(struct playlist_state));
+    if (mod->internal == NULL)
+        return -1;
+
+    return 0;
+}
+
+

Modified: icecast/branches/kh/ices/src/im_playlist.h
===================================================================
--- icecast/trunk/ices/src/im_playlist.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_playlist.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_playlist.h
* - Basic playlist functionality
*
- * $Id: im_playlist.h,v 1.6 2004/01/12 23:39:39 karl Exp $
+ * $Id: im_playlist.h,v 1.3 2002/07/07 11:07:55 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -17,25 +17,40 @@
#include "inputmodule.h"
#include <ogg/ogg.h>

-typedef struct _playlist_state_tag
+struct playlist_state
{
-    FILE *current_file;
-    char *filename; /* Currently streaming file */
-    int errors; /* Consecutive errors */
-    int current_serial;
-    int nexttrack;
-    int allow_repeat;
-    ogg_sync_state oy;
-
-    char *(*get_filename)(void *data); /* returns the next desired filename */
+	FILE *current_file;
+	char *filename; /* Currently streaming file */
+	int errors; /* Consecutive errors */
+	int next_track;
+    int terminate;
+    int sleep;
+    int os_init;
+    int more_headers;
+    int prev_window;
+    ogg_int64_t granulepos;
+	ogg_sync_state oy;
+	ogg_stream_state os;
+    vorbis_info vi;
+    vorbis_comment vc;
+    ogg_packet *prev_packet;
+    ogg_packet *prev_op;
+
+	char *(*get_filename)(void *data); /* returns the next desired filename */
void (*free_filename)(void *data, char *fn); /* Called when im_playlist is
done with this filename */
-    void (*clear)(void *data); /* module clears self here */
+	void (*clear) (void *data); /* module clears self here */
+    int (*write_func) (input_module_t *mod, void *buffer, unsigned len);
+    void *(*alloc_buffer) (input_module_t *mod, unsigned len);
+    void (*flush_func) (input_module_t *mod);

-    void *data; /* Internal data for this particular playlist module */
+	void *data; /* Internal data for this particular playlist module */
+};

-} playlist_state_t;
+int  playlist_open_module (input_module_t *);
+void playlist_close_module (input_module_t *mod);
+int  playlist_init_module (input_module_t *);
+void playlist_shutdown_module (input_module_t *mod);

-input_module_t *playlist_open_module(module_param_t *params);

#endif  /* __IM_PLAYLIST_H__ */

Modified: icecast/branches/kh/ices/src/im_sun.c
===================================================================
--- icecast/trunk/ices/src/im_sun.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_sun.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,11 +1,12 @@
/* im_sun.c
* - Raw PCM input from Solaris audio devices
*
- * $Id: im_sun.c,v 1.13 2004/01/17 04:24:10 karl Exp $
+ * $Id: im_sun.c,v 1.7 2002/08/03 15:05:39 msmith Exp $
*
* by Ciaran Anscomb <ciarana at rd.bbc.co.uk>, based
* on im_oss.c which is...
- * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2001 Michael Smith <msmith at xiph.org>
+ * Copyright (c) 2003 karl Heyes <karl at xiph.org>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -13,8 +14,11 @@
* with this source.
*/

+#define SAMPLES    8192
+
+
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include <config.h>
#endif

#include <stdio.h>
@@ -31,8 +35,10 @@
#endif
#include <fcntl.h>

-#include "cfgparse.h"
+
+#include "thread/thread.h"
#include "stream.h"
+#include "signals.h"
#include "inputmodule.h"
#include "metadata.h"

@@ -43,224 +49,301 @@

#define BUFSIZE 8192

-static void close_module(input_module_t *mod)
+#define MAX_DEAD_AUDIO_BYTES 48000*2*2
+
+
+static void sun_return_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
{
-    if(mod)
+    ib->critical = 0;
+    ib->eos = 0;
+    return;
+}
+
+
+static void sun_free_buffer (input_module_t *mod, input_buffer *ib)
+{
+    im_sun_state *s = mod->internal;
+    float **ptr;
+    int i;
+
+    ptr = ib->buf;
+    for (i=s->device_info.record.channels; i; i--)
{
-        if(mod->internal)
+        if (ptr)
{
-            im_sun_state *s = mod->internal;
-            if(s->fd >= 0)
-                close(s->fd);
-            thread_mutex_destroy(&s->metadatalock);
-            free(s);
+            free (*ptr);
+            *ptr = NULL;
}
-        free(mod);
+        ptr++;
}
+    free (ib->buf);
+    ib->buf = NULL;
}
-static int event_handler(input_module_t *mod, enum event_type ev, void *param)
+
+static int sun_initialise_buffer (input_module_t *mod, input_buffer *ib)
{
im_sun_state *s = mod->internal;
-
-    switch(ev)
+    float **ptr;
+    int i;
+
+    if ((ib->buf = calloc (1, s->default_len)) == NULL)
+        return -1;
+
+    if ((ib->buf = calloc (s->device_info.record.channels, sizeof (float*))) == NULL)
+        return -1;
+    ptr = ib->buf;
+    for (i=s->device_info.record.channels ; i; i--)
{
-        case EVENT_SHUTDOWN:
-            close_module(mod);
-            break;
-        case EVENT_NEXTTRACK:
-            s->newtrack = 1;
-            break;
-        case EVENT_METADATAUPDATE:
-            thread_mutex_lock(&s->metadatalock);
-            if(s->metadata)
-            {
-                char **md = s->metadata;
-                while(*md)
-                    free(*md++);
-                free(s->metadata);
-            }
-            s->metadata = (char **)param;
-            s->newtrack = 1;
-            thread_mutex_unlock(&s->metadatalock);
-            break;
-        default:
-            LOG_WARN1("Unhandled event %d", ev);
+        if ((*ptr = calloc (sizeof(float), s->samples)) == NULL)
return -1;
+        ptr++;
}
-
+
+    ib->type = ICES_INPUT_PCM;
+#ifdef WORDS_BIGENDIAN
+    ib->subtype = INPUT_PCM_BE_16;
+#else
+    ib->subtype = INPUT_PCM_LE_16;
+#endif
+    ib->critical = 0;
+    ib->channels = s->device_info.record.channels;
+    ib->samplerate = s->device_info.record.sample_rate;
+    ib->mod = mod;
+
return 0;
}

-static void metadata_update(void *self, vorbis_comment *vc)
-{
-    im_sun_state *s = self;
-    char **md;

-    thread_mutex_lock(&s->metadatalock);

-    md = s->metadata;
-
-    if(md)
+void sun_close_module (input_module_t *mod)
+{
+    im_sun_state *s = mod->internal;
+
+    LOG_INFO0("Closing Sun audio module");
+    if (s->fd > -1)
{
-        while(*md)
-            vorbis_comment_add(vc, *md++);
+       close (s->fd);
+       s->fd = -1;
}
+}

-    thread_mutex_unlock(&s->metadatalock);
+void sun_shutdown_module (input_module_t *mod)
+{
+    im_sun_state *s = mod->internal;
+
+    LOG_INFO0 ("Shutdown Sun audio module");
+    free (s);
+    mod->internal = NULL;
}
+

+
/* Core streaming function for this module
* This is what actually produces the data which gets streamed.
*
- * returns:  >0  Number of bytes read
- *            0  Non-fatal error.
- *           <0  Fatal error.
*/
-static int sun_read(void *self, ref_buffer *rb)
+static int sun_read(input_module_t *mod)
{
-    int result;
-    im_sun_state *s = self;
-    unsigned char *i, j;
+    im_sun_state *s = mod->internal;
+    input_buffer *ib;
+    int len;
+    int dead_air;

-    rb->buf = malloc(BUFSIZE*2*s->device_info.record.channels);
-    if(!rb->buf)
-        return -1;
-    result = read(s->fd, rb->buf, BUFSIZE*2*s->device_info.record.channels);
+    while (1)
+    {
+        if (s->user_terminated)
+        {
+            s->user_terminated = 0;
+            return 0;
+        }

-    rb->len = result;
-    rb->aux_data = s->device_info.record.sample_rate*s->device_info.record.channels*2;
+        dead_air = 100;
+        while ((ib = input_alloc_buffer (mod)) == NULL)
+        {
+            if (dead_air == 100)
+                LOG_WARN0 ("will skip input for a short time");
+            read (s->fd, dead_audio, MAX_DEAD_AUDIO_BYTES);
+            if (--dead_air == 0)
+            {
+                mod->failures++;
+                return 0;
+            }
+        }
+
+        len = read (s->fd, s->read_buffer, s->read_buffer_len);
+
+        if (len == -1)
+        {
+            LOG_ERROR1("Error reading from audio device: %s", strerror(errno));
+            input_free_buffer (ib);
+            return 0;
+        }

-    if(s->newtrack)
-    {
-        rb->critical = 1;
-        s->newtrack = 0;
-    }
+        ib->samples = len/(ib->channels*2);

-    if(result == -1 && errno == EINTR)
-    {
-        return 0; /* Non-fatal error */
+#ifdef __sparc
+        uninterleave_pcm_be ((signed char*)s->read_buffer, ib->channels, ib->samples, ib->buf);
+#else
+        uninterleave_pcm_le ((signed char*)s->read_buffer, ib->channels, ib->samples, ib->buf);
+#endif
+
+        if (s->newtrack)
+        {
+            LOG_DEBUG0 ("metadata updates flagged");
+            metadata_thread_signal (mod, ib);
+            ib->critical = 1;
+            s->newtrack = 0;
+        }
+
+        if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+        {
+            LOG_DEBUG0("marking buffer eos");
+            s->newtrack = 1;
+            ib->eos = 1;
+            if (move_to_next_input || ices_config->shutdown)
+                s->user_terminated = 1;
+        }
+        input_adv_sleep ((uint64_t)ib->samples * 1000000 / s->device_info.record.sample_rate);
+        send_for_processing (mod, ib);
}
-    else if(result <= 0)
-    {
-        if(result == 0)
-            LOG_INFO0("Reached EOF, no more data available");
-        else
-            LOG_ERROR1("Error reading from audio device: %s", strerror(errno));
-        free(rb->buf);
-        return -1;
-    }

-    return rb->len;
+    return 0;
}

-input_module_t *sun_open_module(module_param_t *params)
+
+
+int sun_init_module(input_module_t *mod)
{
-    input_module_t *mod = calloc(1, sizeof(input_module_t));
im_sun_state *s;
module_param_t *current;
char *device = "/dev/audio"; /* default device */
int sample_rate = 44100;
int channels = 2;
-    int use_metadata = 1; /* Default to on */
+    int samples = SAMPLES;

+    mod->name = "Sun Audio";
+
mod->type = ICES_INPUT_PCM;
-#ifdef WORDS_BIGENDIAN
-    mod->subtype = INPUT_PCM_BE_16;
-#else
mod->subtype = INPUT_PCM_LE_16;
-#endif
mod->getdata = sun_read;
-    mod->handle_event = event_handler;
-    mod->metadata_update = metadata_update;
+    mod->initialise_buffer = sun_initialise_buffer;
+    mod->free_input_buffer = sun_free_buffer;
+    mod->buffer_count = ices_config->runner_count*10 + 5;
+    mod->prealloc_count = ices_config->runner_count * 4;

mod->internal = calloc(1, sizeof(im_sun_state));
-    s = mod->internal;
+    do
+    {
+        if (mod->internal == NULL)
+            break;
+        s = mod->internal;

-    s->fd = -1; /* Set it to something invalid, for now */
+        s->fd = -1; /* Set it to something invalid, for now */

-    thread_mutex_create(&s->metadatalock);
+        current = mod->module_params;

-    current = params;
+        while (current) {
+            if (!strcmp(current->name, "rate"))
+                sample_rate = atoi(current->value);
+            else if (!strcmp(current->name, "channels"))
+                channels = atoi(current->value);
+            else if (!strcmp(current->name, "device"))
+                device = current->value;
+            else if (!strcmp(current->name, "samples"))
+                samples = atoi (current->value);
+            else if(!strcmp(current->name, "metadatafilename"))
+                mod->metadata_filename = current->value;
+            else
+                LOG_WARN1("ignored parameter %s for sun module", current->name);
+            current = current->next;
+        }

-    while (current) {
-        if (!strcmp(current->name, "rate"))
-            sample_rate = s->device_info.record.sample_rate = atoi(current->value);
-        else if (!strcmp(current->name, "channels"))
-            channels = s->device_info.record.channels = atoi(current->value);
-        else if (!strcmp(current->name, "device"))
-            device = current->value;
-        else if (!strcmp(current->name, "metadata"))
-            use_metadata = atoi(current->value);
-        else if(!strcmp(current->name, "metadatafilename"))
-            ices_config->metadata_filename = current->value;
-        else
-            LOG_WARN1("Unknown parameter %s for sun module", current->name);
-        current = current->next;
+        /* Try and set up what we want */
+        AUDIO_INITINFO(&s->device_info);
+        s->device_info.record.sample_rate = sample_rate;
+        s->device_info.record.channels = channels;
+        s->device_info.record.precision = 16;
+        s->device_info.record.encoding = AUDIO_ENCODING_LINEAR;
+        s->device_info.record.port = AUDIO_LINE_IN;
+        s->device_info.record.pause = 0;
+        s->device = device;
+        s->samples = samples;
+        s->default_len = samples * 2 * channels;
+        s->read_buffer_len = s->samples*2*channels;
+        s->read_buffer = malloc (s->read_buffer_len);
+
+        return 0;
}
+    while (0);
+    /* error, need to cleanup */
+    if (mod->internal)
+    {
+        free (mod->internal);
+        mod->internal = NULL;
+    }
+    return -1;
+}

+int sun_open_module (input_module_t *mod)
+{
+    im_sun_state *s = mod->internal;
+	int sample_rate = s->device_info.record.sample_rate;
+	int channels = s->device_info.record.channels;
+
/* First up, lets open the audio device */
-    if((s->fd = open(device, O_RDONLY, 0)) < 0) {
-        LOG_ERROR2("Failed to open audio device %s: %s",
-                device, strerror(errno));
-        goto fail;
-    }
+    do
+    {
+        if ((s->fd = open(s->device, O_RDONLY, 0)) < 0)
+        {
+            LOG_ERROR2("Failed to open audio device %s: %s", s->device, strerror(errno));
+            break;
+        }

-    /* Try and set up what we want */
-    AUDIO_INITINFO(&s->device_info);
-    s->device_info.record.sample_rate = sample_rate;
-    s->device_info.record.channels = channels;
-    s->device_info.record.precision = 16;
-    s->device_info.record.encoding = AUDIO_ENCODING_LINEAR;
-    s->device_info.record.port = AUDIO_LINE_IN;
-    s->device_info.record.pause = 0;
-
-    if (ioctl(s->fd, AUDIO_SETINFO, &s->device_info) < 0) {
-        LOG_ERROR2("Failed to configure audio device %s: %s",
-                device, strerror(errno));
-        goto fail;
-    }
+        if (ioctl (s->fd, AUDIO_SETINFO, &s->device_info) < 0)
+        {
+            LOG_ERROR2("Failed to configure audio device %s: %s",
+                    s->device, strerror(errno));
+            break;
+        }
#ifdef __sun
-    ioctl (s->fd, I_FLUSH, FLUSHR);
+        ioctl(s->fd, I_FLUSH, FLUSHR);
#endif
#ifdef __OpenBSD__
-    ioctl (s->fd, AUDIO_FLUSH, NULL);
+        ioctl(s->fd, AUDIO_FLUSH, NULL);
#endif

-    /* Check all went according to plan */
-    if (s->device_info.record.sample_rate != sample_rate) {
-        LOG_ERROR0("Couldn't set sampling rate");
-        goto fail;
-    }
-    if (s->device_info.record.channels != channels) {
-        LOG_ERROR0("Couldn't set number of channels");
-        goto fail;
-    }
-    if (s->device_info.record.precision != 16) {
-        LOG_ERROR0("Couldn't set 16 bit precision");
-        goto fail;
-    }
-    if (s->device_info.record.encoding != AUDIO_ENCODING_LINEAR) {
-        LOG_ERROR0("Couldn't set linear encoding");
-        goto fail;
-    }
+        /* Check all went according to plan */
+        if (s->device_info.record.sample_rate != sample_rate)
+        {
+            LOG_ERROR0("Couldn't set sampling rate");
+            break;
+        }
+        if (s->device_info.record.channels != channels) {
+            LOG_ERROR0("Couldn't set number of channels");
+            break;
+        }
+        if (s->device_info.record.precision != 16)
+        {
+            LOG_ERROR0("Couldn't set 16 bit precision");
+            break;
+        }
+        if (s->device_info.record.encoding != AUDIO_ENCODING_LINEAR)
+        {
+            LOG_ERROR0("Couldn't set linear encoding");
+            break;
+        }

-    /* We're done, and we didn't fail! */
-    LOG_INFO3("Opened audio device %s at %d channel(s), %d Hz",
-            device, channels, sample_rate);
+        /* We're done, and we didn't fail! */
+        LOG_INFO3("Opened audio device %s at %d channel(s), %d Hz",
+                s->device, channels, sample_rate);

-    if(use_metadata)
-    {
-        LOG_INFO0("Starting metadata update thread");
-        if(ices_config->metadata_filename)
-            thread_create("im_sun-metadata", metadata_thread_signal, mod, 1);
-        else
-            thread_create("im_sun-metadata", metadata_thread_stdin, mod, 1);
+        s->newtrack = 1;
+        return 0;
}
+    while (0);

-    return mod;
-
-fail:
-    close_module(mod); /* safe, this checks for valid contents */
+    sun_close_module(mod); /* safe, this checks for valid contents */
return NULL;
}

+

Modified: icecast/branches/kh/ices/src/im_sun.h
===================================================================
--- icecast/trunk/ices/src/im_sun.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_sun.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_sun.h
* - read pcm data from sun devices
*
- * $Id: im_sun.h,v 1.4 2003/07/01 23:53:06 karl Exp $
+ * $Id: im_sun.h,v 1.2 2001/09/25 12:04:21 msmith Exp $
*
* by Ciaran Anscomb <ciarana at rd.bbc.co.uk>, based
* on im_oss.c which is...
@@ -18,20 +18,28 @@

#include <sys/audioio.h>
#include "inputmodule.h"
-#include "thread/thread.h"
#include <ogg/ogg.h>

typedef struct
{
-    audio_info_t device_info;
-    int fd;
-    int fdctl;
-    char **metadata;
-    int newtrack;
-    mutex_t metadatalock;
+	audio_info_t device_info;
+	int fd;
+	int fdctl;
+	char **metadata;
+	int newtrack;
+	int user_terminated;
+	int samples;
+	int default_len;
+	int read_buffer_len;
+	void *read_buffer;
+	const char *device;
} im_sun_state;

-input_module_t *sun_open_module(module_param_t *params);
+int sun_open_module (input_module_t *mod);
+void sun_close_module (input_module_t *mod);
+void sun_shutdown_module (input_module_t *mod);
+int  sun_init_module (input_module_t *mod);

+
#endif  /* __IM_SUN_H__ */


Modified: icecast/branches/kh/ices/src/input.c
===================================================================
--- icecast/trunk/ices/src/input.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/input.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,10 +1,9 @@
/* input.c
*  - Main producer control loop. Fetches data from input modules, and controls
- *    submission of these to the instance threads. Timing control happens here.
- *
- * $Id: input.c,v 1.32 2004/03/11 17:22:59 karl Exp $
+ *    submission of these to the runner threads. Timing control happens here.
+ *    originally based on the work by Michael Smith
*
- * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2001-4 Karl Heyes <karl at xiph.org>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -13,41 +12,50 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
+#include <pwd.h>
#include <sys/types.h>
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#include <ogg/ogg.h>
#include <vorbis/codec.h>
-#include <string.h>

-#include <timing/timing.h>
-#include <thread/thread.h>
+#include "runner.h"
+#include "thread/thread.h"
+#include "timing/timing.h"
#include "cfgparse.h"
-#include "stream.h"
-#include "input.h"
-#include "event.h"
+#include "metadata.h"
#include "inputmodule.h"
#include "im_playlist.h"
-#include "im_stdinpcm.h"
+#include "im_pcm.h"
+#include "signals.h"

-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_AUDIO
#include "im_oss.h"
#endif

-#ifdef HAVE_SUN_AUDIO
-#include "im_sun.h"
+#ifdef HAVE_ALSA_AUDIO
+#include "im_alsa.h"
#endif

-#ifdef HAVE_ALSA
-#include "im_alsa.h"
+#ifdef HAVE_JACK
+#include "im_jack.h"
#endif

+#ifdef HAVE_SUN_AUDIO
+#include "im_sun.h"
+#endif
+
#ifdef _WIN32
typedef __int64 int64_t
typedef unsigned __int64 uint64_t
@@ -58,478 +66,378 @@

#define MAX_BUFFER_FAILURES 15

-typedef struct _timing_control_tag
-{
-    uint64_t starttime;
-    uint64_t senttime;
-    uint64_t samples;
-    uint64_t oldsamples;
-    unsigned samplerate;
-    long serialno;
-} timing_control;
+char dead_audio [DEAD_AIR_BYTES];

-typedef struct _module
-{
-    char *name;
-    input_module_t *(*open)(module_param_t *params);
-} module;
-
-static module modules[] = {
-    { "playlist", playlist_open_module},
-    { "stdinpcm", stdin_open_module},
-#ifdef HAVE_OSS
-    { "oss", oss_open_module},
+module_t modules[] = {
+	{ "playlist",   playlist_init_module,   playlist_open_module,  playlist_close_module,  playlist_shutdown_module },
+    { "pcm",        pcm_initialise_module,  pcm_open_module,       pcm_close_module,       pcm_shutdown_module },
+#ifdef HAVE_ALSA_AUDIO
+	{ "alsa",       alsa_init_module,       alsa_open_module,      alsa_close_module,      alsa_shutdown_module },
#endif
-#ifdef HAVE_SUN_AUDIO
-    { "sun", sun_open_module},
+#ifdef HAVE_JACK
+	{ "jack",       jack_init_module,       jack_open_module,      jack_close_module,      jack_shutdown_module },
#endif
-#ifdef HAVE_ALSA
-    { "alsa", alsa_open_module},
+#ifdef HAVE_OSS_AUDIO
+	{ "oss",        oss_init_module,        oss_open_module,       oss_close_module,       oss_shutdown_module },
#endif
-    {NULL,NULL}
+#ifdef HAVE_SUN_AUDIO
+	{ "sun",        sun_init_module,        sun_open_module, sun_close_module, sun_shutdown_module },
+#endif
+	{NULL,NULL,NULL,NULL,NULL}
};

+
static timing_control control;

-
-/* This is identical to shout_sync(), really. */
-void input_sleep(void)
+void input_sleep (void)
{
-    int64_t sleep;
+    signed long sleep;

-    /* no need to sleep if we haven't sent data */
-    if (control.senttime == 0) return;
+    sleep = control.senttime - ((timing_get_time() - control.starttime) *1000);

-    sleep = ((double)control.senttime / 1000) -
-        (timing_get_time() - control.starttime);
-
-    /* trap for long sleeps, typically indicating a clock change.  it's not */
-    /* perfect though, as low bitrate/low samplerate vorbis can trigger this */
-    if(sleep > 8000) {
-        LOG_WARN1("Extended sleep requested (%ld ms), sleeping for 5 seconds",
-                sleep);
-        timing_sleep(5000);
+    if (sleep > 1000000)
+    {
+        LOG_WARN1 ("Sleeping for over 1 second (%ld), resetting timer", sleep);
+        control.starttime = timing_get_time();
+        control.senttime = 0;
+        sleep = 0;
}
-    else if(sleep > 0)
-        timing_sleep((uint64_t)sleep);
+#if 0
+    printf ("sentime is %lld\n", control.senttime/1000);
+    printf ("Sleeping for %ld\n", sleep);
+#endif
+    /* only sleep if the difference is above 5ms, might as well use the timeslice */
+    if (sleep > 5000)
+    {
+        /* printf ("Sleeping for %ld\n", sleep);
+        LOG_DEBUG1("sleep is %ld\n", sleep); */
+        thread_sleep((unsigned long)sleep);
+    }
}

-int input_calculate_pcm_sleep(unsigned bytes, unsigned bytes_per_sec)
+void input_adv_sleep (unsigned long adv)
{
-    control.senttime += ((uint64_t)bytes * 1000000)/bytes_per_sec;
-
-    return 0;
+    control.senttime += adv; /* in uS */
+    /* printf ("Adding %lu\n", adv); */
}

-int input_calculate_ogg_sleep(ogg_page *page)
-{
-    static ogg_stream_state os;
-    ogg_packet op;
-    static vorbis_info vi;
-    static vorbis_comment vc;
-    static int need_start_pos, need_headers, state_in_use = 0;
-    static int serialno = 0;
-    static uint64_t offset;
-    static uint64_t first_granulepos;

-    if (ogg_page_granulepos(page) == -1)
+void uninterleave_pcm_le (signed char  *src, unsigned channels, unsigned samples, float **dest)
+{
+    unsigned int i,j;
+    float *dst;
+    signed char *from = src;
+    for (j=0 ; j<channels ; j++)
{
-        LOG_ERROR0("Timing control: corrupt timing information in vorbis file, cannot stream.");
-        return -1;
+        dst = dest[j];
+        from = src+(j<<1);
+        for(i=samples; i ; i--)
+        {
+            *dst = (((*(from+1)) << 8) | (0x00ff&(int)(*from))) / 32768.f;
+            dst++;
+            from += channels*2;
+        }
}
-    if (ogg_page_bos (page))
-    {
-        control.oldsamples = 0;
+}

-        if (state_in_use)
-            ogg_stream_clear (&os);
-        ogg_stream_init (&os, ogg_page_serialno (page));
-        serialno = ogg_page_serialno (page);
-        state_in_use = 1;
-        vorbis_info_init (&vi);
-        vorbis_comment_init (&vc);
-        need_start_pos = 1;
-        need_headers = 3;
-        offset = (uint64_t)0;
+
+
+void uninterleave_pcm_be (signed char  *src, unsigned channels, unsigned samples, float **dest)
+{
+    unsigned int i,j;
+    float *dst;
+    signed char *from = src;
+    for (j=0 ; j<channels ; j++)
+    {
+        dst = dest[j];
+        from = src+(j<<1);
+        for(i=samples; i ; i--)
+        {
+            *dst = (((*from) << 8) | (0x00ff&(int)(*(from+1)))) / 32768.f;
+            dst++;
+            from += channels*2;
+        }
}
-    if (need_start_pos)
+}
+
+static int initialise_input_modules (void)
+{
+    input_module_t *mod;
+
+    mod = ices_config->inputs;
+    while (mod)
{
-        int found_first_granulepos = 0;
+        int i;
+        input_buffer *ib;

-        ogg_stream_pagein (&os, page);
-        while (ogg_stream_packetout (&os, &op) == 1)
+        if (mod->initialise_module (mod) < 0)
+            return -1;
+
+        mod->free_list_tail = &mod->free_list;
+
+        /* set the global minimum if need be */
+        if (mod->buffer_count < 2)
+            mod->buffer_count = 2;
+
+        if (mod->prealloc_count < 2)
+            mod->prealloc_count = 2;
+        if (mod->prealloc_count > mod->buffer_count)
+            mod->prealloc_count = mod->buffer_count;
+
+        /* create the pre-allocated buffers */
+        for (i=mod->prealloc_count; i ; i--)
{
-            if (need_headers)
-            {
-                if (vorbis_synthesis_headerin (&vi, &vc, &op) < 0)
-                {
-                    LOG_ERROR0("Timing control: can't determine sample rate for input, not vorbis.");
-                    control.samplerate = 0;
-                    return -1;
-                }
-                need_headers--;
-                control.samplerate = vi.rate;
+            ib = calloc (1, sizeof (input_buffer));
+            if (ib == NULL)
+               return -1;
+            if (mod->initialise_buffer && mod->initialise_buffer (mod, ib) < 0)
+               return -1;

-                if (need_headers == 0)
-                {
-                    vorbis_comment_clear (&vc);
-                    first_granulepos = (uint64_t)0;
-                    return 0;
-                }
-                continue;
-            }
-            /* headers have been read */
-            if (first_granulepos == 0 && op.granulepos > 0)
-            {
-                first_granulepos = op.granulepos;
-                found_first_granulepos = 1;
-            }
-            offset += vorbis_packet_blocksize (&vi, &op) / 4;
+            *mod->free_list_tail = ib;
+            mod->free_list_tail = &ib->next;
}
-        if (!found_first_granulepos)
-            return 0;
+        LOG_DEBUG4 ("Module %d (%s) has pre-allocated %d buffers out of %d",
+                mod->id, mod->name, mod->prealloc_count, mod->buffer_count);

-        need_start_pos = 0;
-        control.oldsamples = first_granulepos - offset;
-        vorbis_info_clear (&vi);
-        ogg_stream_clear (&os);
-        state_in_use = 0;
+        mod = mod->next;
}
-    if (serialno != ogg_page_serialno (page))
-    {
-        LOG_ERROR0 ("Found page which does not belong to current logical stream");
-        return -1;
-    }
-    control.samples = ogg_page_granulepos (page) - control.oldsamples;
-    control.oldsamples = ogg_page_granulepos (page);

-    control.senttime += ((uint64_t)control.samples * 1000000 /
-            (uint64_t)control.samplerate);
-
return 0;
}


-
-void input_flush_queue(buffer_queue *queue, int keep_critical)
+void input_free_buffer(input_buffer *ib)
{
-    queue_item *item, *next, *prev=NULL;
+    input_module_t *mod;
+    if (ib == NULL)
+        return;

-    LOG_DEBUG0("Input queue flush requested");
+    mod = ib->mod;

-    thread_mutex_lock(&queue->lock);
-    if(!queue->head)
+    if (ib->serial != mod->expected)
{
-        thread_mutex_unlock(&queue->lock);
-        return;
+        LOG_DEBUG2("expected %lld, saw %lld", mod->expected, ib->serial);
+        mod->expected = ib->serial + 1;
}
-
-    item = queue->head;
-    while(item)
+    else
+        mod->expected++;
+    metadata_free (ib->metadata);
+    ib->metadata = NULL;
+    if (mod->release_input_buffer)
+        mod->release_input_buffer (mod, ib);
+    ib->next = NULL;
+    ib->critical = 0;
+    ib->eos = 0;
+    mod->released_serial++;
+#if 0
+    /* every so often, actually free a buffer, so that memory usage is reduced  */
+    if (mod->allotted_serial - mod->released_serial > mod->prealloc_count)
+    /* if ((ib->serial & (uint64_t)255) == 255) */
{
-        next = item->next;
-
-        if(!(keep_critical && item->buf->critical))
-        {
-            thread_mutex_lock(&ices_config->refcount_lock);
-            item->buf->count--;
-            if(!item->buf->count)
-            {
-                free(item->buf->buf);
-                free(item->buf);
-            }
-            thread_mutex_unlock(&ices_config->refcount_lock);
-
-            if(prev)
-                prev->next = next;
-            else
-                queue->head = next;
-
-            free(item);
-            item = next;
-
-            queue->length--;
-        }
-        else
-        {
-            prev = item;
-            item = next;
-        }
+        if (mod->free_input_buffer)
+            mod->free_input_buffer (mod, ib);
+        free (ib);
+        /* LOG_DEBUG1 ("removed buffer, length now %d", mod->allotted_serial - mod->released_serial); */
}
-
-    /* Now, fix up the tail pointer */
-    queue->tail = NULL;
-    item = queue->head;
-
-    while(item)
+    else
+#endif
{
-        queue->tail = item;
-        item = item->next;
+        *mod->free_list_tail = ib;
+        mod->free_list_tail = &ib->next;
}

-    thread_mutex_unlock(&queue->lock);
+    return;
}

-void input_loop(void)
+input_buffer *input_alloc_buffer (input_module_t *mod)
{
-    input_module_t *inmod=NULL;
-    instance_t *instance, *prev, *next;
-    queue_item *queued;
-    int shutdown = 0;
-    int current_module = 0;
-    int valid_stream = 1;
-    int inc_count;
-    int not_waiting_for_critical;
+    input_buffer *ib;

-    thread_cond_create(&ices_config->queue_cond);
-    thread_cond_create(&ices_config->event_pending_cond);
-    thread_mutex_create(&ices_config->refcount_lock);
-    thread_mutex_create(&ices_config->flush_lock);
-
-    memset (&control, 0, sizeof (control));
-
-    while(ices_config->playlist_module && modules[current_module].open)
+    do
{
-        if(!strcmp(ices_config->playlist_module, modules[current_module].name))
+        ib = mod->free_list;
+        if (ib == NULL || ib->next == NULL)
{
-            inmod = modules[current_module].open(ices_config->module_params);
-            break;
+            unsigned in_use = mod->allotted_serial - mod->released_serial;
+            if (in_use > mod->buffer_count)
+                return NULL;
+            ib = calloc (1, sizeof (input_buffer));
+            if (ib == NULL)
+                return NULL;
+            if (mod->initialise_buffer && mod->initialise_buffer (mod, ib) < 0)
+                return NULL;
+            mod->buffer_alloc++;
+            mod->delay_buffer_check = 1000;
+            /* LOG_DEBUG1 ("added buffer, length now %d", mod->allotted_serial - mod->released_serial); */
}
-        current_module++;
+        else
+        {
+            mod->free_list = ib->next;
+            if (mod->delay_buffer_check == 0 && mod->buffer_alloc > mod->prealloc_count)
+            {
+                /* LOG_DEBUG0("reducing free list"); */
+                if (mod->free_input_buffer)
+                    mod->free_input_buffer (mod, ib);
+                mod->buffer_alloc--;
+                ib = NULL;
+                mod->delay_buffer_check = 500;
+                continue;
+            }
+            if (mod->delay_buffer_check)
+                mod->delay_buffer_check--;
+        }
}
+    while (ib == NULL);

-    if(!inmod)
-    {
-        LOG_ERROR1("Couldn't initialise input module \"%s\"",
-                ices_config->playlist_module);
-        return;
-    }
+    ib->next = NULL;
+    ib->serial = mod->allotted_serial++;

-    ices_config->inmod = inmod;
+    return ib;
+}


-    /* ok, basic config stuff done. Now, we want to start all our listening
-     * threads.
-     */

-    instance = ices_config->instances;
+void process_input(input_module_t *mod)
+{
+	if (!mod)
+	{
+		LOG_ERROR0("NULL input module");
+		return;
+	}

-    while(instance)
-    {
-        stream_description *arg = calloc(1, sizeof(stream_description));
-        arg->stream = instance;
-        arg->input = inmod;
-        /*
-        if(instance->savefilename != NULL)
-            thread_create("savefile", savefile_stream, arg, 1);
-         */
-        thread_create("stream", ices_instance_stream, arg, 1);
+    move_to_next_input = 0;
+    control.samples = control.oldsamples = 0;

-        instance = instance->next;
-    }
-    /* treat as if a signal has arrived straight away */
-    signal_usr1_handler (0);
+    while (mod->getdata (mod))
+        ;
+}

-    /* now we go into the main loop
-     * We shut down the main thread ONLY once all the instances
-     * have killed themselves.
-     */
-    while(!shutdown)
-    {
-        ref_buffer *chunk = calloc(1, sizeof(ref_buffer));
-        buffer_queue *current;
-        int ret;
+static input_module_t *open_next_input_module (input_module_t *mod)
+{
+    input_module_t *next_mod = mod;

-        instance = ices_config->instances;
-        prev = NULL;
-
-        while(instance)
+    if (ices_config->shutdown || mod == NULL)
+        return NULL;
+    do
+    {
+        LOG_DEBUG1 ("checking module %d", next_mod->id);
+        if (next_mod->failures < 10)
{
-            /* if an instance has died, get rid of it
-            ** this should be replaced with something that tries to
-            ** restart the instance, probably.
-            */
-            if (instance->died)
+            if (next_mod->open_module)
{
-                LOG_DEBUG0("An instance died, removing it");
-                next = instance->next;
+                time_t start = time (NULL);

-                if (prev)
-                    prev->next = next;
-                else
-                    ices_config->instances = next;
-
-                /* Just in case, flush any existing buffers
-                 * Locks shouldn't be needed, but lets be SURE */
-                thread_mutex_lock(&ices_config->flush_lock);
-                input_flush_queue(instance->queue, 0);
-                thread_mutex_unlock(&ices_config->flush_lock);
-
-                config_free_instance(instance);
-                free(instance);
-
-                instance = next;
-                continue;
+                if (next_mod->start+2 > start)
+                {
+                    LOG_WARN0("restarted input within 2 seconds, it probably failed");
+                    next_mod->failures++;
+                }
+                if (next_mod->open_module (next_mod) == 0)
+                {
+                    next_mod->start = start;
+                    ices_config->next_track = 0;
+                    return next_mod;
+                }
}
-
-            prev = instance;
-            instance = instance->next;
+            else
+                next_mod->failures++;
}
+        else
+            LOG_WARN2 ("Too many failures on input module %d (%s)", next_mod->id, next_mod->name);

-        instance = ices_config->instances;
+        next_mod = next_mod->next;

-        if(!instance)
-        {
-            shutdown = 1;
-            free(chunk);
-            continue;
-        }
+    } while (next_mod != mod && next_mod);

-        if(ices_config->shutdown) /* We've been signalled to shut down, but */
-        {                          /* the instances haven't done so yet... */
-            timing_sleep(250); /* sleep for quarter of a second */
-            free(chunk);
-            continue;
-        }
-
-        /* If this is the first time through, set initial time. This should
-         * be done before the call to inmod->getdata() below, in order to
-         * properly keep time if this input module blocks.
-         */
-        if (control.starttime == 0)
-            control.starttime = timing_get_time();
+    return NULL;
+}

-        /* get a chunk of data from the input module */
-        ret = inmod->getdata(inmod->internal, chunk);

-        /* input module signalled non-fatal error. Skip this chunk */
-        if(ret==0)
-        {
-            free(chunk);
-            continue;
-        }
+static void free_modules()
+{
+    input_module_t *mod = ices_config->inputs;
+    input_buffer *next, *ib;

-        /* Input module signalled fatal error, shut down - nothing we can do
-         * from here */
-        if(ret < 0)
+    LOG_DEBUG0 ("freeing up module storage");
+    while (mod)
+    {
+        ib = mod->free_list;
+        while (ib)
{
-            ices_config->shutdown = 1;
-            thread_cond_broadcast(&ices_config->queue_cond);
-            free(chunk);
-            continue;
+            next = ib->next;
+            if (mod->free_input_buffer)
+                mod->free_input_buffer (mod, ib);
+            free (ib);
+            ib = next;
}
+        mod->free_list = NULL;
+        mod->free_list_tail = NULL;
+        mod = mod->next;
+    }
+}

-        if(chunk->critical)
-            valid_stream = 1;
+void send_for_processing (input_module_t *mod, input_buffer *ib)
+{
+#if 0
+    if (ib->critical) printf ("BOS seen\n");
+    if (ib->eos) printf ("EOS seen\n");
+#endif
+    send_to_runner (ices_config->runners, ib);
+}

-        if(ret < 0) {
-            /* Tell the input module to go to the next track, hopefully allowing
-             * resync. */
-            ices_config->inmod->handle_event(ices_config->inmod,
-                    EVENT_NEXTTRACK,NULL);
-            valid_stream = 0;
-        }

-        inc_count = 0;
-        not_waiting_for_critical = 0;
+void *input_loop(void *arg)
+{
+    input_module_t *mod;
+    struct runner *r = ices_config->runners;

-        if(valid_stream)
-        {
-            while(instance)
-            {
-                if(instance->wait_for_critical && !chunk->critical)
-                {
-                    instance = instance->next;
-                    continue;
+    start_runners();
+    thread_sleep (300000);

-                }
+    if (initialise_input_modules () < 0)
+    {
+        printf ("Unable to initialise input modules\n");
+        return NULL;
+    }

-                not_waiting_for_critical = 1;
+    /* start the clock */
+    control.starttime = timing_get_time();

-                if(instance->skip)
-                {
-                    instance = instance->next;
-                    continue;
-                }
-
-                queued = malloc(sizeof(queue_item));
-
-                queued->buf = chunk;
-                current = instance->queue;
-
-                inc_count++;
-
-                thread_mutex_lock(&current->lock);
-
-                if(current->head == NULL)
-                {
-                    current->head = current->tail = queued;
-                    current->head->next = current->tail->next = NULL;
-                }
-                else
-                {
-                    current->tail->next = queued;
-                    queued->next = NULL;
-                    current->tail = queued;
-                }
+    mod = open_next_input_module (ices_config->inputs);

-                current->length++;
-                thread_mutex_unlock(&current->lock);
+    while (ices_config->shutdown == 0)
+    {
+        if (mod == NULL)
+            break;

-                instance = instance->next;
-            }
-        }
+        process_input (mod);

-        /* If everything is waiting for a critical buffer, force one
-         * early. (This will take effect on the next pass through)
-         */
-        if(valid_stream && !not_waiting_for_critical) {
-            ices_config->inmod->handle_event(ices_config->inmod,
-                    EVENT_NEXTTRACK,NULL);
-            instance = ices_config->instances;
-            while(instance) {
-                thread_mutex_lock(&ices_config->flush_lock);
-                input_flush_queue(instance->queue, 0);
-                instance->wait_for_critical = 0;
-                thread_mutex_unlock(&ices_config->flush_lock);
-                instance = instance->next;
-            }
+        if (mod->close_module)
+        {
+            LOG_INFO0("Closing input module");
+            mod->close_module (mod);
}

-        /* Make sure we don't end up with a 0-refcount buffer that
-         * will never hit any of the free points. (this happens
-         * if all threads are set to skip, for example).
-         */
-        thread_mutex_lock(&ices_config->refcount_lock);
-        chunk->count += inc_count;
-        if(!chunk->count)
+        mod = mod->next;
+        if (mod == NULL)
{
-            free(chunk->buf);
-            free(chunk);
+            if (ices_config->input_once_thru)
+            {
+               ices_config->shutdown = 1;
+               break;
+            }
+            else
+                mod = ices_config->inputs;
}
-        thread_mutex_unlock(&ices_config->refcount_lock);

-        if(valid_stream) {
-            /* wake up the instances */
-            thread_cond_broadcast(&ices_config->queue_cond);
-
-        }
+        mod = open_next_input_module (mod);
}

-    LOG_INFO0 ("All instances removed, shutting down...");
+	LOG_DEBUG0("All input stopped, shutting down.");

-    ices_config->shutdown = 1;
-    thread_cond_broadcast(&ices_config->event_pending_cond);
-    timing_sleep(250); /* sleep for quarter of a second */
+    runner_close (r);
+    free_modules();

-    thread_cond_destroy(&ices_config->queue_cond);
-    thread_cond_destroy(&ices_config->event_pending_cond);
-    thread_mutex_destroy(&ices_config->flush_lock);
-    thread_mutex_destroy(&ices_config->refcount_lock);
-
-    inmod->handle_event(inmod, EVENT_SHUTDOWN, NULL);
-
-    return;
+    return NULL;
}

-

Modified: icecast/branches/kh/ices/src/inputmodule.h
===================================================================
--- icecast/trunk/ices/src/inputmodule.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/inputmodule.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* inputmodule.h
* - the interface for input modules to implement.
*
- * $Id: inputmodule.h,v 1.3 2003/03/16 14:21:48 msmith Exp $
+ * $Id: inputmodule.h,v 1.2 2001/09/25 12:04:21 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -14,32 +14,149 @@
#ifndef __INPUTMODULE_H__
#define __INPUTMODULE_H__

-#include "stream.h"
-#include "event.h"
-
#include <vorbis/codec.h>

-typedef enum _input_type {
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+typedef struct _input_module_tag input_module_t;
+typedef struct _timing_control_tag  timing_control;
+typedef struct _input_buffer_tag input_buffer;
+typedef struct _module_param_tag module_param_t;
+typedef struct _module module_t;
+
+#define DEAD_AIR_BYTES  16384
+extern char dead_audio [DEAD_AIR_BYTES];
+
+typedef enum {
+    ICES_INPUT_NONE = 0,
ICES_INPUT_PCM,
ICES_INPUT_VORBIS,
+    ICES_INPUT_VORBIS_PACKET
/* Can add others here in the future, if we want */
} input_type;

typedef enum _input_subtype {
+    INPUT_SUBTYPE_INVALID,
INPUT_PCM_LE_16,
INPUT_PCM_BE_16,
+    INPUT_PCM_UNINTERLEAVE
} input_subtype;

-typedef struct _input_module_tag {
+
+#define MIN_INPUT_BUFFERS 12
+
+struct _input_buffer_tag
+{
+    uint64_t serial;
+    void *buf;          /* could be ogg or pcm data */
+    /* unsigned len; */
+
+    int critical;
+    int eos;
+
+    /* long aux_data; */
input_type type;
input_subtype subtype;
-    int (*getdata)(void *self, ref_buffer *rb);
-    int (*handle_event)(struct _input_module_tag *self, enum event_type event,
-            void *param);
-    void (*metadata_update)(void *self, vorbis_comment *vc);
+    char **metadata;
+    input_module_t *mod;

+    input_buffer *next;         /* for list work */
+
+    unsigned samplerate;
+    unsigned channels;
+    unsigned samples;
+};
+
+
+
+struct _timing_control_tag
+{
+    uint64_t starttime;
+    uint64_t senttime;
+    uint64_t oldsamples;
+    int samples;
+    int samplerate;
+    int channels;
+};
+
+
+
+struct _module_param_tag
+{
+    char *name;
+    char *value;
+
+    module_param_t *next;
+};
+
+
+struct _input_module_tag
+{
+    module_param_t *module_params;
+    char *module;
+
+    unsigned id;
+    const char *name;
+    input_type type;
+    input_subtype subtype;
+    unsigned failures;
+    time_t started;
+    unsigned buffer_count;
+    unsigned buffer_alloc;
+    unsigned prealloc_count;
+    unsigned delay_buffer_check;
+    time_t start;
+
+    int  (*getdata)(input_module_t *self);
+    int  (*initialise_module)(input_module_t *);
+    int  (*open_module)(input_module_t *);
+    void (*close_module)(input_module_t *);
+    void (*shutdown_module)(input_module_t *);
+    void (*release_input_buffer)(input_module_t *, input_buffer *);
+    void (*free_input_buffer)(input_module_t *, input_buffer *);
+    int  (*initialise_buffer)(input_module_t *, input_buffer *);
+
+    input_buffer   *free_list, **free_list_tail;
+    uint64_t  expected;
+    uint64_t  allotted_serial, released_serial;
+
+    char *metadata_filename;
+
void *internal; /* For the modules internal state data */
-} input_module_t;

+    input_module_t *next;
+};
+
+
+struct _module
+{
+    char *name;
+    int  (*initialise)(input_module_t *);
+    int  (*open)(input_module_t *);
+    void (*close)(input_module_t *);
+    void (*shutdown)(input_module_t *);
+};
+
+
+extern module_t modules[];
+
+void input_sleep (void);
+void input_free_buffer(input_buffer *);
+input_buffer *input_alloc_buffer (input_module_t *);
+
+void uninterleave_pcm_le (signed char  *src, unsigned channels, unsigned samples, float **dest);
+void uninterleave_pcm_be (signed char  *src, unsigned channels, unsigned samples, float **dest);
+
+void input_adv_sleep (unsigned long adv);  /* advance time in uS */
+#if 0
+void calculate_pcm_sleep(unsigned len, unsigned rate);
+int calculate_ogg_sleep(ogg_page *page);
+#endif
+
+void send_for_processing (input_module_t *mod, input_buffer *ib);
+void *input_loop(void*);
+
#endif /* __INPUTMODULE_H__ */


Modified: icecast/branches/kh/ices/src/logging.h
===================================================================
--- icecast/trunk/ices/src/logging.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/logging.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -16,38 +16,33 @@

#include "log/log.h"

-#ifndef __GNUC__
-#define __FUNCTION__ ""
-#endif
+#define LOG_ERROR0(x) log_write(ices_config->log_id,1, MODULE, __func__,x)
+#define LOG_ERROR1(x,a) log_write(ices_config->log_id,1, MODULE, __func__,x, a)
+#define LOG_ERROR2(x,a,b) log_write(ices_config->log_id,1, MODULE, __func__,x, a,b)
+#define LOG_ERROR3(x,a,b,c) log_write(ices_config->log_id,1, MODULE, __func__,x, a,b,c)
+#define LOG_ERROR4(x,a,b,c,d) log_write(ices_config->log_id,1, MODULE, __func__,x, a,b,c,d)
+#define LOG_ERROR5(x,a,b,c,d,e) log_write(ices_config->log_id,1, MODULE, __func__,x, a,b,c,d,e)

+#define LOG_WARN0(x) log_write(ices_config->log_id,2, MODULE, __func__,x)
+#define LOG_WARN1(x,a) log_write(ices_config->log_id,2, MODULE, __func__,x, a)
+#define LOG_WARN2(x,a,b) log_write(ices_config->log_id,2, MODULE, __func__,x, a,b)
+#define LOG_WARN3(x,a,b,c) log_write(ices_config->log_id,2, MODULE, __func__,x, a,b,c)
+#define LOG_WARN4(x,a,b,c,d) log_write(ices_config->log_id,2, MODULE, __func__,x, a,b,c,d)

-#define LOG_ERROR0(x) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x)
-#define LOG_ERROR1(x,a) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x, a)
-#define LOG_ERROR2(x,a,b) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x, a,b)
-#define LOG_ERROR3(x,a,b,c) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x, a,b,c)
-#define LOG_ERROR4(x,a,b,c,d) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x, a,b,c,d)
-#define LOG_ERROR5(x,a,b,c,d,e) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x, a,b,c,d,e)
+#define LOG_INFO0(x) log_write(ices_config->log_id,3, MODULE, __func__,x)
+#define LOG_INFO1(x,a) log_write(ices_config->log_id,3, MODULE, __func__,x, a)
+#define LOG_INFO2(x,a,b) log_write(ices_config->log_id,3, MODULE, __func__,x, a,b)
+#define LOG_INFO3(x,a,b,c) log_write(ices_config->log_id,3, MODULE, __func__,x, a,b,c)
+#define LOG_INFO4(x,a,b,c,d) log_write(ices_config->log_id,3, MODULE, __func__,x, a,b,c,d)
+#define LOG_INFO5(x,a,b,c,d,e) log_write(ices_config->log_id,3, MODULE, __func__,x, a,b,c,d,e)

-#define LOG_WARN0(x) log_write(ices_config->log_id,2, MODULE, __FUNCTION__,x)
-#define LOG_WARN1(x,a) log_write(ices_config->log_id,2, MODULE, __FUNCTION__,x, a)
-#define LOG_WARN2(x,a,b) log_write(ices_config->log_id,2, MODULE, __FUNCTION__,x, a,b)
-#define LOG_WARN3(x,a,b,c) log_write(ices_config->log_id,2, MODULE, __FUNCTION__,x, a,b,c)
-#define LOG_WARN4(x,a,b,c,d) log_write(ices_config->log_id,2, MODULE, __FUNCTION__,x, a,b,c,d)
+#define LOG_DEBUG0(x) log_write(ices_config->log_id,4, MODULE, __func__,x)
+#define LOG_DEBUG1(x,a) log_write(ices_config->log_id,4, MODULE, __func__,x, a)
+#define LOG_DEBUG2(x,a,b) log_write(ices_config->log_id,4, MODULE, __func__,x, a,b)
+#define LOG_DEBUG3(x,a,b,c) log_write(ices_config->log_id,4, MODULE, __func__,x, a,b,c)
+#define LOG_DEBUG4(x,a,b,c,d) log_write(ices_config->log_id,4, MODULE, __func__,x, a,b,c,d)
+#define LOG_DEBUG5(x,a,b,c,d,e) log_write(ices_config->log_id,4, MODULE, __func__,x, a,b,c,d,e)

-#define LOG_INFO0(x) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x)
-#define LOG_INFO1(x,a) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x, a)
-#define LOG_INFO2(x,a,b) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x, a,b)
-#define LOG_INFO3(x,a,b,c) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x, a,b,c)
-#define LOG_INFO4(x,a,b,c,d) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x, a,b,c,d)
-#define LOG_INFO5(x,a,b,c,d,e) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x, a,b,c,d,e)

-#define LOG_DEBUG0(x) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x)
-#define LOG_DEBUG1(x,a) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x, a)
-#define LOG_DEBUG2(x,a,b) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x, a,b)
-#define LOG_DEBUG3(x,a,b,c) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x, a,b,c)
-#define LOG_DEBUG4(x,a,b,c,d) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x, a,b,c,d)
-#define LOG_DEBUG5(x,a,b,c,d,e) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x, a,b,c,d,e)
-
-
#endif /* __LOGGING_H */


Modified: icecast/branches/kh/ices/src/metadata.c
===================================================================
--- icecast/trunk/ices/src/metadata.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/metadata.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* metadata.c
* - Metadata manipulation
*
- * $Id: metadata.c,v 1.13 2004/02/24 15:39:14 karl Exp $
+ * $Id: metadata.c,v 1.6 2002/07/20 12:52:06 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -12,7 +12,7 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif

#include <stdio.h>
@@ -23,153 +23,98 @@

#include "cfgparse.h"
#include "inputmodule.h"
-#include "event.h"
-#include "thread/thread.h"

#define MODULE "metadata/"
#include "logging.h"

volatile int metadata_update_signalled = 0;

-void *metadata_thread_stdin(void *arg)
+
+void metadata_thread_signal(input_module_t *mod, input_buffer *buffer)
{
-    char buf[1024];
-    input_module_t *mod = arg;
+	static char line[1024];
+    char **md = NULL;
+    int comments = 0;
+    FILE *file;

-    if (ices_config->background)
+    metadata_update_signalled = 0;
+
+    if (mod->metadata_filename == NULL)
+        return;
+    file = fopen(mod->metadata_filename, "r");
+    if (file == NULL)
{
-        LOG_INFO0("Metadata thread shutting down, tried to use "
-                "stdin from background");
-        return NULL;
+#ifdef STRERROR_R_CHAR_P
+        char *buf = strerror_r (errno, line, sizeof (line));
+#else
+        char buf[256];
+        int i = strerror_r (errno, line, sizeof (line));
+#endif
+        LOG_WARN2("Failed to open file \"%s\" for metadata update: %s",
+                mod->metadata_filename, buf);
+        return;
}
-    while(1)
-    {
-        char **md = NULL;
-        int comments = 0;
-        int wait_for_data = 1;

-        /* wait for data */
-        while (wait_for_data)
+    while(fgets(line, 1024, file))
+    {
+        if(line[0] == '\n')
+            break;
+        else
{
-            struct timeval t;
-            fd_set fds;
-            FD_ZERO (&fds);
-            FD_SET (0, &fds);
-            t.tv_sec = 0;
-            t.tv_usec = 150;
-
-            switch (select (1, &fds, NULL, NULL, &t))
-            {
-            case 1:
-                wait_for_data = 0;
-            case 0:
-                break;
-            default:
-                if (errno != EAGAIN)
-                {
-                    LOG_INFO1 ("shutting down thread (%d)", errno);
-                    return NULL; /* problem get out quick */
-                }
-                break;
-            }
+            char **old_buf;
+            unsigned len = strlen(line);

-            if (ices_config->shutdown)
+            if(line[len-1] == '\n')
+                line[len-1] = '\0';
+            old_buf = md;
+            md = realloc(md, (comments+2)*sizeof(char *));
+            if (md)
{
-                LOG_INFO0 ("metadata thread shutting down");
-                return NULL;
-            }
-        }
-        while(fgets(buf, 1024, stdin))
-        {
-            if(buf[0] == '\n')
-                break;
-            else
-            {
-                if(buf[strlen(buf)-1] == '\n')
-                    buf[strlen(buf)-1] = 0;
-                md = realloc(md, (comments+2)*sizeof(char *));
-                md[comments] = malloc(strlen(buf)+1);
-
-                memcpy(md[comments], buf, strlen(buf)+1);
+                md[comments] = malloc(len+1);
+                strcpy(md[comments], line);
comments++;
}
+            else
+                md = old_buf;
}
+    }

-        if(md) /* Don't update if there's nothing there */
-        {
-            md[comments]=0;
+    fclose(file);

-            /* Now, let's actually use the new data */
-            LOG_INFO0("Updating metadata");
-            mod->handle_event(mod,EVENT_METADATAUPDATE,md);
-        }
+    if(md) /* Don't update if there's nothing there */
+    {
+        md[comments]=NULL;

+        /* Now, let's actually use the new data */
+        LOG_INFO1("Updating metadata with %d comments", comments);
+        buffer->metadata = md;
}
+
}

-void *metadata_thread_signal(void *arg)
-{
-    char buf[1024];
-    input_module_t *mod = arg;

-    while(1)
+void metadata_update(char **md, vorbis_comment *vc)
+{
+    if(md)
{
-        char **md = NULL;
-        int comments = 0;
-        FILE *file;
-
-        while(metadata_update_signalled == 0)
+        while(*md)
{
-            thread_cond_wait(&ices_config->event_pending_cond);
-            if (ices_config->shutdown)
-            {
-                LOG_INFO0 ("metadata thread shutting down");
-                return NULL;
-            }
-            LOG_DEBUG0("meta thread wakeup");
+            LOG_INFO1 ("Adding comment %s", *md);
+            vorbis_comment_add(vc, *md++);
}
+    }
+}

-        metadata_update_signalled = 0;
-
-        file = fopen(ices_config->metadata_filename, "r");
-        if(!file) {
-            LOG_WARN2("Failed to open file \"%s\" for metadata update: %s",
-                    ices_config->metadata_filename, strerror(errno));
-            continue;
-        }
-
-        LOG_DEBUG1("reading metadata from \"%s\"", ices_config->metadata_filename);
-        while(fgets(buf, 1024, file))
+void metadata_free (char **md)
+{
+    if(md)
+    {
+        char **comment = md;
+        while(*comment)
{
-            if(buf[0] == '\n')
-                break;
-            else
-            {
-                if(buf[strlen(buf)-1] == '\n')
-                    buf[strlen(buf)-1] = 0;
-                md = realloc(md, (comments+2)*sizeof(char *));
-                md[comments] = malloc(strlen(buf)+1);
-
-                memcpy(md[comments], buf, strlen(buf)+1);
-                comments++;
-                LOG_INFO2 ("tag %d is %s", comments, buf);
-            }
+            free(*comment);
+            comment++;
}
-
-        fclose(file);
-
-        if(md) /* Don't update if there's nothing there */
-        {
-            md[comments]=0;
-
-            /* Now, let's actually use the new data */
-            LOG_INFO0("Updating metadata");
-            mod->handle_event(mod,EVENT_METADATAUPDATE,md);
-        }
-        else
-            LOG_INFO0("No metadata has been read");
-
+        free(md);
}
}
-
-

Modified: icecast/branches/kh/ices/src/metadata.h
===================================================================
--- icecast/trunk/ices/src/metadata.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/metadata.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -14,8 +14,14 @@
#ifndef __METADATA_H__
#define __METADATA_H__

-void *metadata_thread_stdin(void *arg);
-void *metadata_thread_signal(void *arg);
+#include "inputmodule.h"

+extern int metadata_update_signalled;
+
+void *metadata_thread_signal(void *arg, input_buffer *buffer);
+void metadata_update(char **md, vorbis_comment *vc);
+void metadata_free (char **md);
+
+
#endif /* __METADATA_H__ */


Added: icecast/branches/kh/ices/src/om_file.c
===================================================================
--- icecast/trunk/ices/src/om_file.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/om_file.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,455 @@
+/* om_file.c
+ *
+ * output module for writing stream to file
+ *
+ * Copyright (c) 2003 Karl Heyes <karl at xiph.org>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>
+
+#include <ogg/ogg.h>
+
+#include <cfgparse.h>
+#define MODULE "om_file/"
+#include <logging.h>
+#include <om_file.h>
+
+static int _write_to_file (int savefile, ogg_page *og)
+{
+    struct iovec iov[2];
+    iov[0].iov_base = (void*)og->header;
+    iov[0].iov_len = og->header_len;
+    iov[1].iov_base = (void*)og->body;
+    iov[1].iov_len = og->body_len;
+    if (ogg_page_granulepos(og) == -1)
+        LOG_DEBUG1 ("granulepos is negative on page %ld", ogg_page_pageno(og));
+    if (writev (savefile, iov, 2) == -1)
+    {
+        LOG_ERROR1 ("Failed to write to save file %s", strerror (errno));
+        return -1;
+    };
+    return 0;
+}
+
+
+static void _store_last_packet (struct output_file_stream *file, ogg_packet *packet)
+{
+    /* copy the last packet on the file, so that it can be used as the first packet*/
+    /* of the next, vorbis packets are interleaved so this prevent s data loss */
+    ogg_packet *store_op = &file->last_packet;
+
+    if (store_op->packet)
+    {
+        free (store_op->packet);
+        store_op->packet = NULL;
+    }
+    memcpy (store_op, packet,  sizeof (file->last_packet));
+    if ((store_op->packet = malloc (store_op->bytes)))
+    {
+        memcpy (store_op->packet, packet->packet, packet->bytes);
+    }
+    if (store_op->granulepos < 0)
+        store_op->granulepos = 0;
+
+    file->last_packet_exists = 1;
+}
+
+
+static void _flush_ogg_file (struct output_module *mod, ogg_packet *op)
+{
+    /* struct output_stream_state *out = &state->file; */
+    struct output_file_stream *out = mod->specific;
+    ogg_page page;
+    int write_it = 1;
+
+    long eos = op->e_o_s;
+
+    op->e_o_s = 1;
+#ifdef DEBUG
+    LOG_DEBUG0("marked packet for EOS");
+#endif
+    ogg_stream_packetin (&mod->os, op);
+
+    while (ogg_stream_flush (&mod->os, &page) > 0)
+    {
+#ifdef DEBUG
+        LOG_DEBUG1("flushing page (eos %d)", (ogg_page_eos (&page)?1:0));
+#endif
+        if (write_it && _write_to_file (out->fd, &page) < 0)
+            write_it = 0;
+    }
+    ogg_stream_clear (&mod->os);
+    /* reset to what it was */
+    op->e_o_s = eos;
+}
+
+
+static int _output_file_create(struct output_module *mod, char *filename)
+{
+    char *start = filename;
+    struct output_file_stream *stream = mod->specific;
+
+    if (filename == NULL || filename[0] == '\0')
+        return -1;
+    if (filename[0] == '/')
+        start++;
+
+    while (1)
+    {
+        char *ptr;
+        struct stat st;
+
+        ptr = strchr (start, '/');
+        if (ptr == NULL)
+            break;
+        *ptr = '\0';
+
+#ifdef DEBUG
+        LOG_DEBUG2 ("checking path %s (%s)", filename, start);
+#endif
+        if (stat (filename, &st) < 0)
+        {
+            char cwd[PATH_MAX];
+            char *tmp_ptr;
+            int ret = 0;
+
+            if (errno != ENOENT)
+            {
+                LOG_ERROR2 ("unable to stat path %s (%s)", start, strerror (errno));
+                return -1;
+            }
+            if (getcwd (cwd, sizeof (cwd)) == NULL)
+            {
+                LOG_ERROR0 ("failed to get cwd");
+                return -1;
+            }
+            /* ok now create the directory, but we need to strip off the
+             * new part first */
+            if ((tmp_ptr = strrchr (filename, '/')))
+                *tmp_ptr = '\0';
+            if (tmp_ptr && filename[0] && chdir (filename) < 0)
+            {
+                LOG_ERROR1 ("chdir failed on %s", filename);
+                if (tmp_ptr)
+                    *tmp_ptr = '/';
+                ret = -1;
+            }
+            else
+            {
+                if (mkdir (start, stream->dmask))
+                {
+                    LOG_ERROR2("Failed to mkdir %s in %s", start, filename);
+                    if (tmp_ptr)
+                        *tmp_ptr = '/';
+                    ret = -1;
+                }
+                else
+                {
+                    if (tmp_ptr)
+                        *tmp_ptr = '/';
+                    LOG_INFO1 ("created directory %s", filename);
+                }
+            }
+            if (chdir (cwd) < 0)
+            {
+                LOG_ERROR0 ("failed to reset cwd");
+                ret = -1;
+            }
+            if (ret == -1)
+                return -1;
+        }
+        start = ptr+1;
+        *ptr = '/';
+    }
+    stream->fd = open (filename, O_CREAT|O_EXCL|O_WRONLY, stream->fmask);
+    if (stream->fd == -1)
+    {
+        LOG_WARN2 ("Failed to open file %s (%s)", filename, strerror (errno));
+        return -1;
+    }
+    LOG_INFO1 ("Saving to file %s", filename);
+    return 0;
+}
+
+
+static int file_audio_pageout (struct output_module *mod, ogg_page *page)
+{
+    int ret;
+
+    if (mod->initial_packets)
+    {
+        mod->initial_packets--;
+        if (mod->initial_packets == 0)
+            ret = ogg_stream_flush (&mod->os, page);
+        else
+            ret = 0;
+    }
+    else
+        ret = ogg_stream_pageout (&mod->os, page);
+
+    return ret;
+}
+
+
+/* return non-zero is packet flushed to file */
+static int _output_file_timeout (struct output_module *mod, time_t time_val, ogg_packet *op)
+{
+    int ret = 0, close_it = 0;
+    long packetno = op->packetno;
+    struct output_state *state = mod->parent;
+    struct output_file_stream *stream = mod->specific;
+
+    if (stream->fd < 0)
+        return 0;
+    if (stream->close_on_new_headers && state->new_headers)
+    {
+        close_it = 1;
+    }
+    else
+    {
+        if (stream->saveduration)
+        {
+            if (stream->save_start + (time_t)stream->saveduration <= time_val)
+            {
+                ogg_int64_t  granule = op->granulepos;
+                op->packetno = mod->packetno++;
+                op->granulepos -= mod->start_pos;
+                _flush_ogg_file (mod, op);
+                op->granulepos = granule;
+                close_it = 1;
+                LOG_DEBUG0("file duration expired");
+            }
+        }
+    }
+    if (close_it)
+    {
+        LOG_DEBUG0 ("Closing save file");
+        close (stream->fd);
+        stream->fd = -1;
+        _store_last_packet (stream, op);
+        mod->packetno = 3;
+        op->packetno = packetno;
+        ret = 1;
+    }
+    return ret;
+}
+
+
+int output_ogg_file (struct output_module *mod, ogg_packet *op, unsigned samples)
+{
+    int output_needs_headers = 0;
+    struct output_state *state = mod->parent;
+    struct output_file_stream *file = mod->specific;
+    ogg_page page;
+    long packetno = op->packetno;
+    ogg_int64_t granule = op->granulepos;
+    time_t time_val;
+
+    /* does the file need closing */
+    time_val = time(NULL);   /* called far too much */
+    if (_output_file_timeout (mod, time_val, op))
+    {
+        return 0;
+    }
+#ifdef DEBUG
+    if (op->e_o_s)
+        LOG_DEBUG0("packet seen with eos set");
+#endif
+    /* if no file open then open it */
+    if (file->fd == -1)
+    {
+        struct tm tm;
+        char filename [PATH_MAX];
+        int ret;
+
+        localtime_r (&time_val, &tm);
+        if (file->savefilename)
+        {
+            ret = strftime (filename, sizeof (filename), file->savefilename, &tm);
+            for (; ret && filename [ret-1]=='\\'; ret--)
+                filename [ret-1] = '\0';
+            if (ret == 0)
+            {
+                LOG_WARN1 ("Unable to generate a filename (%d)", ret);
+                return -1;
+            }
+            LOG_DEBUG2 ("filename expansion %s -> %s", file->savefilename, filename);
+        }
+        else
+            snprintf (filename, sizeof (filename), "save-%lu.ogg", file->count++);
+        if (_output_file_create (mod, filename) < 0)
+            return -1;
+
+        file->save_start = time_val;
+        output_needs_headers = 1;
+    }
+    if (state->new_headers || output_needs_headers)
+    {
+        ogg_page page;
+        long packetno;
+        int i;
+
+        if (mod->in_use)
+        {
+            LOG_DEBUG0 ("clearing ogg file stream");
+            ogg_stream_clear (&mod->os);
+        }
+
+        LOG_DEBUG0 ("initialising output stream");
+        if (mod->reset)
+            mod->start_pos = 0;
+        mod->initial_packets = 2;
+        mod->start_pos = 0;
+        mod->in_use = 1;
+        ogg_stream_init (&mod->os, ++mod->serial);
+        mod->granule = (uint64_t)0;
+        mod->packetno = 0; /* need to verify is start from 0 or 3 */
+        for (i=0; i< 3 ; i++)
+        {
+            packetno = state->packets[i] . packetno;
+            state->packets[i] . packetno = mod->packetno++;;
+            /* LOG_DEBUG1 ("Added header %lld", state->packets[i] . packetno); */
+            ogg_stream_packetin (&mod->os, &state->packets[i]);
+        }
+
+        while (ogg_stream_flush (&mod->os, &page) > 0)
+        {
+            /* LOG_DEBUG2 ("header: granulepos is %lld on page %ld\n", ogg_page_granulepos (&page), ogg_page_pageno(&page)); */
+            if (file->fd > -1 && _write_to_file (file->fd, &page) < 0)
+            {
+                close (file->fd);
+                file->fd = -1;
+            }
+        }
+        output_needs_headers = 0;
+
+        if (file->last_packet_exists)
+        {
+            file->last_packet.e_o_s = 0;
+            if (mod->reset)
+                mod->start_pos = file->last_packet.granulepos;
+            file->last_packet.granulepos = 0;
+            file->last_packet.packetno = mod->packetno++;
+            /* LOG_DEBUG2 ("inital packet: granulepos is %lld on packet %ld", mod->last_packet.granulepos, mod->last_packet.packetno); */
+            ogg_stream_packetin (&mod->os, &file->last_packet);
+        }
+        if (op->e_o_s)
+            mod->initial_packets = 1;
+    }
+
+    op -> packetno = mod->packetno++;
+    granule = op->granulepos;
+    op->granulepos -= mod->start_pos;
+    /* LOG_DEBUG2 ("main %lld granulepos is %lld", op->packetno, op->granulepos); */
+    ogg_stream_packetin (&mod->os, op);
+
+    while (file_audio_pageout (mod, &page) > 0)
+    {
+        if (file->fd > -1 && _write_to_file (file->fd, &page) < 0)
+        {
+            close (file->fd);
+            file->fd = -1;
+        }
+    }
+    /* reset packet to what it was */
+    op->packetno = packetno;
+    op->granulepos = granule;
+    return 0;
+}
+
+
+static void output_filestream_clear (struct output_module *mod)
+{
+    if (mod)
+    {
+        struct output_file_stream *stream = mod->specific;
+        LOG_DEBUG0("clearing structure");
+        if (stream)
+        {
+            ogg_stream_clear (&mod->os);
+            if (stream->fd != -1)
+                close (stream->fd);
+            if (stream->savefilename)
+                xmlFree (stream->savefilename);
+            if (stream->last_packet.packet)
+                free (stream->last_packet.packet);
+            free (stream);
+            mod->specific = NULL;
+        }
+    }
+}
+
+
+
+int parse_savefile (xmlNodePtr node, void *arg)
+{
+    struct output_state *state = arg;
+    char *filename = NULL;
+    int duration = 60*60;   /* default to 1 hour */
+    int single = 0;         /* enable to have a single logical stream per file */
+    mode_t fmask = 0600;       /* file umask */
+    mode_t dmask = 0700;       /* directory umask */
+    int reset = 1;
+    struct output_module *mod;
+
+    mod = calloc (1, sizeof (struct output_module));
+    while (mod)
+    {
+        struct cfg_tag  savefile_tags[] =
+        {
+            { "filename",       get_xml_string, &filename },
+            { "duration",       get_xml_int,    &duration },
+            { "on-metadata",    get_xml_bool,   &single },
+            { "fmask",          get_xml_int,    &fmask },
+            { "dmask",          get_xml_int,    &dmask },
+            { "reset-time",     get_xml_bool,   &reset },
+            { NULL, NULL, NULL }
+        };
+        struct output_file_stream *stream;
+
+        if (parse_xml_tags ("savefile", node->xmlChildrenNode, savefile_tags))
+            break;
+
+        mod->parent = state;
+        mod->output_send = output_ogg_file;
+        mod->output_clear = output_filestream_clear;
+        stream = calloc (1, sizeof (struct output_file_stream));
+        if (stream == NULL)
+            break;
+
+        mod->reset = reset;
+        mod->specific = stream;
+        stream->fmask = fmask;
+        stream->dmask = dmask;
+        stream->close_on_new_headers = single;
+        stream->savefilename = filename;
+        stream->saveduration = duration;
+        stream->fd = -1;
+
+        mod->next = state->head;
+        state->head = mod;
+#ifdef DEBUG
+        printf("Added file output stream\n");
+#endif
+        return 0;
+    }
+    if (mod) free (mod);
+    if (filename) xmlFree (filename);
+
+    return -1;
+}
+

Added: icecast/branches/kh/ices/src/om_file.h
===================================================================
--- icecast/trunk/ices/src/om_file.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/om_file.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,21 @@
+#ifndef __OM_FILE_H
+#define __OM_FILE_H
+
+struct output_file_stream
+{
+    int fd;
+    char *savefilename;
+    mode_t fmask;
+    mode_t dmask;
+    unsigned long count;
+    unsigned saveduration;
+    time_t save_start;
+    /* int reset; */
+    int close_on_new_headers;
+    int last_packet_exists;
+    ogg_packet last_packet;
+};
+
+int parse_savefile (xmlNodePtr node, void *arg);
+
+#endif  /* __OM_FILE_H */

Added: icecast/branches/kh/ices/src/om_shout.c
===================================================================
--- icecast/trunk/ices/src/om_shout.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/om_shout.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,354 @@
+/* om_shout.c
+ *
+ * - Output module for sending to shout streams
+ *
+ * Copyright (c) 2002 Karl Heyes <karl at xiph.org>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source. */
+
+#include <config.h>
+#include <string.h>
+#include <ogg/ogg.h>
+
+#include <cfgparse.h>
+
+#include <om_shout.h>
+
+#define MODULE "om_shout/"
+#include <logging.h>
+
+
+#define SHOUT_TIMEOUT 10
+
+#define DEFAULT_HOSTNAME "localhost"
+#define DEFAULT_PORT 8000
+#define DEFAULT_PASSWORD "password"
+#define DEFAULT_USERNAME "source"
+#define DEFAULT_MOUNT "/stream.ogg"
+#define DEFAULT_RECONN_DELAY 30
+#define DEFAULT_RECONN_ATTEMPTS -1
+
+
+static void _output_connection_close (struct output_module *mod)
+{
+    struct output_shout_state *stream = mod->specific;
+    LOG_DEBUG0("closed shout connection");
+    shout_close (stream->shout);
+    shout_free (stream->shout);
+    stream->shout = NULL;
+    if (stream->connected)
+    {
+        if (mod->in_use)
+            ogg_stream_clear (&mod->os);
+
+        stream->connected = 0;
+    }
+    stream->restart_time = time(NULL) + stream->reconnect_delay;
+    stream->reconnect_count++;
+}
+
+
+void check_shout_connected (struct output_module *mod)
+{
+    struct output_shout_state *stream = mod->specific;
+    if (shout_connection_ready (stream->shout))
+        return;
+    if (stream->reconnect_attempts == -1 ||
+            stream->reconnect_attempts > stream->reconnect_count)
+    {
+        time_t now = time (NULL);
+
+        if (stream->restart_time <= now)
+        {
+            int shouterr;
+
+            if (stream->shout == NULL || shout_get_errno (stream->shout) != SHOUTERR_PENDING)
+            {
+                char audio_info[11];
+                int failed = 1;
+                struct output_state *state = mod->parent;
+
+                LOG_DEBUG3 ("Time we started stream on %s:%d%s", stream->hostname,
+                        stream->port, stream->mount);
+                /* allow for traping long icecast connects */
+                stream->restart_time = now;
+
+                do
+                {
+                    if ((stream->shout = shout_new ()) == NULL)
+                        break;
+                    if (shout_set_host (stream->shout, stream->hostname) != SHOUTERR_SUCCESS)
+                        break;
+                    if (shout_set_password (stream->shout, stream->password) != SHOUTERR_SUCCESS)
+                        break;
+                    if (shout_set_port (stream->shout, stream->port) != SHOUTERR_SUCCESS)
+                        break;
+                    if (stream->user && shout_set_user (stream->shout, stream->user) != SHOUTERR_SUCCESS)
+                        break;
+                    if (shout_set_mount (stream->shout, stream->mount) != SHOUTERR_SUCCESS)
+                        break;
+                    if (shout_set_agent (stream->shout, PACKAGE_STRING) != SHOUTERR_SUCCESS)
+                        break;
+                    if (shout_set_name (stream->shout, state->name) != SHOUTERR_SUCCESS)
+                        break;
+                    if (shout_set_genre (stream->shout, state->genre) != SHOUTERR_SUCCESS)
+                        break;
+                    if (state->url && shout_set_url (stream->shout, state->url) != SHOUTERR_SUCCESS)
+                        break;
+                    if (shout_set_description (stream->shout, state->description) != SHOUTERR_SUCCESS)
+                        break;
+                    if (stream->yp && shout_set_public (stream->shout, 1) != SHOUTERR_SUCCESS)
+                        break;
+
+                    shout_set_nonblocking (stream->shout, 1);
+                    shout_set_format (stream->shout, SHOUT_FORMAT_VORBISPAGE);
+                    shout_set_protocol (stream->shout, SHOUT_PROTOCOL_HTTP);
+
+                    snprintf(audio_info, sizeof(audio_info), "%ld", state->vi.bitrate_nominal/1000);
+                    shout_set_audio_info (stream->shout, SHOUT_AI_BITRATE, audio_info);
+
+                    snprintf(audio_info, sizeof(audio_info), "%ld", state->vi.rate);
+                    shout_set_audio_info (stream->shout, SHOUT_AI_SAMPLERATE, audio_info);
+
+                    snprintf(audio_info, sizeof(audio_info), "%d", state->vi.channels);
+                    shout_set_audio_info (stream->shout, SHOUT_AI_CHANNELS, audio_info);
+                    failed = 0;
+                }
+                while (0);
+                if (failed)
+                {
+                    _output_connection_close (mod);
+                    return;
+                }
+            }
+            if ((shouterr = shout_open(stream->shout)) == SHOUTERR_SUCCESS)
+            {
+                LOG_INFO3("Connected to server: %s:%d%s",
+                        shout_get_host(stream->shout), shout_get_port(stream->shout), shout_get_mount(stream->shout));
+                stream->connected = 1;
+                stream->reconnect_count = 0;
+                mod->need_headers = 1;
+                return;
+            }
+            if (shouterr == SHOUTERR_PENDING)
+            {
+                if (now - stream->restart_time > SHOUT_TIMEOUT)
+                {
+                    LOG_ERROR3("Terminating connection to %s:%d%s",
+                            shout_get_host(stream->shout), shout_get_port(stream->shout),
+                            shout_get_mount(stream->shout));
+                    LOG_ERROR1("no reply came in %d seconds", SHOUT_TIMEOUT);
+                    _output_connection_close (mod);
+                }
+                /* ok, we just need to come back to this connection later */
+            }
+            else
+            {
+                LOG_ERROR4("Failed to connect to %s:%d%s (%s)",
+                        shout_get_host(stream->shout), shout_get_port(stream->shout),
+                        shout_get_mount(stream->shout), shout_get_error(stream->shout));
+                _output_connection_close (mod);
+            }
+        }
+        return;
+    }
+    LOG_INFO1 ("%d reconnect attempts, will keep re-trying", stream->reconnect_count);
+}
+
+
+static int shout_audio_pageout (struct output_module *mod, ogg_page *page)
+{
+    struct output_shout_state *stream = mod->specific;
+    int ret;
+    uint64_t samples;
+
+    if (mod->initial_packets)
+    {
+        mod->initial_packets--;
+        if (mod->initial_packets == 0)
+            ret = ogg_stream_flush (&mod->os, page);
+        else
+            ret = 0;
+    }
+    else if (stream->page_samples > (uint64_t)mod->parent->vi.rate)
+        ret = ogg_stream_flush (&mod->os, page);
+    else
+        ret = ogg_stream_pageout (&mod->os, page);
+
+    if (ret > 0)
+    {
+        samples = ogg_page_granulepos (page) - stream->prev_page_granulepos;
+        stream->page_samples -= samples;
+        stream->prev_page_granulepos = ogg_page_granulepos (page);
+    }
+
+    return ret;
+}
+
+
+int output_ogg_shout (struct output_module *mod, ogg_packet *op, unsigned samples)
+{
+    struct output_shout_state *stream = mod->specific;
+    check_shout_connected (mod);
+    if (stream->connected)
+    {
+        int send_it = 1;
+        ogg_page page;
+        long packetno = op->packetno;
+        ogg_int64_t granule = op->granulepos;
+        struct output_state *state = mod->parent;
+
+        op -> packetno = mod->packetno++;
+
+        if (state->new_headers || mod->need_headers)
+        {
+            int val = mod->serial;
+            /* start of new logical stream */
+            LOG_DEBUG0 ("initialising output stream");
+            if (mod->in_use)
+            {
+                ogg_stream_clear (&mod->os);
+            }
+            while (val == mod->serial)
+                val = rand();
+            mod->serial = val;
+            ogg_stream_init (&mod->os, val);
+            mod->in_use = 1;
+            ogg_stream_packetin (&mod->os, &state->packets[0]);
+            ogg_stream_packetin (&mod->os, &state->packets[1]);
+            ogg_stream_packetin (&mod->os, &state->packets[2]);
+            mod->need_headers = 0;
+            mod->start_pos = granule;
+            stream->prev_page_granulepos = 0;
+            stream->prev_packet_granulepos = 0;
+
+            while (ogg_stream_flush (&mod->os, &page) > 0)
+            {
+                if (send_it && shout_send (stream->shout, &page, 1) != SHOUTERR_SUCCESS)
+                {
+                    send_it = 0;
+                    LOG_ERROR4("Failed to write headers to %s:%d%s (%s)",
+                            shout_get_host (stream->shout), shout_get_port (stream->shout),
+                            shout_get_mount (stream->shout), shout_get_error (stream->shout));
+                    _output_connection_close (mod);
+                    return 0;
+                }
+            }
+            mod->packetno = 3;
+            mod->initial_packets = 2; /* flush 2 packets into a single page */
+        }
+
+        op->granulepos -= mod->start_pos;
+        ogg_stream_packetin (&mod->os, op);
+
+        stream->page_samples += (op->granulepos - stream->prev_packet_granulepos);
+        stream->prev_packet_granulepos = op->granulepos;
+
+        while (shout_audio_pageout (mod, &page) > 0)
+        {
+            if (send_it && shout_send (stream->shout, &page, 1) != SHOUTERR_SUCCESS)
+            {
+                send_it = 0;
+                LOG_ERROR4("Failed to write to %s:%d%s (%s)",
+                        shout_get_host (stream->shout), shout_get_port (stream->shout),
+                        shout_get_mount (stream->shout), shout_get_error (stream->shout));
+                _output_connection_close (mod);
+                return 0;
+            }
+        }
+        /* reset to what it was */
+        op->packetno = packetno;
+        op->granulepos = granule;
+    }
+    return 0;
+}
+
+
+
+static void output_shout_clear (struct output_module *mod)
+{
+    if (mod)
+    {
+        struct output_shout_state *stream = mod->specific;
+        _output_connection_close (mod);
+        shout_free (stream->shout);
+        xmlFree (stream->hostname);
+        xmlFree (stream->user);
+        xmlFree (stream->password);
+        xmlFree (stream->mount);
+        free (stream);
+    }
+}
+
+
+
+int parse_shout (xmlNodePtr node, void *arg)
+{
+    struct output_state *state = arg;
+    char *hostname   = NULL,
+         *user       = xmlStrdup (DEFAULT_USERNAME),
+         *password   = xmlStrdup (DEFAULT_PASSWORD),
+         *mount      = xmlStrdup (DEFAULT_MOUNT);
+    int  yp          = 0,
+         reconnect_delay = DEFAULT_RECONN_DELAY,
+         reconnect_attempts = DEFAULT_RECONN_ATTEMPTS,
+         port        = 8000;
+
+    struct cfg_tag shout_tags[] =
+    {
+        { "hostname",           get_xml_string, &hostname },
+        { "port",               get_xml_int,    &port },
+        { "password",           get_xml_string, &password },
+        { "username",           get_xml_string, &user },
+        { "mount",              get_xml_string, &mount },
+        { "yp",                 get_xml_bool,   &yp },
+        { "reconnectdelay",     get_xml_int,    &reconnect_delay },
+        { "reconnectattempts",  get_xml_int,    &reconnect_attempts },
+        { NULL, NULL, NULL }
+    };
+
+    if (parse_xml_tags ("shout", node->xmlChildrenNode, shout_tags))
+        return 1;
+
+    if (hostname == NULL)
+        return 0;
+
+    while (1)
+    {
+        struct output_shout_state *stream = NULL;
+        struct output_module *mod = calloc (1, sizeof (struct output_module));
+        if (mod == NULL)
+            break;
+        mod->parent = state;
+        mod->output_send = output_ogg_shout;
+        mod->output_clear = output_shout_clear;
+        stream = calloc (1, sizeof (struct output_shout_state));
+        if (stream == NULL)
+            break;
+        mod->specific = stream;
+        stream->hostname = hostname;
+        stream->user = user;
+        stream->password = password;
+        stream->mount = mount;
+        stream->port = port;
+        stream->yp = yp;
+
+        stream->reconnect_delay = reconnect_delay;
+        stream->reconnect_attempts = reconnect_attempts;
+
+        /* add populated struct to list of outputs */
+        mod->next = state->head;
+        state->head = mod;
+        return 0;
+    }
+    fprintf (stderr, "shout output failed\n");
+    if (hostname)   xmlFree (hostname);
+    if (user)       xmlFree (user);
+    if (password)   xmlFree (password);
+    if (mount)      xmlFree (mount);
+    return -1;
+}
+

Added: icecast/branches/kh/ices/src/om_shout.h
===================================================================
--- icecast/trunk/ices/src/om_shout.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/om_shout.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,41 @@
+/* om_shout.h
+ *
+ * output module header for rebuild stream fro sending though libshout
+ *
+ * Copyright (c) 2002 Karl Heyes <karl at pts.tele2.co.uk>
+ *
+ * his program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifndef __OM_SHOUT_H
+#define __OM_SHOUT_H
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+struct output_shout_state
+{
+    shout_t *shout;
+    char *hostname;
+    char *user;
+    char *password;
+    char *mount;
+    int port;
+    int yp;
+    int connected;
+    int reconnect_delay;
+    int reconnect_count;
+    int reconnect_attempts;
+    time_t restart_time;
+    uint64_t page_samples;
+    uint64_t prev_page_granulepos;
+    uint64_t prev_packet_granulepos;
+};
+
+int parse_shout (xmlNodePtr node, void *arg);
+
+
+#endif /* __OM_SHOUT_H */

Modified: icecast/branches/kh/ices/src/playlist_basic.c
===================================================================
--- icecast/trunk/ices/src/playlist_basic.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/playlist_basic.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,10 @@
/* playlist_basic.c
* - Simple built-in unscripted playlist
*
- * $Id: playlist_basic.c,v 1.13 2003/08/13 00:58:02 karl Exp $
+ * $Id: playlist_basic.c,v 1.7 2002/08/10 04:26:52 msmith Exp $
*
- * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2001-2 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2003 Karl Heyes <k.heyes at blueyonder.co.uk>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -12,7 +13,7 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif

#include <stdio.h>
@@ -33,14 +34,13 @@

static void shuffle(char **buf, int len)
{
-    /* From ices1 src/playlist_basic/rand.c */
int n,d;
char *temp;

n = len;
while(n > 1)
{
-        d = (int) ((double)len * rand()/(RAND_MAX+1.0));
+        d = (int) ((double)n * rand()/(RAND_MAX+1.0));
temp = buf[d];
buf[d] = buf[n-1];
buf[n-1] = temp;
@@ -55,7 +55,7 @@
char buf[1024];
int buflen;

-    file = fopen(data->file, "rb");
+    file = fopen(data->file, "r");

if (file == NULL)
{
@@ -93,8 +93,12 @@

if(buflen < data->len+1)
{
+            char **tmp;
buflen += 100;
-            data->pl = realloc(data->pl, buflen*sizeof(char *));
+            tmp = realloc(data->pl, buflen*sizeof(char *));
+            if (tmp == NULL)
+                break;
+            data->pl = tmp;
}

data->pl[data->len++] = strdup(buf);
@@ -103,10 +107,12 @@
if(data->random)
shuffle(data->pl, data->len);

+    fclose (file);
+
return 0;
}

-void playlist_basic_clear(void *data)
+static void playlist_basic_clear(void *data)
{
basic_playlist *pl = data;
if(pl)
@@ -116,24 +122,24 @@
int i;
for(i=0; i < pl->len; i++)
free(pl->pl[i]);
-            free(pl->pl);
+			free(pl->pl);
}
-        free(pl);
-    }
+		free(pl);
+	}
}

-char *playlist_basic_get_next_filename(void *data)
+static char *playlist_basic_get_next_filename(void *data)
{
-    basic_playlist *pl = (basic_playlist *)data;
+	basic_playlist *pl = (basic_playlist *)data;
char *ptr = NULL, *dest = NULL;
int reload_playlist = 0;
-    struct stat st;
+	struct stat st;

-    if (stat(pl->file, &st))
-    {
-        LOG_ERROR2("Couldn't stat file \"%s\": %s", pl->file, strerror(errno));
-        return NULL;
-    }
+	if (stat(pl->file, &st))
+	{
+		LOG_ERROR2("Couldn't stat file \"%s\": %s", pl->file, strerror(errno));
+		return NULL;
+	}

if (pl->pl)
{
@@ -157,17 +163,17 @@
return NULL;
if (pl->restartafterreread)
pl->pos = 0;
-    }
+	}

-    if (pl->pos >= pl->len)  /* reached the end of the potentially updated list */
-    {
-        if (pl->once)
+	if (pl->pos >= pl->len)  /* reached the end of the potentially updated list */
+	{
+        if (pl->once)
return NULL;

pl->pos = 0;
if (pl->random)
shuffle(pl->pl, pl->len);
-    }
+    }

ptr = pl->pl [pl->pos++];

@@ -177,53 +183,54 @@
return dest;
}

-void playlist_basic_free_filename(void *data, char *fn)
+static void playlist_basic_free_filename(void *data __attribute__((unused)), char *fn)
{
-   free (fn);
+    if (fn) free (fn);
}

-int playlist_basic_initialise(module_param_t *params, playlist_state_t *pl)
+int playlist_basic_initialise(module_param_t *params, struct playlist_state *pl)
{
-    basic_playlist *data;
+	basic_playlist *data;

-    pl->get_filename = playlist_basic_get_next_filename;
-    pl->clear = playlist_basic_clear;
+	pl->get_filename = playlist_basic_get_next_filename;
+	pl->clear = playlist_basic_clear;
pl->free_filename = playlist_basic_free_filename;
+    pl->sleep = 1;

-    pl->data = calloc(1, sizeof(basic_playlist));
-    data = (basic_playlist *)pl->data;
+	pl->data = calloc(1, sizeof(basic_playlist));
+	data = (basic_playlist *)pl->data;

-    while (params != NULL) {
-        if (!strcmp(params->name, "file"))
-        {
-            if (data->file) free(data->file);
-            data->file = params->value;
-        }
-        else if (!strcmp(params->name, "random"))
-            data->random = atoi(params->value);
-        else if(!strcmp(params->name, "once"))
-            data->once = atoi(params->value);
-        else if(!strcmp(params->name, "allow-repeats"))
-            pl->allow_repeat = atoi(params->value);
-        else if(!strcmp(params->name, "restart-after-reread"))
-            data->restartafterreread = atoi(params->value);
-        else if(!strcmp(params->name, "type"))
-            ; /* We recognise this, but don't want to do anything with it */
-        else
-        {
-            LOG_WARN1("Unknown parameter to playlist input module: %s",
-                    params->name);
-        }
-        params = params->next;
-    }
+	while (params != NULL) {
+		if (!strcmp(params->name, "file"))
+		{
+			if (data->file) free(data->file);
+			data->file = params->value;
+		}
+		else if (!strcmp(params->name, "random"))
+			data->random = atoi(params->value);
+		else if(!strcmp(params->name, "once"))
+			data->once = atoi(params->value);
+		else if(!strcmp(params->name, "restart-after-reread"))
+			data->restartafterreread = atoi(params->value);
+		else if(!strcmp(params->name, "sleep"))
+			pl->sleep = atoi(params->value);
+		else if(!strcmp(params->name, "type"))
+			; /* We recognise this, but don't want to do anything with it */
+		else
+		{
+			LOG_WARN1("Unknown parameter to playlist input module: %s",
+					params->name);
+		}
+		params = params->next;
+	}

-    if (!data->file)
-    {
-        LOG_ERROR0("No filename specified for playlist module");
-        free(data);
-        return -1;
-    }
+	if (!data->file)
+	{
+		LOG_ERROR0("No filename specified for playlist module");
+		free(data);
+		return -1;
+	}

-    return 0;
+	return 0;
}


Modified: icecast/branches/kh/ices/src/playlist_basic.h
===================================================================
--- icecast/trunk/ices/src/playlist_basic.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/playlist_basic.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* playlist_basic.h
* - Simple unscripted playlist
*
- * $Id: playlist_basic.h,v 1.5 2003/03/16 14:21:49 msmith Exp $
+ * $Id: playlist_basic.h,v 1.4 2002/06/29 15:19:18 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -16,21 +16,19 @@

typedef struct
{
-    char **pl;
-    int len;
-    int pos;
-    char *file; /* Playlist file */
-    time_t mtime;
-    int random;
-    int once;
+	char **pl;
+	int len;
+	int pos;
+	char *file; /* Playlist file */
+	time_t mtime;
+	int random;
+	int once;
int restartafterreread;

} basic_playlist;

-void playlist_basic_clear(void *data);
-char *playlist_basic_get_next_filename(void *data);
-int playlist_basic_initialise(module_param_t *params, playlist_state_t *pl);
-int playlist_script_initialise(module_param_t *params, playlist_state_t *pl);
+int playlist_basic_initialise (module_param_t *params, struct playlist_state *);
+int playlist_script_initialise (module_param_t *params, struct playlist_state *);


#endif  /* __PLAYLIST_BASIC_H__ */

Modified: icecast/branches/kh/ices/src/playlist_script.c
===================================================================
--- icecast/trunk/ices/src/playlist_script.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/playlist_script.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -2,7 +2,7 @@
* - Gets a filename to play back based on output from a program/shell script
*   run each time.
*
- * $Id: playlist_script.c,v 1.7 2003/08/13 00:58:02 karl Exp $
+ * $Id: playlist_script.c,v 1.4 2002/08/09 13:59:02 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -13,7 +13,7 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif

#include <stdio.h>
@@ -32,12 +32,12 @@
char *program;
} script_playlist;

-void playlist_script_clear(void *data) {
+static void playlist_script_clear(void *data) {
if(data)
free(data);
}

-char *playlist_script_get_filename(void *data) {
+static char *playlist_script_get_filename(void *data) {
script_playlist *pl = data;
char *prog = pl->program;
FILE *pipe;
@@ -82,18 +82,19 @@
return buf;
}

-void playlist_script_free_filename(void *data, char *fn)
+static void playlist_script_free_filename(void *data __attribute__((unused)), char *fn)
{
free(fn);
}

-int playlist_script_initialise(module_param_t *params, playlist_state_t *pl)
+int playlist_script_initialise(module_param_t *params, struct playlist_state *pl)
{
script_playlist *data;

pl->get_filename = playlist_script_get_filename;
pl->clear = playlist_script_clear;
pl->free_filename = playlist_script_free_filename;
+    pl->sleep = 1;

pl->data = calloc(1, sizeof(script_playlist));
if(!pl->data)
@@ -106,11 +107,11 @@
if(data->program) free(data->program);
data->program = params->value;
}
-        else if(!strcmp(params->name, "allow-repeats"))
-            pl->allow_repeat = atoi(params->value);
else if(!strcmp(params->name, "type")) {
/* We ignore this one */
}
+        else if(!strcmp(params->name, "sleep"))
+            pl->sleep = atoi(params->value);
else
LOG_WARN1("Unknown parameter to playlist script module: %s",
params->name);

Modified: icecast/branches/kh/ices/src/reencode.c
===================================================================
--- icecast/trunk/ices/src/reencode.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/reencode.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,10 @@
/* reencode.c
* - runtime reencoding of vorbis audio (usually to lower bitrates).
*
- * $Id: reencode.c,v 1.10 2003/12/22 14:01:09 karl Exp $
+ * $Id: reencode.c,v 1.6 2002/08/03 14:41:10 msmith Exp $
*
- * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2001   Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2002-4 Karl Heyes <karl at xiph.org>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -12,7 +13,7 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif

#include <stdio.h>
@@ -22,8 +23,8 @@
#include <ogg/ogg.h>
#include <vorbis/codec.h>

-#include "reencode.h"
#include "cfgparse.h"
+#include "reencode.h"
#include "stream.h"
#include "encode.h"
#include "audio.h"
@@ -31,220 +32,160 @@
#define MODULE "reencode/"
#include "logging.h"

-reencode_state *reencode_init(instance_t *stream)
+extern ogg_packet *copy_ogg_packet (ogg_packet *packet);
+
+struct reencode *reencode_new_init (struct encoder_settings *settings)
{
-    reencode_state *new = calloc(1, sizeof(reencode_state));
+    struct reencode *reenc = calloc (1, sizeof (struct reencode));
+    if (reenc)
+    {
+        reenc->encoder = encode_create();
+        if (reenc->encoder == NULL)
+        {
+            free (reenc);
+            return NULL;
+        }

-    new->out_min_br = stream->min_br;
-    new->out_nom_br = stream->nom_br;
-    new->out_max_br = stream->max_br;
-    new->quality = stream->quality;
-    new->managed = stream->managed;
+        vorbis_info_init (&reenc->vi);
+        vorbis_comment_init (&reenc->vc);
+        reenc->settings = settings;
+        reenc->need_headers = 3;
+        LOG_DEBUG0("Reencoder setup complete");
+    }
+    return reenc;
+}

-    new->out_samplerate = stream->samplerate;
-    new->out_channels = stream->channels;
-    new->current_serial = -1; /* FIXME: that's a valid serial */
-    new->need_headers = 0;

-    return new;
-}

-void reencode_clear(reencode_state *s)
+void reencode_free(struct reencode *s)
{
-    if(s)
+    if (s)
{
-        LOG_DEBUG0("Clearing reencoder");
-        ogg_stream_clear(&s->os);
-        vorbis_block_clear(&s->vb);
-        vorbis_dsp_clear(&s->vd);
-        vorbis_comment_clear(&s->vc);
-        vorbis_info_clear(&s->vi);
-
-        free(s);
+        if (s->need_headers == 0)
+        {
+            vorbis_block_clear (&s->vb);
+            vorbis_dsp_clear (&s->vd);
+        }
+        vorbis_comment_clear (&s->vc);
+        vorbis_info_clear (&s->vi);
+        downmix_clear (s->downmix);
+        resample_clear (s->resamp);
+        encode_free (s->encoder);
+        free (s);
}
}

-/* Returns: -1 fatal death failure, argh!
- *              0 haven't produced any output yet
- *             >0 success
- */
-int reencode_page(reencode_state *s, ref_buffer *buf,
-        unsigned char **outbuf, int *outlen)
-{
-    ogg_page og, encog;
-    ogg_packet op;
-    int retbuflen=0, old;
-    unsigned char *retbuf=NULL;
-

-    og.header_len = buf->aux_data;
-    og.body_len = buf->len - buf->aux_data;
-    og.header = buf->buf;
-    og.body = buf->buf + og.header_len;

-    if(s->current_serial != ogg_page_serialno(&og))
+static int reencode_vorbis_header (struct reencode *s, ogg_packet *op)
+{
+    if (s->need_headers)
{
-        s->current_serial = ogg_page_serialno(&og);
-
-        if(s->encoder)
+        /* LOG_DEBUG0("processing vorbis header"); */
+        if (vorbis_synthesis_headerin (&s->vi, &s->vc, op) < 0)
{
-            if(s->resamp) {
-                resample_finish(s->resamp);
-                encode_data_float(s->encoder, s->resamp->buffers,
-                        s->resamp->buffill);
+            LOG_ERROR1 ("Failed to process Vorbis headers (%d)", 4-s->need_headers);
+            return -1;
+        }
+        s->need_headers--;
+        if (s->need_headers == 0)
+        {
+            char **comment;
+            int channels = s->vi.channels;
+
+            vorbis_synthesis_init (&s->vd, &s->vi);
+            vorbis_block_init (&s->vd, &s->vb);
+
+            s->settings->encode_channels = s->vi.channels;
+            if (s->settings->channels && s->settings->channels != s->vi.channels)
+            {
+                s->downmix = downmix_initialise();
+                s->settings->encode_channels = 1;
}
-            encode_finish(s->encoder);
-            while(encode_flush(s->encoder, &encog) != 0)
+
+            s->settings->encode_rate = s->vi.rate;
+            if (s->settings->samplerate && s->settings->samplerate != s->vi.rate)
{
-                old = retbuflen;
-                retbuflen += encog.header_len + encog.body_len;
-                retbuf = realloc(retbuf, retbuflen);
-                memcpy(retbuf+old, encog.header, encog.header_len);
-                memcpy(retbuf+old+encog.header_len, encog.body,
-                        encog.body_len);
+                s->settings->encode_rate = s->settings->samplerate;
+                s->resamp = resample_initialise (s->settings->encode_channels, s->vi.rate, s->settings->samplerate);
}
+
+            comment=s->vc.user_comments;
+            while (*comment)
+            {
+                encode_comment (s->encoder, *comment);
+                ++comment;
+            }
+
+            if (encode_setup (s->encoder, s->settings) < 0)
+                return -1;
}
-        encode_clear(s->encoder);
-        s->encoder = NULL;
-        resample_clear(s->resamp);
-        s->resamp = NULL;
-        downmix_clear(s->downmix);
-        s->downmix = NULL;

-        ogg_stream_clear(&s->os);
-        ogg_stream_init(&s->os, s->current_serial);
-        ogg_stream_pagein(&s->os, &og);
+        return 0;
+    }
+    LOG_WARN0("function called when not expecting header");
+    return -1;
+}

-        vorbis_block_clear(&s->vb);
-        vorbis_dsp_clear(&s->vd);
-        vorbis_comment_clear(&s->vc);
-        vorbis_info_clear(&s->vi);

-        vorbis_info_init(&s->vi);
-        vorbis_comment_init(&s->vc);
+int reencode_packetin (struct reencode *s, ogg_packet *packet)
+{
+    int ret = 0;
+    float **pcm;
+    int samples;

-        if(ogg_stream_packetout(&s->os, &op) != 1)
+    if (s->need_headers == 0)
+    {
+        if (vorbis_synthesis (&s->vb, packet) == 0)
{
-            LOG_ERROR0("Invalid primary header in stream");
-            return -1;
+            vorbis_synthesis_blockin (&s->vd, &s->vb);
}

-        if(vorbis_synthesis_headerin(&s->vi, &s->vc, &op) < 0)
+        /* NOTE: we could expose pcm float buffer from encoder so that copies can be reduced further */
+        while ((samples = vorbis_synthesis_pcmout (&s->vd, &pcm)) > 0)
{
-            LOG_ERROR0("Input stream not vorbis, can't reencode");
-            return -1;
-        }
-
-        s->need_headers = 2; /* We still need two more header packets */
-        LOG_DEBUG0("Reinitialising reencoder for new logical stream");
-    }
-    else
-    {
-        ogg_stream_pagein(&s->os, &og);
-        while(ogg_stream_packetout(&s->os, &op) > 0)
-        {
-            if(s->need_headers)
+            if (s->downmix)
{
-                vorbis_synthesis_headerin(&s->vi, &s->vc, &op);
-                /* If this was the last header, init the rest */
-                if(!--s->need_headers)
+                downmix_buffer_float(s->downmix, pcm, samples, s->vi.channels);
+                if(s->resamp)
{
-                    vorbis_block_init(&s->vd, &s->vb);
-                    vorbis_synthesis_init(&s->vd, &s->vi);
-
-                    s->encoder = encode_initialise(s->out_channels,
-                            s->out_samplerate, s->managed,
-                            s->out_min_br, s->out_nom_br, s->out_max_br,
-                            s->quality, &s->vc);
-
-                    if(!s->encoder) {
-                        LOG_ERROR0("Failed to configure encoder for reencoding");
-                        return -1;
-                    }
-                    if(s->vi.rate != s->out_samplerate) {
-                        s->resamp = resample_initialise(s->out_channels,
-                                s->vi.rate, s->out_samplerate);
-                    }
-                    else
-                        s->resamp = NULL;
-
-                    if(s->vi.channels != s->out_channels) {
-                        if(s->vi.channels == 2 && s->out_channels == 1)
-                            s->downmix = downmix_initialise();
-                        else {
-                            LOG_ERROR2("Converting from %d to %d channels is not"
-                                    " currently supported", s->vi.channels,
-                                    s->out_channels);
-                            return -1;
-                        }
-                    }
-                    else
-                        s->downmix = NULL;
+                    resample_buffer_float(s->resamp, &s->downmix->buffer, samples);
+                    encode_data_float(s->encoder, s->resamp->buffers, s->resamp->buffill);
}
+                else
+                    encode_data_float(s->encoder, &s->downmix->buffer, samples);
}
+            else if (s->resamp)
+            {
+                resample_buffer_float(s->resamp, pcm, samples);
+                encode_data_float(s->encoder, s->resamp->buffers,
+                        s->resamp->buffill);
+            }
else
{
-                float **pcm;
-                int samples;
-
-
-                if(vorbis_synthesis(&s->vb, &op)==0)
-                {
-                    vorbis_synthesis_blockin(&s->vd, &s->vb);
-                }
-
-                while((samples = vorbis_synthesis_pcmout(&s->vd, &pcm))>0)
-                {
-                    if(s->downmix) {
-                        downmix_buffer_float(s->downmix, pcm, samples);
-                        if(s->resamp) {
-                            resample_buffer_float(s->resamp, &s->downmix->buffer,
-                                    samples);
-                            encode_data_float(s->encoder, s->resamp->buffers,
-                                    s->resamp->buffill);
-                        }
-                        else
-                            encode_data_float(s->encoder, &s->downmix->buffer,
-                                    samples);
-                    }
-                    else if(s->resamp) {
-                        resample_buffer_float(s->resamp, pcm, samples);
-                        encode_data_float(s->encoder, s->resamp->buffers,
-                                s->resamp->buffill);
-                    }
-                    else
-                        encode_data_float(s->encoder, pcm, samples);
-                    vorbis_synthesis_read(&s->vd, samples);
-                }
-
-                while(encode_dataout(s->encoder, &encog) != 0)
-                {
-
-                    old = retbuflen;
-                    retbuflen += encog.header_len + encog.body_len;
-                    retbuf = realloc(retbuf, retbuflen);
-                    memcpy(retbuf+old, encog.header, encog.header_len);
-                    memcpy(retbuf+old+encog.header_len, encog.body,
-                            encog.body_len);
-                }
+                encode_data_float(s->encoder, pcm, samples);
}
+            vorbis_synthesis_read(&s->vd, samples);
+            ret = 1;
}
+        if (packet->e_o_s)
+            encode_endstream (s->encoder);
+        return ret;
}
+    return reencode_vorbis_header (s, packet);
+}

-    /* We've completed every packet from this page, so
-     * now we can return what we wanted, depending on whether
-     * we actually got data out or not
-     */
-    if(retbuflen > 0)
+
+
+
+int reencode_packetout(struct reencode *s, ogg_packet *op)
+{
+    if (s)
{
-        *outbuf = retbuf;
-        *outlen = retbuflen;
-        return retbuflen;
+        if (s->need_headers)
+            return 0;
+        return encode_packetout (s->encoder, op);
}
-    else
-    {
-        return 0;
-    }
+    return -1;
}

-

Modified: icecast/branches/kh/ices/src/reencode.h
===================================================================
--- icecast/trunk/ices/src/reencode.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/reencode.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* reencode.h
* - reencoding functions
*
- * $Id: reencode.h,v 1.7 2004/01/13 16:35:27 karl Exp $
+ * $Id: reencode.h,v 1.4 2002/08/03 14:41:10 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -17,45 +17,55 @@
#include <ogg/ogg.h>
#include <vorbis/codec.h>

-#include "cfgparse.h"
-#include "stream.h"
+typedef struct _reencode_tag reencode;
+
#include "encode.h"
+#include "inputmodule.h"
#include "audio.h"

-typedef struct {
-    int out_min_br;
-    int out_nom_br;
-    int out_max_br;
-    float quality;
-    int managed;
+struct instance;

+struct reencode
+{
+    struct encoder *encoder;
+    struct encoder_settings *settings;
+
int out_samplerate;
int out_channels;

int in_samplerate;
int in_channels;

-    int current_serial;
+    int in_use;
+
int need_headers;

-    ogg_stream_state os;
vorbis_info vi;
vorbis_comment vc;
vorbis_dsp_state vd;
vorbis_block vb;

-    encoder_state *encoder;
-    downmix_state *downmix;
-    resample_state *resamp;
+    struct downmix *downmix;
+    struct resample *resamp;

-} reencode_state;
+};

-reencode_state *reencode_init(instance_t *stream);
-int reencode_page(reencode_state *s, ref_buffer *buf,
-        unsigned char **outbuf, int *outlen);
-void reencode_clear(reencode_state *s);
+struct reencode *reencode_new_init ();

+int reencode_init (struct instance *);
+void reencode_free (struct reencode *s);

+int reencode_pagein (struct reencode *s, ogg_page *og);
+int reencode_pageout (struct reencode *s, ogg_page *og);
+int reencode_packetout (struct reencode *s, ogg_packet *op);
+int reencode_packetin (struct reencode *s, ogg_packet *packet);

+void reencode_clear(struct reencode *s);
+int reencode_send (struct instance *stream);
+int reencode_flush (struct reencode *s, ogg_page *og);
+void reencode_setup (struct reencode *s, long serial);
+int reencode_ogg_header (struct reencode  *s, ogg_page *og, unsigned samplerate, unsigned channels);
+
+
#endif /* __REENCODE_H */


Modified: icecast/branches/kh/ices/src/resample.c
===================================================================
--- icecast/trunk/ices/src/resample.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/resample.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,6 @@
/* resample.c: see resample.h for interesting stuff */
-
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include <config.h>
#endif

#include <math.h>
@@ -129,7 +128,7 @@
}


-int resampler_init(resampler_state *state, int channels, int outfreq, int infreq, resampler_parameter op1, ...)
+int resampler_init(struct resampler *state, int channels, int outfreq, int infreq, resampler_parameter op1, ...)
{
double beta = 16.0,
cutoff = 0.80,
@@ -240,7 +239,7 @@
}


-static int push(resampler_state const * const state, SAMPLE *pool, int * const poolfill, int * const offset, SAMPLE *dest, int dststep, SAMPLE const *source, int srcstep, size_t srclen)
+static int push(struct resampler const * const state, SAMPLE *pool, int * const poolfill, int * const offset, SAMPLE *dest, int dststep, SAMPLE const *source, int srcstep, size_t srclen)
{
SAMPLE    * const destbase = dest,
*poolhead = pool + *poolfill,
@@ -319,13 +318,13 @@
}


-int resampler_push_max_input(resampler_state const * const state, size_t maxoutput)
+int resampler_push_max_input(struct resampler const * const state, size_t maxoutput)
{
return maxoutput * state->infreq / state->outfreq;
}


-int resampler_push_check(resampler_state const * const state, size_t srclen)
+int resampler_push_check(struct resampler const * const state, size_t srclen)
{
if (state->poolfill < state->taps)
srclen -= state->taps - state->poolfill;
@@ -334,7 +333,7 @@
}


-int resampler_push(resampler_state *state, SAMPLE **dstlist, SAMPLE const **srclist, size_t srclen)
+int resampler_push(struct resampler *state, SAMPLE **dstlist, SAMPLE const **srclist, size_t srclen)
{
int result = -1, poolfill = -1, offset = -1, i;

@@ -356,7 +355,7 @@
}


-int resampler_push_interleaved(resampler_state *state, SAMPLE *dest, SAMPLE const *source, size_t srclen)
+int resampler_push_interleaved(struct resampler *state, SAMPLE *dest, SAMPLE const *source, size_t srclen)
{
int result = -1, poolfill = -1, offset = -1, i;

@@ -378,7 +377,7 @@
}


-int resampler_drain(resampler_state *state, SAMPLE **dstlist)
+int resampler_drain(struct resampler *state, SAMPLE **dstlist)
{
SAMPLE *tail;
int result = -1, poolfill = -1, offset = -1, i;
@@ -405,7 +404,7 @@
}


-int resampler_drain_interleaved(resampler_state *state, SAMPLE *dest)
+int resampler_drain_interleaved(struct resampler *state, SAMPLE *dest)
{
SAMPLE *tail;
int result = -1, poolfill = -1, offset = -1, i;
@@ -432,7 +431,7 @@
}


-void resampler_clear(resampler_state *state)
+void resampler_clear(struct resampler *state)
{
assert(state);
assert(state->table);
@@ -443,3 +442,20 @@
memset(state, 0, sizeof(*state));
}

+
+int parse_resample(xmlNodePtr node, void *resample_ptr)
+{
+    struct cfg_tag resample_old_tags[] =
+    {
+        { "out-rate", get_xml_int, resample_ptr },
+        { NULL, NULL, NULL }
+    };
+    /* for backward compatability */
+    if (parse_xml_tags ("resample", node->xmlChildrenNode, resample_old_tags))
+        return -1;
+    if (*(unsigned*)resample_ptr == 0)
+        if (get_xml_int (node, resample_ptr))
+            return -1;
+    return 0;
+}
+

Modified: icecast/branches/kh/ices/src/resample.h
===================================================================
--- icecast/trunk/ices/src/resample.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/resample.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -20,9 +20,11 @@
#ifndef _RESAMPLE_H_INCLUDED
#define _RESAMPLE_H_INCLUDED

+#include <cfgparse.h>
+
typedef float SAMPLE;

-typedef struct
+struct resampler
{
unsigned int channels, infreq, outfreq, taps;
float *table;
@@ -31,7 +33,7 @@
/* dynamic bits */
int poolfill;
int offset;
-} resampler_state;
+};

typedef enum
{
@@ -42,7 +44,7 @@
RES_BETA    /* (double)16.0 */
} resampler_parameter;

-int resampler_init(resampler_state *state, int channels, int outfreq, int infreq, resampler_parameter op1, ...);
+int resampler_init(struct resampler *state, int channels, int outfreq, int infreq, resampler_parameter op1, ...);
/*
* Configure *state to manage a data stream with the specified parameters.  The
* string 'params' is currently unspecified, but will configure the parameters
@@ -58,7 +60,7 @@
*/


-int resampler_push_max_input(resampler_state const *state, size_t maxoutput);
+int resampler_push_max_input(struct resampler const *state, size_t maxoutput);
/*
*  Returns the maximum number of input elements that may be provided without
*  risk of flooding an output buffer of size maxoutput.  maxoutput is
@@ -66,15 +68,15 @@
*/


-int resampler_push_check(resampler_state const *state, size_t srclen);
+int resampler_push_check(struct resampler const *state, size_t srclen);
/*
* Returns the number of elements that will be returned if the given srclen
* is used in the next call to resampler_push().
*/


-int resampler_push(resampler_state *state, SAMPLE **dstlist, SAMPLE const **srclist, size_t srclen);
-int resampler_push_interleaved(resampler_state *state, SAMPLE *dest, SAMPLE const *source, size_t srclen);
+int resampler_push(struct resampler *state, SAMPLE **dstlist, SAMPLE const **srclist, size_t srclen);
+int resampler_push_interleaved(struct resampler *state, SAMPLE *dest, SAMPLE const *source, size_t srclen);
/*
* Pushes srclen samples into the front end of the filter, and returns the
* number of resulting samples.
@@ -87,8 +89,8 @@
*/


-int resampler_drain(resampler_state *state, SAMPLE **dstlist);
-int resampler_drain_interleaved(resampler_state *state, SAMPLE *dest);
+int resampler_drain(struct resampler *state, SAMPLE **dstlist);
+int resampler_drain_interleaved(struct resampler *state, SAMPLE *dest);
/*
* Recover the remaining elements by flushing the internal pool with 0 values,
* and storing the resulting samples.
@@ -98,10 +100,13 @@
*/


-void resampler_clear(resampler_state *state);
+void resampler_clear(struct resampler *state);
/*
* Free allocated buffers, etc.
*/

+
+int parse_resample (xmlNodePtr node, void *resample_ptr);
+
#endif


Added: icecast/branches/kh/ices/src/runner.c
===================================================================
--- icecast/trunk/ices/src/runner.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/runner.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,435 @@
+/* runner.c
+ * runner processing. The process involved in by each runner
+ *
+ * Copyright (c) 2002-3 Karl Heyes <karl at xiph.org>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+#include <shout/shout.h>
+
+#include "cfgparse.h"
+#include "runner.h"
+#include "stream.h"
+#include "net/resolver.h"
+#include "signals.h"
+#include "thread/thread.h"
+#include "reencode.h"
+#include "encode.h"
+#include "audio.h"
+#include "inputmodule.h"
+
+#include <om_file.h>
+#include <om_shout.h>
+
+#define DEFAULT_STREAM_NAME "unnamed ices stream"
+#define DEFAULT_STREAM_GENRE "ices unset"
+#define DEFAULT_STREAM_DESCRIPTION "no description set"
+#define DEFAULT_QUALITY 3
+#define DEFAULT_DOWNMIX 0
+#define DEFAULT_RESAMPLE 0
+
+
+#define MODULE "stream/"
+#include "logging.h"
+
+#define MAX_ERRORS 10
+
+/*
+ * Close channel to next runner
+ */
+
+void runner_close (struct runner *run)
+{
+    if (run)
+    {
+        LOG_DEBUG1 ("Runner thread %d shutting down", run->id);
+        close (run->fd[1]);
+        run->fd[1] = -1;
+        thread_join (run->thread);
+    }
+}
+
+/*
+ * send the input buffer to the next runner or if none
+ * free the buffer.
+ *
+ * The queue does not do locking, the assumption is that
+ * there is always one on the queue, so the tail will never
+ * be referring to the head.
+ *
+ */
+
+int send_to_runner (struct runner *run, input_buffer *buffer)
+{
+    if (run)
+    {
+        buffer->next = NULL;
+
+#ifdef USE_PIPES
+        if (write (run->fd[1], &buffer, sizeof (buffer)) < (ssize_t)sizeof (buffer))
+        {
+            LOG_WARN0 ("unable to write to runner");
+            return -1;
+        }
+#else
+        *run->pending_tail = buffer;
+        run->pending_tail = &buffer->next;
+        thread_cond_signal (&run->data_available);
+#endif
+
+        return 0;
+    }
+    input_free_buffer (buffer);
+    return 0;
+}
+
+
+/* retreive input buffer from runner queue. Companion
+ * to previous function to remove from the queue, again
+ * make sure there is at least one on the queue.
+ */
+
+input_buffer *runner_wait_for_data(struct runner *run)
+{
+    input_buffer *ib;
+
+#ifdef USE_PIPES
+    if (read (run->fd[0], &ib, sizeof (ib)) < (ssize_t)sizeof (ib))
+        return NULL;
+#else
+    while (run->pending == NULL || (run->pending && run->pending->next == NULL))
+    {
+        thread_cond_wait (&run->data_available);
+        if (ices_config->shutdown)
+            return NULL;
+    }
+
+    ib = run->pending;
+    run->pending = ib->next;
+    ib->next = NULL;
+#endif
+
+    return ib;
+}
+
+
+void stream_cleanup (struct instance *stream)
+{
+
+    if (stream->ops && stream->ops->flush_data)
+    {
+        LOG_DEBUG1 ("Cleanup of stream %d required", stream->id);
+        stream->ops->flush_data (stream);
+    }
+    output_clear (&stream->output);
+}
+
+
+
+/* process a block of data. This may be skipped, or may even kick off
+ * a new connection.
+ *
+ */
+static void add_to_stream (struct instance *stream, input_buffer *ib)
+{
+    if (ib->critical)
+        process_critical (stream, ib);
+
+    /* LOG_DEBUG1 ("ops is %p", stream->ops); */
+    if (stream->ops && stream->ops->process_buffer(stream, ib) < 0)
+        stream_cleanup (stream);
+
+    if (ib->type == ICES_INPUT_NONE)
+        return;
+    /* the normal end of stream flush */
+    if (ib->eos && stream->ops)
+    {
+        if (stream->ops->flush_data)
+        {
+            LOG_DEBUG1("stream flushed due to EOS [%d]", stream->id);
+            stream->ops->flush_data (stream);
+        }
+        /* EOS seen and handled so disable further processing until
+         * another start of stream is sent.  */
+        stream->ops = NULL;
+    }
+
+    return;
+}
+
+static struct instance *_allocate_instance (void)
+{
+    struct instance *instance = (struct instance *)calloc(1, sizeof(struct instance));
+    static int id = 1;
+
+    if (instance == NULL)
+        return NULL;
+
+    instance->resampleoutrate = DEFAULT_RESAMPLE;
+    instance->passthru = 0;
+
+    instance->encode_settings.quality = DEFAULT_QUALITY;
+    instance->downmix = DEFAULT_DOWNMIX;
+
+    instance->id = id++;
+    instance->next = NULL;
+
+    return instance;
+}
+
+
+
+int parse_instance (xmlNodePtr node, void *arg)
+{
+    struct runner *run = arg;
+    struct instance *instance = _allocate_instance();
+
+    while (instance)
+    {
+        struct cfg_tag  instance_tags[] =
+        {
+            { "name",           get_xml_string,     &instance->output.name },
+            { "genre",          get_xml_string,     &instance->output.genre },
+            { "description",    get_xml_string,     &instance->output.description },
+            { "url",            get_xml_string,     &instance->output.url },
+            { "downmix",        get_xml_bool,       &instance->downmix },
+            { "passthru",       get_xml_bool,       &instance->passthru},
+            { "passthrough",    get_xml_bool,       &instance->passthru},
+            { "resample",       parse_resample,     &instance->resampleoutrate },
+            { "encode",         parse_encode,       &instance->encode_settings },
+            { "savestream",     parse_savefile,     &instance->output },
+            { "savefile",       parse_savefile,     &instance->output },
+            { "shout",          parse_shout,        &instance->output },
+            { NULL, NULL, NULL }
+        };
+
+        /* config should be derived from runner */
+        xmlMemoryDump();
+        if (ices_config->stream_name)
+            instance->output.name = xmlStrdup (ices_config->stream_name);
+        if (ices_config->stream_genre)
+            instance->output.genre = xmlStrdup (ices_config->stream_genre);
+        if (ices_config->stream_description)
+            instance->output.description = xmlStrdup (ices_config->stream_description);
+        if (ices_config->stream_url)
+            instance->output.url = xmlStrdup (ices_config->stream_url);
+
+        if (parse_xml_tags ("instance", node->xmlChildrenNode, instance_tags))
+            break;
+
+        if (run->instances == NULL)
+            run->instances = instance;
+        else
+        {
+            struct instance *i = run->instances;
+            while (i->next != NULL) i = i->next;
+            i->next = instance;
+
+        }
+        return 0;
+    }
+    free (instance);
+
+    return -1;
+}
+
+
+
+void *ices_runner (void *arg)
+{
+    struct runner *run = arg;
+    struct instance *current;
+
+#ifdef HAVE_SCHED_GET_PRIORITY_MAX
+    int policy;
+    struct sched_param param;
+
+    pthread_getschedparam (pthread_self(), &policy, &param);
+    param . sched_priority = sched_get_priority_min (SCHED_OTHER);
+
+    if (pthread_setschedparam (pthread_self(), SCHED_OTHER, &param))
+    {
+        LOG_ERROR1 ("failed to set priority: %s", strerror (errno));
+    }
+    else
+        LOG_INFO0 ("set priority on runner");
+#endif
+    LOG_INFO1 ("Runner %d ready", run->id);
+
+    while (1)
+    {
+        input_buffer *buffer;
+
+        buffer = runner_wait_for_data (run);
+
+        if (buffer == NULL)
+            break;
+
+        current = run->instances;
+
+        while (current != NULL)
+        {
+            add_to_stream (current, buffer);
+            current = current->next;
+        }
+
+        send_to_runner (run->next, buffer);
+    }
+
+    runner_close (run->next);
+    LOG_DEBUG1 ("Runner thread %d cleaning up streams", run->id);
+    current = run->instances;
+    while (current)
+    {
+        struct instance *next;
+        next = current->next;
+        stream_cleanup (current);
+        current = next;
+    }
+    close (run->fd[0]);
+    run->fd[0] = -1;
+    run->not_running = 1;
+    LOG_DEBUG1 ("Runner thread %d finshed", run->id);
+
+    return NULL;
+}
+
+struct instance *instance_free (struct instance *instance)
+{
+    struct instance *next = NULL;
+    if (instance)
+    {
+        next = instance->next;
+        /* reencode_free (instance->reenc); */
+        free (instance);
+    }
+    return next;
+}
+
+
+struct runner *config_free_runner(struct runner *run)
+{
+    struct runner *next = run->next;
+    struct instance *instance = NULL;
+
+    while ((instance = run->instances))
+    {
+        run->instances = instance->next;
+        instance_free (instance);
+    }
+
+    free (run);
+
+    return next;
+}
+
+
+struct runner *allocate_runner()
+{
+    static int runner_id = 1;
+    struct runner *run = calloc (1,sizeof (struct runner));
+
+    if (run == NULL)
+        return NULL;
+
+    run->not_running = 1;
+
+#ifdef USE_PIPES
+    pipe (run->fd);
+#else
+    thread_cond_create (&run->data_available);
+    run->pending_tail = &run->pending;
+#endif
+    run->id = runner_id++;
+
+    return run;
+}
+
+
+int create_runner_thread (struct runner *run)
+{
+    if (run == NULL)
+        return -1;
+
+    run->not_running = 0;
+
+    run->thread = thread_create("runner", ices_runner, run, 0);
+    if (run->thread == NULL)
+    {
+        run->not_running = 1;
+        return -1;
+    }
+    return 0;
+}
+
+
+void start_runners()
+{
+    struct runner **runnerptr = &ices_config->runners;
+    struct runner *run;
+
+    while (*runnerptr != NULL)
+    {
+        run = *runnerptr;
+
+        if (run->not_running && !ices_config->shutdown)
+        {
+            LOG_DEBUG0("starting runner");
+            create_runner_thread (run);
+        }
+
+        runnerptr = &run->next;
+    }
+}
+
+
+
+int parse_runner (xmlNodePtr node, void *arg)
+{
+    config_t *config = arg;
+    struct runner *run = allocate_runner ();
+
+    while (run)
+    {
+        struct cfg_tag runner_tag[] =
+        {
+            { "instance", parse_instance, run },
+            { NULL, NULL, NULL }
+        };
+        if (parse_xml_tags ("runner", node->xmlChildrenNode, runner_tag))
+            break;
+
+        if (config->runners == NULL)
+            config->runners = run;
+        else
+        {
+            struct runner *i = config->runners;
+            while (i->next != NULL) i = i->next;
+            i->next = run;
+        }
+        config->runner_count++;
+        return 0;
+    }
+    free (run);  /* separate function */
+    return -1;
+}
+

Added: icecast/branches/kh/ices/src/runner.h
===================================================================
--- icecast/trunk/ices/src/runner.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/runner.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,77 @@
+/* stream.h
+ * - Core streaming functions/main loop.
+ *
+ * $Id: stream.h,v 1.2 2001/09/25 12:04:22 msmith Exp $
+ *
+ * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+
+#ifndef __RUNNER_H
+#define __RUNNER_H
+
+#include "thread/thread.h"
+#include "inputmodule.h"
+#include "cfgparse.h"
+#include "encode.h"
+
+#define MAX_QUEUE_NODE_BUFFER 16*1024
+typedef struct _runner_tag runner;
+typedef struct _instance_tag instance_t;
+
+struct instance
+{
+    input_type type;
+    int id;
+
+    struct encoder_settings encode_settings;
+
+    struct codec_ops *ops;
+
+    int downmix;
+    int passthru;
+
+    struct output_state output;
+
+    float quality;
+    int channels;
+
+    int resampleoutrate;
+
+    struct instance *next;
+};
+
+
+struct runner
+{
+#ifdef USE_PIPES
+    int fd [2];
+#else
+    input_buffer *pending, **pending_tail;
+    cond_t   data_available;
+#endif
+    int id;
+    struct instance *instances;
+    thread_type *thread;
+    int not_running;
+
+    struct runner *next;
+};
+
+
+void *ices_runner (void *arg);
+int  send_to_runner (struct runner *run, input_buffer *buffer);
+void runner_close (struct runner *run);
+void stream_cleanup(struct instance *stream);
+int create_runner_thread (struct runner *run);
+int parse_runner (xmlNodePtr node, void *arg);
+struct runner *config_free_runner(struct runner *run);
+void start_runners();
+
+#endif
+

Modified: icecast/branches/kh/ices/src/signals.c
===================================================================
--- icecast/trunk/ices/src/signals.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/signals.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* signals.c
* - signal handling/setup
*
- * $Id: signals.c,v 1.8 2003/03/28 01:07:37 karl Exp $
+ * $Id: signals.c,v 1.4 2001/09/25 12:04:22 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -12,67 +12,66 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

-#include <thread/thread.h>
+#include "thread/thread.h"

#include "cfgparse.h"
#include "stream.h"
-#include "input.h"
#include "inputmodule.h"
-#include "event.h"

#define MODULE "signals/"
#include "logging.h"

extern volatile int metadata_update_signalled;
+int move_to_next_input;

-void signal_usr1_handler(int signum)
+void signal_usr1_handler(int signum __attribute__((unused)))
{
-    LOG_INFO0("Metadata update requested");
+    /* LOG_INFO0("Metadata update requested"); */
metadata_update_signalled = 1;
-    thread_cond_broadcast(&ices_config->event_pending_cond);

signal(SIGUSR1, signal_usr1_handler);
}

-void signal_hup_handler(int signum)
+void signal_usr2_handler(int signum __attribute__((unused)))
{
+    /* LOG_INFO0("Switch to next input stream requested"); */
+
+    move_to_next_input = 1;
+
+	signal(SIGUSR2, signal_usr2_handler);
+}
+
+void signal_hup_handler(int signum __attribute__((unused)))
+{
LOG_INFO0("Flushing logs");
log_flush(ices_config->log_id);

-    /* Now, let's tell it to move to the next track */
-    ices_config->inmod->handle_event(ices_config->inmod,EVENT_NEXTTRACK,NULL);
-
+    ices_config->next_track = 1;
signal(SIGHUP, signal_hup_handler);
}

-void signal_int_handler(int signum)
+void signal_int_handler(int signum __attribute__((unused)))
{
-    /* Is a mutex needed here? Probably */
-    if (!ices_config->shutdown)
-    {
-        LOG_INFO0("Shutdown requested...");
-        ices_config->shutdown = 1;
-        thread_cond_broadcast(&ices_config->queue_cond);
-
-        /* If user gives a second sigint, just die. */
-        signal(SIGINT, SIG_DFL);
-    }
+    /* LOG_INFO0("Shutdown requested..."); */
+    ices_config->shutdown = 1;
+    signal(SIGINT, signal_int_handler);
}


void signals_setup(void)
{
-    signal(SIGHUP, signal_hup_handler);
-    signal(SIGINT, signal_int_handler);
-    signal(SIGUSR1, signal_usr1_handler);
-    signal(SIGPIPE, SIG_IGN);
+	signal(SIGINT, signal_int_handler);
+	signal(SIGTERM, signal_int_handler);
+	signal(SIGUSR1, signal_usr1_handler);
+	signal(SIGUSR2, signal_usr2_handler);
+	signal(SIGPIPE, SIG_IGN);
}



Modified: icecast/branches/kh/ices/src/signals.h
===================================================================
--- icecast/trunk/ices/src/signals.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/signals.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -16,6 +16,9 @@

#include <signal.h>

+extern int move_to_next_input;
+
+
void signal_hup_handler(int signum);
void signal_int_handler(int signum);


Modified: icecast/branches/kh/ices/src/stream.c
===================================================================
--- icecast/trunk/ices/src/stream.c	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/stream.c	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,10 +1,9 @@
-/* stream.c
- * - Core streaming functions/main loop.
+/* stream_shared.c
+ * - Stream utility functions.
*
- * $Id: stream.c,v 1.33 2004/03/11 17:22:59 karl Exp $
+ * Copyright (c) 2001   Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2002-4 Karl Heyes <karl at xiph.org>
*
- * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
- *
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
* it under the terms of this license. A copy should be included
@@ -12,348 +11,469 @@
*/

#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <errno.h>
-#include <pthread.h>
+#include <sys/uio.h>
#include <unistd.h>
+#include <string.h>
+#include <limits.h>

-#include <shout/shout.h>
+#include <thread/thread.h>

#include "cfgparse.h"
-#include "input.h"
-#include "im_playlist.h"
-#include "signals.h"
-#include "thread/thread.h"
+#include "inputmodule.h"
+#include "stream.h"
#include "reencode.h"
#include "encode.h"
#include "audio.h"
-#include "inputmodule.h"
-#include "stream_shared.h"
-#include "stream.h"
+#include "metadata.h"

-#include <ogg/ogg.h>
-#include <vorbis/codec.h>
-
#define MODULE "stream/"
#include "logging.h"

-#define MAX_ERRORS 10

-/* The main loop for each instance. Gets data passed to it from the stream
- * manager (which gets it from the input module), and streams it to the
- * specified server
- */
-void *ices_instance_stream(void *arg)
+struct pcm2vorbis_encode
{
-    int ret, shouterr;
-    ref_buffer *buffer;
-    stream_description *sdsc = arg;
-    instance_t *stream = sdsc->stream;
-    input_module_t *inmod = sdsc->input;
-    int reencoding = (inmod->type == ICES_INPUT_VORBIS) && stream->encode;
-    int encoding = (inmod->type == ICES_INPUT_PCM) && stream->encode;
-    char *stream_name = NULL, *stream_genre = NULL, *stream_description = NULL;
-    char *stream_url = NULL, *user = NULL;
-    char audio_info[11];
-
-    vorbis_comment_init(&sdsc->vc);
+    struct encoder *enc;
+    struct resample *resamp;
+    struct downmix *downmix;
+};

-    sdsc->shout = shout_new();

-    /* we only support the ice protocol and vorbis streams currently */
-    shout_set_format(sdsc->shout, SHOUT_FORMAT_VORBIS);
-    shout_set_protocol(sdsc->shout, SHOUT_PROTOCOL_HTTP);
+void output_clear(struct output_state *state)
+{
+    if (state == NULL)
+        return;
+    /* clear comment and info */
+    if (state->info_in_use)
+    {
+        struct output_module *mod, *next;
+        LOG_DEBUG0("Clearing up output state");
+        vorbis_comment_clear (&state->vc);
+        vorbis_info_clear (&state->vi);

-    signal(SIGPIPE, signal_hup_handler);
+        /* clear stored headers */
+        free (state->packets[0].packet);
+        /* ogg_packet_clear (&state->packets[1]); */
+        free (state->packets[1].packet);
+        free (state->packets[2].packet);

-    if (!(shout_set_host(sdsc->shout, stream->hostname)) == SHOUTERR_SUCCESS) {
-        LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
-        stream->died = 1;
-        return NULL;
+        /* should clear individual outputs */
+        mod = state->head;
+        while (mod)
+        {
+            next = mod->next;
+            /* clear each output module */
+            mod->output_clear (mod);
+            free (mod);
+            mod = next;
+        }
+        state->head = NULL;
+        if (state->url)
+        {
+            xmlFree (state->url);
+            state->url = NULL;
+        }
+        if (state->name)
+        {
+            xmlFree (state->name);
+            state->name = NULL;
+        }
+        if (state->description)
+        {
+            xmlFree (state->description);
+            state->description = NULL;
+        }
+        if (state->genre)
+        {
+            xmlFree (state->genre);
+            state->genre = NULL;
+        }
+        state->info_in_use = 0;
}
+}

-    shout_set_port(sdsc->shout, stream->port);
-    if (!(shout_set_password(sdsc->shout, stream->password)) == SHOUTERR_SUCCESS) {
-        LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
-        stream->died = 1;
-        return NULL;
-    }
-    if (stream->user)
-        user = stream->user;
-    else
-        user = "source";

-    if(shout_set_user(sdsc->shout, user) != SHOUTERR_SUCCESS) {
-        LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
-        stream->died = 1;
-        return NULL;
+static int _output_oggpacket (struct instance *stream, ogg_packet *op, unsigned samples)
+{
+    struct output_state *state = &stream->output;
+    struct output_module *mod;
+
+    if (op->b_o_s)
+    {
+        LOG_DEBUG0 ("seen new stream, better get headers");
+        state->headers = 3;
}
+    if (op->e_o_s)    LOG_DEBUG0 ("packet marked with EOS seen");

-    if (!(shout_set_agent(sdsc->shout, PACKAGE_STRING)) == SHOUTERR_SUCCESS) {
-        LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
-        stream->died = 1;
-        return NULL;
+    /* need to store vorbis headers */
+    if (state->headers)
+    {
+        ogg_packet *header;
+        void *ptr;
+
+#ifdef DEBUG
+        LOG_DEBUG1 ("header packet has packetno of %lld", op->packetno);
+#endif
+        if (state->headers == 3)
+        {
+            /* LOG_DEBUG1 ("intial header packet %d", stream->id); */
+            if (state->info_in_use)
+            {
+                LOG_DEBUG0 ("Clearing output info/comment settings");
+                vorbis_comment_clear (&state->vc);
+                vorbis_info_clear (&state->vi);
+            }
+            /* LOG_DEBUG0("state vi/vc initialised"); */
+            vorbis_info_init (&state->vi);
+            vorbis_comment_init (&state->vc);
+            state->info_in_use = 1;
+        }
+        /* LOG_DEBUG0("state vi/vc headerin"); */
+        vorbis_synthesis_headerin (&state->vi, &state->vc, op);
+
+        state->headers--;
+        header = &state->packets [2-state->headers];
+
+        switch (state->headers)
+        {
+#if  0
+            /* vorbis_commentheader_out leaks a small amount of mem in v1.0 */
+            case 1:
+                LOG_DEBUG0("processing comment header");
+                if (header->packet)
+                {
+                    LOG_DEBUG0("clearing old header");
+                    ogg_packet_clear (header);
+                    header->packet = NULL;
+                }
+                vorbis_comment_add (&state->vc, "EncodedBy=" PACKAGE_STRING);
+                vorbis_commentheader_out (&state->vc, header);
+                break;
+#endif
+
+            case 0:
+                LOG_DEBUG2 ("samplerate is %d, channels is %d", state->vi.rate, state->vi.channels);
+                state->new_headers = 1;
+                state->start_pos = 0;
+                /* fall thru  we need to store all 3 headers */
+            case 1:
+            case 2:
+                /* LOG_DEBUG1("header count is %d", state->headers); */
+                if (header->packet)
+                {
+                    free (header->packet);
+                    header->packet = NULL;
+                }
+                ptr = malloc (op->bytes);
+                memcpy (header, op, sizeof (*header));
+                memcpy (ptr, op->packet, op->bytes);
+                header->packet = ptr;
+                header->granulepos = 0;
+                break;
+
+            default:
+                LOG_ERROR1("headers expected value is unexpected %d", state->headers);
+                break;
+        }
+        return 0;
}
+    /* LOG_DEBUG1("granulepos is %lld", op->granulepos); */
+    /* printf("granulepos is %lld\n", op->granulepos); */

-    if (!(shout_set_mount(sdsc->shout, stream->mount)) == SHOUTERR_SUCCESS) {
-        LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
-        stream->died = 1;
-        return NULL;
-    }
-    if (shout_set_public (sdsc->shout, stream->public_stream & 1) != SHOUTERR_SUCCESS)
+    mod = state->head;
+    while (mod)
{
-        LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
-        stream->died = 1;
-        return NULL;
+        mod->output_send (mod, op, samples);
+        mod = mod->next;
}

-    /* set the metadata for the stream */
-    if(stream->stream_name)
-        stream_name = stream->stream_name;
-    else if (ices_config->stream_name)
-        stream_name = ices_config->stream_name;
+    state->new_headers = 0;

-    if(stream->stream_description)
-        stream_description = stream->stream_description;
-    else if (ices_config->stream_description)
-        stream_description = ices_config->stream_description;
+    return 0;
+}

-    if(stream->stream_genre)
-        stream_genre = stream->stream_genre;
-    else if (ices_config->stream_genre)
-        stream_genre = ices_config->stream_genre;

-    if(stream->stream_url)
-        stream_url = stream->stream_url;
-    else if (ices_config->stream_url)
-        stream_url = ices_config->stream_url;
+static void flush_vorbis_input (struct instance *stream)
+{
+    LOG_DEBUG1("Flushing ogg packets on %d", stream->id);
+    reencode_free (stream->ops->data);
+    free (stream->ops);
+    stream->ops = NULL;
+}

-    if(stream_name)
-        if (!(shout_set_name(sdsc->shout, stream_name)) == SHOUTERR_SUCCESS) {
-            LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
-            stream->died = 1;
-            return NULL;
-        }
-    if (stream_genre)
-        if (!(shout_set_genre(sdsc->shout, stream_genre)) == SHOUTERR_SUCCESS) {
-            LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
-            stream->died = 1;
-            return NULL;
-        }
-    if (stream_description)
-        if (!(shout_set_description(sdsc->shout, stream_description)) == SHOUTERR_SUCCESS) {
-            LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
-            stream->died = 1;
-            return NULL;
-        }
-    if (stream_url)
-        if (!(shout_set_url(sdsc->shout, stream_url) == SHOUTERR_SUCCESS)) {
-            LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
-            stream->died = 1;
-            return NULL;
-        }

-    if(stream->downmix && encoding && stream->channels == 1) {
-        stream->channels = 1;
-        sdsc->downmix = downmix_initialise();
-    }

-    if(stream->resampleinrate && stream->resampleoutrate && encoding) {
-        stream->samplerate = stream->resampleoutrate;
-        sdsc->resamp = resample_initialise(stream->channels,
-                stream->resampleinrate, stream->resampleoutrate);
+void flush_ogg_packets (struct instance *stream)
+{
+    int send_it = 1;
+    ogg_packet op;
+    struct pcm2vorbis_encode *s = stream->ops->data;
+
+    if (encode_endstream(s->enc))
+    {
+        LOG_DEBUG1("Flushing out encoded ogg packets stream %d", stream->id);
+        while (encode_packetout (s->enc, &op) > 0)
+        {
+            if (send_it && _output_oggpacket (stream, &op, 0) < 0)
+                send_it = 0;
+        }
}
+    encode_free (s->enc);
+    downmix_clear (s->downmix);
+    resample_clear (s->resamp);
+    free (s);
+    stream->ops->data = NULL;
+}

-    /* max integer is 10 bytes + 1 for null term */
-    snprintf(audio_info, sizeof(audio_info), "%d", stream->samplerate);
-    shout_set_audio_info(sdsc->shout, SHOUT_AI_SAMPLERATE, audio_info);
-    snprintf(audio_info, sizeof(audio_info), "%d", stream->channels);
-    shout_set_audio_info(sdsc->shout, SHOUT_AI_CHANNELS, audio_info);
-    if (stream->managed)
+
+static int encode_pcm (struct instance *stream, input_buffer *buffer)
+{
+    ogg_packet op;
+    int ret = 0;
+    struct pcm2vorbis_encode *s = stream->ops->data;
+
+    if (buffer->samples == 0)
+        return 0;
+    if (stream->downmix)
{
-        snprintf(audio_info, sizeof(audio_info), "%d", stream->nom_br/1000);
-        shout_set_audio_info(sdsc->shout, SHOUT_AI_BITRATE, audio_info);
+        downmix_buffer_float (s->downmix, buffer->buf, buffer->samples, buffer->channels);
+        if (s->resamp)
+        {
+            resample_buffer_float (s->resamp, &s->downmix->buffer, buffer->samples);
+            encode_data_float (s->enc, s->resamp->buffers, s->resamp->buffill);
+        }
+        else
+            encode_data_float (s->enc, &s->downmix->buffer, buffer->samples);
}
+    else if (s->resamp)
+    {
+        resample_buffer_float (s->resamp, buffer->buf, buffer->samples);
+        encode_data_float (s->enc, s->resamp->buffers, s->resamp->buffill);
+    }
else
{
-        snprintf(audio_info, sizeof(audio_info), "%2.2f", stream->quality);
-        shout_set_audio_info(sdsc->shout, SHOUT_AI_QUALITY, audio_info);
+        encode_data_float (s->enc, buffer->buf, buffer->samples);
}
-
-    if(encoding)
+    while (encode_packetout (s->enc, &op) > 0)
{
-        if(inmod->metadata_update)
-            inmod->metadata_update(inmod->internal, &sdsc->vc);
-        sdsc->enc = encode_initialise(stream->channels, stream->samplerate,
-                stream->managed, stream->min_br, stream->nom_br, stream->max_br,
-                stream->quality, &sdsc->vc);
-        if(!sdsc->enc) {
-            LOG_ERROR0("Failed to configure encoder");
-            stream->died = 1;
-            return NULL; /* FIXME: probably leaking some memory here */
+        if (ret == 0 && _output_oggpacket (stream, &op, 0) < 0)
+        {
+            ret = -1;
+            break;
}
}
-    else if(reencoding)
-        sdsc->reenc = reencode_init(stream);
+    return ret;
+}

-    if(stream->savefilename != NULL)
-    {
-        stream->savefile = fopen(stream->savefilename, "wb");
-        if(!stream->savefile)
-            LOG_ERROR2("Failed to open stream save file %s: %s",
-                    stream->savefilename, strerror(errno));
-        else
-            LOG_INFO1("Saving stream to file %s", stream->savefilename);
-    }

-    if((shouterr = shout_open(sdsc->shout)) == SHOUTERR_SUCCESS)
+static int process_encode_init (struct instance *stream, input_buffer *buffer)
+{
+    unsigned samplerate;
+    int channels;
+    ogg_packet op;
+    struct pcm2vorbis_encode *s;
+    struct encoder *enc = encode_create();
+
+    do
{
-        LOG_INFO3("Connected to server: %s:%d%s",
-                shout_get_host(sdsc->shout), shout_get_port(sdsc->shout), shout_get_mount(sdsc->shout));
+        struct codec_ops *ops = stream->ops;

-        while(1)
+        if (enc == NULL)
+            break;
+        s = calloc (1, sizeof (struct pcm2vorbis_encode));
+        if (s == NULL)
+            break;
+        s->enc = enc;
+        LOG_INFO1 ("Restarting encoder for PCM input on stream %d", stream->id);
+        samplerate = buffer->samplerate;
+
+        if (stream->resampleoutrate)
+            samplerate = stream->resampleoutrate;
+
+        channels = buffer->channels;
+        if (stream->downmix && channels == 2)
{
-            if(stream->buffer_failures > MAX_ERRORS)
-            {
-                LOG_WARN0("Too many errors, shutting down");
+            s->downmix = downmix_initialise();
+            if (s->downmix == NULL)
break;
-            }
+            channels = 1;
+        }

-            buffer = stream_wait_for_data(stream);
-
-            /* buffer being NULL means that either a fatal error occured,
-             * or we've been told to shut down
-             */
-            if(!buffer)
+        if (buffer->samplerate != samplerate)
+        {
+            s->resamp = resample_initialise (channels, buffer->samplerate, samplerate);
+            if (s->resamp == NULL)
break;
+        }

-            /* If data is NULL or length is 0, we should just skip this one.
-             * Probably, we've been signalled to shut down, and that'll be
-             * caught next iteration. Add to the error count just in case,
-             * so that we eventually break out anyway
-             */
-            if(!buffer->buf || !buffer->len)
+        if (buffer->metadata)
+        {
+            char **md = buffer->metadata;
+            while(*md)
{
-                LOG_WARN0("Bad buffer dequeued!");
-                stream->buffer_failures++;
-                continue;
+                LOG_INFO1 ("Adding comment %s", *md);
+                encode_comment (enc, *md++);
}
+        }

-            if(stream->wait_for_critical)
-            {
-                LOG_INFO0("Trying restart on new substream");
-                stream->wait_for_critical = 0;
-            }
+        stream->encode_settings.encode_rate = samplerate;
+        stream->encode_settings.encode_channels = channels;
+        if (encode_setup (enc, &stream->encode_settings) < 0)
+        {
+            LOG_ERROR1("[%d] Failed to configure encoder", stream->id);
+            break;
+        }
+        while (encode_packetout (enc, &op) > 0)
+        {
+            _output_oggpacket (stream, &op, 0);
+        }
+        ops->data = s;
+        ops->flush_data = flush_ogg_packets;
+        ops->process_buffer = encode_pcm;

-            ret = process_and_send_buffer(sdsc, buffer);
+        return 0;
+    } while (0);

-            /* No data produced, do nothing */
-            if(ret == -1)
-                ;
-            /* Fatal error */
-            else if(ret == -2)
-            {
-                LOG_ERROR0("Serious error, waiting to restart on "
-                           "next substream. Stream temporarily suspended.");
-                /* Set to wait until a critical buffer comes through (start of
-                 * a new substream, typically), and flush existing queue.
-                 */
-                thread_mutex_lock(&ices_config->flush_lock);
-                stream->wait_for_critical = 1;
-                input_flush_queue(stream->queue, 0);
-                thread_mutex_unlock(&ices_config->flush_lock);
-            }
-            /* Non-fatal shout error */
-            else if(ret == 0)
-            {
-                LOG_ERROR2("Send error: %s (%s)",
-                        shout_get_error(sdsc->shout), strerror(errno));
-                if(shout_get_errno(sdsc->shout) == SHOUTERR_SOCKET)
-                {
-                    int i=0;
+    if (enc) encode_free (enc);
+    if (s)
+    {
+        if (s->resamp) resample_clear (s->resamp);
+        if (s->downmix) downmix_clear (s->downmix);
+        free (s);
+    }
+    LOG_ERROR0("Encoder failed");
+    return -1;
+}

-                    /* While we're trying to reconnect, don't receive data
-                     * to this instance, or we'll overflow once reconnect
-                     * succeeds
-                     */
-                    thread_mutex_lock(&ices_config->flush_lock);
-                    stream->skip = 1;

-                    /* Also, flush the current queue */
-                    input_flush_queue(stream->queue, 1);
-                    thread_mutex_unlock(&ices_config->flush_lock);
-
-                    while((i < stream->reconnect_attempts ||
-                            stream->reconnect_attempts==-1) &&
-                            !ices_config->shutdown)
-                    {
-                        i++;
-                        LOG_WARN0("Trying reconnect after server socket error");
-                        shout_close(sdsc->shout);
-                        if((shouterr = shout_open(sdsc->shout)) == SHOUTERR_SUCCESS)
-                        {
-                            LOG_INFO3("Connected to server: %s:%d%s",
-                                    shout_get_host(sdsc->shout), shout_get_port(sdsc->shout),
-                                    shout_get_mount(sdsc->shout));
-                            /* This stream can't restart until the next
-                             * logical stream comes along, since the
-                             * server won't have any cached headers for
-                             * this source/connection. So, don't continue
-                             * yet.
-                             */
-                            thread_mutex_lock(&ices_config->flush_lock);
-                            stream->wait_for_critical = 1;
-                            input_flush_queue(stream->queue, 0);
-                            thread_mutex_unlock(&ices_config->flush_lock);
-                            break;
-                        }
-                        else
-                        {
-                            LOG_ERROR3("Failed to reconnect to %s:%d (%s)",
-                                shout_get_host(sdsc->shout),shout_get_port(sdsc->shout),
-                                shout_get_error(sdsc->shout));
-                            if(i==stream->reconnect_attempts)
-                            {
-                                LOG_ERROR0("Reconnect failed too many times, "
-                                          "giving up.");
-                                /* We want to die now */
-                                stream->buffer_failures = MAX_ERRORS+1;
-                            }
-                            else /* Don't try again too soon */
-                                thread_sleep (stream->reconnect_delay*1000000);
-                        }
-                    }
-                    stream->skip = 0;
-                }
-                stream->buffer_failures++;
+
+static int reencode_vorbis_packet (struct instance *stream, input_buffer *buffer)
+{
+    ogg_packet *packet = (ogg_packet *)buffer->buf;
+    int ret = 0;
+
+    if (reencode_packetin (stream->ops->data, packet) < 0)
+    {
+        LOG_ERROR1("[%d] Fatal reencoding error encountered", stream->id);
+        return -1;
+    }
+    while (1)
+    {
+        ogg_packet reencoded_packet;
+
+        if ((ret = reencode_packetout (stream->ops->data, &reencoded_packet)) > 0)
+        {
+            _output_oggpacket (stream, &reencoded_packet, 0);
+        }
+        else
+        {
+            if (ret < 0)
+            {
+                LOG_ERROR1("failed getting packet from re-encoder [%d]", stream->id);
+                return -1;
}
-            stream_release_buffer(buffer);
+            else
+                break;
}
}
+    return ret;
+}
+
+
+static int process_ogg_init (struct instance *stream, input_buffer *buffer)
+{
+    if (stream->passthru)
+        stream->ops = &passthru_ptks_ops;
else
{
-        LOG_ERROR4("Failed initial connect to %s:%d (%s: %s)",
-                shout_get_host(sdsc->shout),shout_get_port(sdsc->shout), shout_get_error(sdsc->shout), strerror(errno));
+        struct codec_ops *ops = stream->ops;
+
+        if (stream->resampleoutrate)
+            stream->encode_settings.samplerate = stream->resampleoutrate;
+        if (stream->downmix)
+            stream->encode_settings.channels = 1;
+
+        if ((ops->data = reencode_new_init (&stream->encode_settings)) == NULL)
+        {
+            LOG_ERROR1("failed intialising re-encoder [%d]", stream->id);
+            return -1;
+        }
+        ops->flush_data = flush_vorbis_input;
+        ops->process_buffer = reencode_vorbis_packet;
}
-
-    shout_close(sdsc->shout);
+    return 0;
+}

-    if(stream->savefile != NULL)
-        fclose(stream->savefile);

-    shout_free(sdsc->shout);
-    encode_clear(sdsc->enc);
-    reencode_clear(sdsc->reenc);
-    downmix_clear(sdsc->downmix);
-    resample_clear(sdsc->resamp);
-    vorbis_comment_clear(&sdsc->vc);
+static int send_vorbis_packet (struct instance *stream, input_buffer *buffer)
+{
+    ogg_packet *packet = (ogg_packet *)buffer->buf;

-    stream->died = 1;
-    return NULL;
+    if (_output_oggpacket (stream, packet, buffer->samples) < 0)
+        return -1;
+
+    return 0;
}

+
+/*
+ * Process a critical buffer of data
+ */
+void process_critical (struct instance *stream, input_buffer *buffer)
+{
+    struct codec_ops *ops;
+    int ret = 0;
+
+    if (stream->ops && stream->ops->flush_data)
+    {
+        LOG_DEBUG0("Stream has restarted but no EOS of previous seen");
+        stream->ops->flush_data (stream);
+        free (stream->ops);
+        stream->ops = NULL;
+    }
+
+    ops = malloc (sizeof (struct codec_ops));
+    if (ops == NULL)
+    {
+        LOG_DEBUG1("stream %d - codec ops allocation error", stream->id);
+        return;
+    }
+    stream->ops = ops;
+
+    switch (buffer->type)
+    {
+        case ICES_INPUT_VORBIS_PACKET:
+            ret = process_ogg_init (stream, buffer);
+            break;
+
+        case ICES_INPUT_PCM:
+            ret = process_encode_init (stream, buffer);
+            break;
+
+        default:
+            LOG_ERROR2 ("[%d] No known buffer type %d", stream->id, buffer->type);
+            break;
+    }
+    if (ret < 0)  /* failed initialiser */
+    {
+        free (stream->ops);
+        stream->ops = NULL;
+    }
+}
+
+struct codec_ops passthru_ptks_ops =
+{
+    NULL,
+    send_vorbis_packet,
+    NULL
+};
+

Modified: icecast/branches/kh/ices/src/stream.h
===================================================================
--- icecast/trunk/ices/src/stream.h	2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/stream.h	2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* stream.h
* - Core streaming functions/main loop.
*
- * $Id: stream.h,v 1.4 2003/03/22 01:14:35 karl Exp $
+ * $Id: stream_shared.h,v 1.3 2001/09/25 12:04:22 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -11,36 +11,29 @@
* with this source.
*/

-
#ifndef __STREAM_H
#define __STREAM_H

-#include <shout/shout.h>
+#include "runner.h"

-#include "thread/thread.h"
-#include "cfgparse.h"
+struct codec_ops
+{
+    void *data;
+    int  (*process_buffer)(struct instance *stream, input_buffer *buffer);
+    void (*flush_data)(struct instance *stream);
+};

-typedef struct {
-    unsigned char *buf;
-    long len;
-    int count;
-    int critical;
-    long aux_data;
-} ref_buffer;
+extern struct codec_ops passthru_ptks_ops;
+extern struct codec_ops no_encoding_ops;
+extern struct codec_ops reencode_ops;
+extern struct codec_ops encode_ops;
+extern struct codec_ops reencode_new_ops;
+extern struct codec_ops reencode_packet_ops;

-typedef struct _queue_item {
-    ref_buffer *buf;
-    struct _queue_item *next;
-} queue_item;

-typedef struct buffer_queue {
-    queue_item *head, *tail;
-    int length;
-    mutex_t lock;
-} buffer_queue;
+void process_critical (struct instance *stream, input_buffer *buffer);
+void output_clear(struct output_state *state);

-void *ices_instance_stream(void *arg);
-void *savefile_stream(void *arg);
-
#endif

+



More information about the commits mailing list