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

j at dactyl.lonelymoon.com j
Fri Jun 25 13:48:38 PDT 2004


ices-kh/src
Message-ID: <20040625204838.1B0FC9AAAB at dactyl.lonelymoon.com>

Author: j
Date: Fri Jun 25 13:48:38 2004
New Revision: 6845

Added:
icecast/branches/ices-kh/
icecast/branches/ices-kh/AUTHORS
icecast/branches/ices-kh/COPYING
icecast/branches/ices-kh/Makefile.am
icecast/branches/ices-kh/NEWS
icecast/branches/ices-kh/README
icecast/branches/ices-kh/TODO
icecast/branches/ices-kh/autogen.sh
icecast/branches/ices-kh/conf/
icecast/branches/ices-kh/conf/Makefile.am
icecast/branches/ices-kh/conf/ices-fixit.xml
icecast/branches/ices-kh/conf/ices-live.xml
icecast/branches/ices-kh/conf/ices-pcm.xml
icecast/branches/ices-kh/conf/ices-pcm2.xml
icecast/branches/ices-kh/conf/ices-playlist.xml
icecast/branches/ices-kh/conf/ices-switch.xml
icecast/branches/ices-kh/configure.in
icecast/branches/ices-kh/src/
icecast/branches/ices-kh/src/Makefile.am
icecast/branches/ices-kh/src/audio.c
icecast/branches/ices-kh/src/audio.h
icecast/branches/ices-kh/src/cfgparse.c
icecast/branches/ices-kh/src/cfgparse.h
icecast/branches/ices-kh/src/encode.c
icecast/branches/ices-kh/src/encode.h
icecast/branches/ices-kh/src/ices.c
icecast/branches/ices-kh/src/im_alsa.c
icecast/branches/ices-kh/src/im_alsa.h
icecast/branches/ices-kh/src/im_jack.c
icecast/branches/ices-kh/src/im_jack.h
icecast/branches/ices-kh/src/im_oss.c
icecast/branches/ices-kh/src/im_oss.h
icecast/branches/ices-kh/src/im_pcm.c
icecast/branches/ices-kh/src/im_pcm.h
icecast/branches/ices-kh/src/im_playlist.c
icecast/branches/ices-kh/src/im_playlist.h
icecast/branches/ices-kh/src/im_sun.c
icecast/branches/ices-kh/src/im_sun.h
icecast/branches/ices-kh/src/input.c
icecast/branches/ices-kh/src/inputmodule.h
icecast/branches/ices-kh/src/logging.h
icecast/branches/ices-kh/src/metadata.c
icecast/branches/ices-kh/src/metadata.h
icecast/branches/ices-kh/src/om_file.c
icecast/branches/ices-kh/src/om_file.h
icecast/branches/ices-kh/src/om_shout.c
icecast/branches/ices-kh/src/om_shout.h
icecast/branches/ices-kh/src/playlist_basic.c
icecast/branches/ices-kh/src/playlist_basic.h
icecast/branches/ices-kh/src/playlist_script.c
icecast/branches/ices-kh/src/reencode.c
icecast/branches/ices-kh/src/reencode.h
icecast/branches/ices-kh/src/resample.c
icecast/branches/ices-kh/src/resample.h
icecast/branches/ices-kh/src/runner.c
icecast/branches/ices-kh/src/runner.h
icecast/branches/ices-kh/src/signals.c
icecast/branches/ices-kh/src/signals.h
icecast/branches/ices-kh/src/stream.c
icecast/branches/ices-kh/src/stream.h
Log:
import of ices-kh

Added: icecast/branches/ices-kh/AUTHORS
===================================================================
--- icecast/branches/ices-kh/AUTHORS	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/AUTHORS	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1 @@
+Karl Heyes      <karl at xiph.org>

Added: icecast/branches/ices-kh/COPYING
===================================================================
--- icecast/branches/ices-kh/COPYING	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/COPYING	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

Added: icecast/branches/ices-kh/Makefile.am
===================================================================
--- icecast/branches/ices-kh/Makefile.am	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/Makefile.am	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,17 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = 1.6 foreign dist-bzip2
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = src conf
+
+EXTRA_DIST = README AUTHORS COPYING TODO NEWS m4
+
+# SCCS Definitions (for BitKeeper)
+GET = true
+
+debug:
+	$(MAKE) all CFLAGS="@DEBUG@"
+
+profile:
+	$(MAKE) all CFLAGS="@PROFILE@"

Added: icecast/branches/ices-kh/NEWS
===================================================================
--- icecast/branches/ices-kh/NEWS	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/NEWS	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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
+

Added: icecast/branches/ices-kh/README
===================================================================
--- icecast/branches/ices-kh/README	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/README	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,3 @@
+this is ices 2.0
+
+it's the primary source client for icecast 2.0

Added: icecast/branches/ices-kh/TODO
===================================================================
--- icecast/branches/ices-kh/TODO	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/TODO	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,34 @@
+. 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.
+
+

Added: icecast/branches/ices-kh/autogen.sh
===================================================================
--- icecast/branches/ices-kh/autogen.sh	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/autogen.sh	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,113 @@
+#!/bin/sh
+# Run this to set up the build system: configure, makefiles, etc.
+# (based on the version in enlightenment's cvs)
+
+package="ices"
+
+olddir=`pwd`
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+cd "$srcdir"
+DIE=0
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have autoconf installed to compile $package."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+        DIE=1
+}
+VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9]\).*/\1/"
+
+# do we need automake?
+if test -r Makefile.am; then
+    echo Checking for automake version
+    options=`fgrep AUTOMAKE_OPTIONS Makefile.am`
+    AM_NEEDED=`echo "$options" | $VERSIONGREP`
+    AM_PROGS=automake
+    AC_PROGS=aclocal
+    if test -n "$AM_NEEDED" && test "x$AM_NEEDED" != "x$options"
+    then
+        AM_PROGS="automake-$AM_NEEDED automake$AM_NEEDED $AM_PROGS"
+        AC_PROGS="aclocal-$AM_NEEDED aclocal$AM_NEEDED $AC_PROGS"
+    else
+        AM_NEEDED=""
+    fi
+    AM_PROGS="$AUTOMAKE $AM_PROGS"
+    AC_PROGS="$ACLOCAL $AC_PROGS"
+    for am in $AM_PROGS; do
+      ($am --version > /dev/null 2>&1) 2>/dev/null || continue
+      ver=`$am --version | head -1 | $VERSIONGREP`
+      AWK_RES=`echo $ver $AM_NEEDED | awk '{ if ( $1 >= $2 ) print "yes"; else print "no" }'`
+      if test "$AWK_RES" = "yes"; then
+        AUTOMAKE=$am
+        echo "  found $AUTOMAKE"
+        break
+      fi
+    done
+    for ac in $AC_PROGS; do
+      ($ac --version > /dev/null 2>&1) 2>/dev/null || continue
+      ver=`$ac --version < /dev/null | head -1 | $VERSIONGREP`
+      AWK_RES=`echo $ver $AM_NEEDED | awk '{ if ( $1 >= $2 ) print "yes"; else print "no" }'`
+      if test "$AWK_RES" = "yes"; then
+        ACLOCAL=$ac
+        echo "  found $ACLOCAL"
+        break
+      fi
+    done
+    test -z $AUTOMAKE || test -z $ACLOCAL && {
+        echo
+        if test -n "$AM_NEEDED"; then
+            echo "You must have automake version $AM_NEEDED installed"
+            echo "to compile $package."
+        else
+            echo "You must have automake installed to compile $package."
+        fi
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+        DIE=1
+      }
+fi
+
+(libtoolize --version)  > /dev/null 2>&1 || {
+	echo
+	echo "You must have libtool installed to compile $package."
+	echo "Download the appropriate package for your system,"
+	echo "or get the source from one of the GNU ftp sites"
+	echo "listed in http://www.gnu.org/order/ftp.html"
+	DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+echo "Generating configuration files for $package, please wait...."
+
+ACLOCAL_FLAGS="$ACLOCAL_FLAGS -I m4"
+if test -n "$ACLOCAL"; then
+  echo "  $ACLOCAL $ACLOCAL_FLAGS"
+  $ACLOCAL $ACLOCAL_FLAGS
+fi
+
+echo "  autoheader"
+autoheader
+
+echo "  libtoolize --automake"
+libtoolize --automake
+
+if test -n "$AUTOMAKE"; then
+  echo "  $AUTOMAKE --add-missing"
+  $AUTOMAKE --add-missing
+fi
+
+echo "  autoconf"
+autoconf
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+cd $olddir
+$srcdir/configure "$@" && echo


Property changes on: icecast/branches/ices-kh/autogen.sh
___________________________________________________________________
Name: svn:executable
+

Added: icecast/branches/ices-kh/conf/Makefile.am
===================================================================
--- icecast/branches/ices-kh/conf/Makefile.am	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/conf/Makefile.am	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,6 @@
+## Process this with automake to create Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+EXTRA_DIST = ices-live.xml ices-playlist.xml ices-pcm.xml ices-pcm2.xml ices-switch.xml ices-fixit.xml
+

Added: icecast/branches/ices-kh/conf/ices-fixit.xml
===================================================================
--- icecast/branches/ices-kh/conf/ices-fixit.xml	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/conf/ices-fixit.xml	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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/ices-kh/conf/ices-live.xml
===================================================================
--- icecast/branches/ices-kh/conf/ices-live.xml	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/conf/ices-live.xml	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,114 @@
+<?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 '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 -->
+
+        <!-- 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>0</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>0</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>
+

Added: icecast/branches/ices-kh/conf/ices-pcm.xml
===================================================================
--- icecast/branches/ices-kh/conf/ices-pcm.xml	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/conf/ices-pcm.xml	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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/ices-kh/conf/ices-pcm2.xml
===================================================================
--- icecast/branches/ices-kh/conf/ices-pcm2.xml	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/conf/ices-pcm2.xml	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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>

Added: icecast/branches/ices-kh/conf/ices-playlist.xml
===================================================================
--- icecast/branches/ices-kh/conf/ices-playlist.xml	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/conf/ices-playlist.xml	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,47 @@
+<?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> <!-- 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>
+
+		<!-- 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>
+
+        <runner>
+            <!-- Stream instance -->
+            <instance>
+                <shout>
+                    <!-- Server details: -->
+                    <hostname>localhost</hostname>
+                    <port>8000</port>
+                    <password>hackme</password>
+                    <mount>/example1.ogg</mount>
+
+                    <reconnectdelay>2</reconnectdelay>
+                    <reconnectattempts>5</reconnectattempts>
+                </shout>
+
+                <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/ices-kh/conf/ices-switch.xml
===================================================================
--- icecast/branches/ices-kh/conf/ices-switch.xml	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/conf/ices-switch.xml	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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>

Added: icecast/branches/ices-kh/configure.in
===================================================================
--- icecast/branches/ices-kh/configure.in	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/configure.in	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,187 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT([IceS], [2.0-kh59], [karl at xiph.org])
+AC_PREREQ(2.54)
+AC_CONFIG_SRCDIR(src/ices.c)
+
+AM_INIT_AUTOMAKE
+AM_CONFIG_HEADER(config.h)
+AM_MAINTAINER_MODE
+
+AC_PROG_CC
+AC_PROG_LIBTOOL
+XIPH_C__FUNC__
+
+dnl Set some options based on environment
+
+case "$host" in
+   *bsd*|*irix*)
+   ;;
+   *) AC_DEFINE(_XOPEN_SOURCE, 600, [Define if you have POSIX and XPG specifications])
+   ;;
+esac
+case "$host" in
+*-*-irix*)
+        DEBUG="-g -signed"
+        XIPH_CFLAGS="-O2 -w -signed"
+        PROFILE="-p -g3 -O2 -signed"
+;;
+*-*-solaris*)
+        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"
+;;
+*)
+        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
+
+dnl Checks for programs.
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_HEADER_STDC
+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_AUDIO,test "$have_oss" = yes)
+if test "$have_oss" = yes; then
+    AC_DEFINE(HAVE_OSS_AUDIO,,[Define to enable OSS input module])
+fi
+
+dnl Check for Sun audio
+
+AC_C_BIGENDIAN
+AC_ARG_ENABLE(sun-audio,
+    AC_HELP_STRING([--disable-sun-audio],
+                   [Disable sun audio input (default autodetect)]),
+    enable_sun="$enableval",
+    enable_sun=yes
+)
+if test x$enable_sun = xyes; then
+   AC_CHECK_HEADER(sys/audioio.h, have_sun_audio=yes, have_sun_audio=no)
+
+   if test "$have_sun_audio" = yes; then
+       AC_DEFINE(HAVE_SUN_AUDIO,,[Define to enable sun audio input module])
+   fi
+fi
+AM_CONDITIONAL(HAVE_SUN_AUDIO,test "$have_sun_audio" = yes)
+
+dnl Check for ALSA audio
+
+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 "$ices_have_alsa" = yes; then
+   ALSA_LIBS="-lasound"
+   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
+
+dnl -- configure options --
+
+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], [-lvorbisenc $SHOUT_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(PTHREAD_CFLAGS)
+AC_SUBST(LIBS)
+AC_SUBST(LDFLAGS)
+
+AC_OUTPUT(Makefile conf/Makefile src/Makefile src/log/Makefile)

Added: icecast/branches/ices-kh/src/Makefile.am
===================================================================
--- icecast/branches/ices-kh/src/Makefile.am	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/Makefile.am	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,46 @@
+## Process this with automake to create Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+SUBDIRS = log
+
+bin_PROGRAMS = ices
+AM_CFLAGS = @XIPH_CFLAGS@
+AM_CPPFLAGS = @XIPH_CPPFLAGS@
+CFLAGS= -g -O20
+
+EXTRA_DIST = thread net timing avl
+EXTRA_ices_SOURCES = im_oss.c im_sun.c im_alsa.c
+
+if HAVE_OSS_AUDIO
+oss = im_oss.c
+endif
+
+if HAVE_SUN_AUDIO
+sun = im_sun.c
+endif
+
+if HAVE_ALSA_AUDIO
+alsa = im_alsa.c
+endif
+
+if HAVE_JACK
+jack = im_jack.c
+endif
+
+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)
+
+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@"
+
+profile:
+	$(MAKE) all CFLAGS="@PROFILE@"
+

Added: icecast/branches/ices-kh/src/audio.c
===================================================================
--- icecast/branches/ices-kh/src/audio.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/audio.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,237 @@
+/* audio.c
+ * stereo->mono downmixing
+ * resampling
+ *
+ * $Id: audio.c,v 1.6 2003/03/15 02:24:18 karl 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cfgparse.h"
+#include "audio.h"
+
+#include "resample.h"
+
+#define MODULE "audio/"
+#include "logging.h"
+
+struct downmix *downmix_initialise(void) {
+    struct downmix *state = calloc(1, sizeof(struct downmix));
+
+    LOG_INFO0("Enabling stereo->mono downmixing");
+
+    return state;
+}
+
+void downmix_clear(struct downmix *s) {
+    if(s) {
+        if (s->buffer)
+            free(s->buffer);
+        free(s);
+    }
+}
+
+void downmix_buffer_float(struct downmix *s, float **buf, int samples, unsigned channels)
+{
+    int i;
+
+    if(samples > s->buflen) {
+        void *tmp = realloc(s->buffer, samples * sizeof(float));
+        if (tmp==NULL)
+            return;
+        s->buffer = tmp;
+        s->buflen = samples;
+    }
+
+    for(i=0; i < samples; i++) {
+        unsigned count = 0;
+        float res = 0.0;
+        for (count = 0; count < channels; count++)
+            res += buf [count][i];
+        s->buffer[i] = res/channels;
+    }
+
+}
+
+
+#if 0
+void downmix_buffer(struct downmix *s, signed char *buf, int len, int be)
+{
+    int samples = len/4;
+    int i;
+
+    if(samples > s->buflen) {
+        void *tmp = realloc(s->buffer, samples * sizeof(float));
+        if (tmp==NULL)
+            return;
+        s->buffer = tmp;
+        s->buflen = samples;
+    }
+
+    if(be) {
+        for(i=0; i < samples; i++) {
+            s->buffer[i] = (((buf[4*i]<<8) | (buf[4*i + 1]&0xff)) +
+                           ((buf[4*i + 2]<<8) | (buf[4*i + 3]&0xff)))/65536.f;
+        }
+    }
+    else {
+        for(i=0; i < samples; i++) {
+            s->buffer[i] = (((buf[4*i + 1]<<8) | (buf[4*i]&0xff)) +
+                           ((buf[4*i + 3]<<8) | (buf[4*i + 2]&0xff)))/65536.f;
+        }
+    }
+}
+#endif
+
+struct resample *resample_initialise(int channels, int infreq, int outfreq)
+{
+    struct resample *state = calloc(1, sizeof(struct resample));
+    int failed = 1;
+
+    do
+    {
+        if (state==NULL)
+            break;
+        if (resampler_init(&state->resampler, channels, outfreq, infreq, RES_END)) {
+            LOG_ERROR0("Couldn't initialise resampler to specified frequency");
+            return NULL;
+        }
+
+        if ((state->buffers = calloc(channels, sizeof(float *))) == NULL)
+            break;
+        if ((state->convbuf = calloc(channels, sizeof(float *))) == NULL)
+            break;
+        failed = 0;
+    }
+    while (0); /* not a loop */
+
+    if (failed)
+    {
+        LOG_ERROR0("Couldn't initialise resampler due to memory allocation failure");
+        resample_clear (state);
+        return NULL;
+    }
+    state->channels = channels;
+
+    LOG_INFO3("Initialised resampler for %d channels, from %d Hz to %d Hz",
+            channels, infreq, outfreq);
+
+    return state;
+}
+
+void resample_clear(struct resample *s)
+{
+    int c;
+
+    if(s) {
+        if(s->buffers) {
+            for(c=0; c<s->channels; c++)
+                if (s->buffers[c])
+                    free(s->buffers[c]);
+            free(s->buffers);
+        }
+        if(s->convbuf) {
+            for(c=0; c<s->channels; c++)
+                if (s->convbuf[c])
+                    free(s->convbuf[c]);
+            free(s->convbuf);
+        }
+        resampler_clear(&s->resampler);
+        free(s);
+    }
+}
+
+void resample_buffer(struct resample *s, signed char *buf, int buflen, int be)
+{
+    int c,i;
+    buflen /= 2*s->channels; /* bytes -> samples conversion */
+
+    if(s->convbuflen < buflen) {
+        s->convbuflen = buflen;
+        for(c=0; c < s->channels; c++)
+            s->convbuf[c] = realloc(s->convbuf[c], buflen * sizeof(float));
+    }
+
+    if(be) {
+        for(i=0; i < buflen; i++) {
+            for(c=0; c < s->channels; c++) {
+                s->convbuf[c][i] = ((buf[2*(i*s->channels + c)]<<8) |
+                                    (0x00ff&(int)buf[2*(i*s->channels + c)+1]))/
+                    32768.f;
+            }
+        }
+    }
+    else {
+        for(i=0; i < buflen; i++) {
+            for(c=0; c < s->channels; c++) {
+                s->convbuf[c][i] = ((buf[2*(i*s->channels + c) + 1]<<8) |
+                                    (0x00ff&(int)buf[2*(i*s->channels + c)]))/
+                    32768.f;
+            }
+        }
+    }
+
+    resample_buffer_float(s, s->convbuf, buflen);
+}
+
+void resample_buffer_float(struct resample *s, float **buf, int buflen)
+{
+    int c;
+    int res;
+
+    s->buffill = resampler_push_check(&s->resampler, buflen);
+    if(s->buffill <= 0) {
+        LOG_ERROR1("Fatal reencoding error: resampler_push_check returned %d",
+                s->buffill);
+    }
+
+    if(s->bufsize < s->buffill) {
+        s->bufsize = s->buffill;
+        for(c=0; c<s->channels; c++)
+            s->buffers[c] = realloc(s->buffers[c], s->bufsize * sizeof(float));
+    }
+
+    if((res = resampler_push(&s->resampler, s->buffers, (float const **)buf, buflen))
+            != s->buffill) {
+        LOG_ERROR2("Internal error in resampling: returned number of samples %d"
+                   ", expected %d", res, s->buffill);
+        s->buffill = res;
+        return;
+    }
+
+}
+
+void resample_finish(struct resample *s)
+{
+    int ret;
+
+    if(!s->buffers[0])
+        return;
+
+    ret = resampler_drain(&s->resampler, s->buffers);
+
+    if(ret > s->bufsize) {
+        LOG_ERROR0("Fatal error in resampler: buffers too small");
+        return;
+    }
+
+    s->buffill = ret;
+}
+
+
+
+
+
+

Added: icecast/branches/ices-kh/src/audio.h
===================================================================
--- icecast/branches/ices-kh/src/audio.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/audio.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,51 @@
+/* audio.h
+ * - stereo->mono downmixing
+ * - resampling
+ *
+ * $Id: audio.h,v 1.3 2003/03/15 02:24:18 karl 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 __AUDIO_H
+#define __AUDIO_H
+
+#include "resample.h"
+
+struct downmix
+{
+    float *buffer;
+    int buflen;
+};
+
+struct resample
+{
+    struct resampler resampler;
+    int channels;
+
+    float **buffers;
+    int buffill;
+    int bufsize;
+
+    float **convbuf;
+    int convbuflen;
+};
+
+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);
+
+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
+

Added: icecast/branches/ices-kh/src/cfgparse.c
===================================================================
--- icecast/branches/ices-kh/src/cfgparse.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/cfgparse.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,440 @@
+/* cfgparse.c
+ * - setup file reading code, plus default settings.
+ *
+ * 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
+ * with this source.
+ */
+
+#ifdef HAVE_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 "cfgparse.h"
+#include "thread/thread.h"
+#include "inputmodule.h"
+#include "encode.h"
+#include "runner.h"
+#include "om_file.h"
+#include "om_shout.h"
+
+#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_BACKGROUND 0
+
+#ifdef DEBUG_CFG
+#define dprintf(...)      printf(__VA_ARGS__)
+#else
+#define dprintf(...)
+#endif
+
+/* this is the global config variable */
+config_t *ices_config;
+
+
+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;
+}
+
+
+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;
+}
+
+
+int get_xml_int (xmlNodePtr node, void *x)
+{
+    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;
+}
+
+
+int get_xml_string (xmlNodePtr node, void *x)
+{
+    char *str = (char *)xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
+    char *p = *(char**)x;
+    if (str == NULL)
+        return -1;
+    if (p)
+    {
+        dprintf ("freeing \"%s\" (%p) alloc \"%s\"\n", p, p, str);
+        xmlMemoryDump();
+        xmlFree (p);
+    }
+    *(char **)x = str;
+    xmlMemoryDump();
+    // xmlMemoryStrdup (str);
+    return 0;
+}
+
+
+int parse_xml_tags (const char *id, xmlNodePtr node, const struct cfg_tag *args)
+{
+    int ret = 0;
+
+    for (; node != NULL && ret == 0; node = node->next)
+    {
+        const struct cfg_tag *argp;
+
+        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 _free_params(module_param_t *param)
+{
+	while (param != NULL)
+	{
+        module_param_t *next;
+
+		if (param->name) xmlFree(param->name);
+		if (param->value) xmlFree(param->value);
+		next = param->next;
+		free(param);
+		param = next;
+    }
+}
+
+
+static input_module_t *_free_input_module (input_module_t *mod)
+{
+    input_module_t *next = mod->next;
+
+    if (mod->module != DEFAULT_PLAYLIST_MODULE)
+        xmlFree (mod->module);
+
+    if (mod->module_params)
+        _free_params(mod->module_params);
+
+    free (mod);
+    return next;
+}
+
+
+
+static int _parse_input_param (xmlNodePtr node, void *arg)
+{
+    module_param_t *param, *p;
+    input_module_t *inp = arg;
+
+    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;
+        }
+        if (get_xml_string (node, &param->value) < 0)
+        {
+            xmlFree (param->name);
+            free (param);
+            return -1;
+        }
+        if (inp->module_params == NULL)
+            inp->module_params = param;
+        else
+        {
+            p = inp->module_params;
+            while (p->next != NULL) p = p->next;
+            p->next = param;
+        }
+        return 0;
+    }
+    return -1;
+}
+
+
+static int _known_module (xmlNodePtr node, void *arg)
+{
+    int current_module = 0, ret = -1;
+    input_module_t *inp = arg;
+
+    if (get_xml_string (node, &inp->module) < 0)
+        return -1;
+
+    while (modules[current_module].name)
+    {
+        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++;
+    }
+    return ret;
+}
+
+static int _parse_input(xmlNodePtr node, void *arg)
+{
+    config_t *config = arg;
+    int save = 1;
+    input_module_t *mod = calloc (1, sizeof (input_module_t));
+    static unsigned input_id = 0;
+
+    while (mod)
+    {
+        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;
+        }
+        return 0;
+    }
+    if (mod)  free (mod);
+    return -1;
+}
+
+
+
+
+static int _parse_stream (xmlNodePtr node, void *arg)
+{
+    config_t *config = arg;
+    struct cfg_tag stream_tag[] =
+    {
+        { "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 }
+    };
+
+    return parse_xml_tags ("stream", node->xmlChildrenNode, stream_tag);
+}
+
+
+static int _parse_root (xmlNodePtr node, void *arg)
+{
+    config_t *config = arg;
+    char *user = NULL;
+
+    if (config && node && strcmp((char*)node->name, "ices") == 0)
+    {
+        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 config_t *allocate_config(void)
+{
+	config_t *c = (config_t *)calloc(1, sizeof(config_t));
+
+    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);
+
+    return c;
+}
+
+void config_initialize(void)
+{
+	srand(time(NULL));
+}
+
+void config_shutdown(void)
+{
+    struct runner *run;
+    input_module_t *mod;
+
+	if (ices_config == NULL) return;
+
+    mod = ices_config->inputs;
+    while (mod)
+    {
+        mod->shutdown_module (mod);
+        mod = _free_input_module (mod);
+    }
+
+    if (ices_config->logpath != DEFAULT_LOGPATH)
+        xmlFree (ices_config->logpath);
+
+    if (ices_config->logfile != DEFAULT_LOGFILE)
+        xmlFree (ices_config->logfile);
+
+    if (ices_config->stream_name != DEFAULT_STREAM_NAME)
+        xmlFree (ices_config->stream_name);
+
+    if (ices_config->stream_genre !=  DEFAULT_STREAM_GENRE)
+        xmlFree (ices_config->stream_genre);
+
+    if (ices_config->stream_description != DEFAULT_STREAM_DESCRIPTION)
+        xmlFree (ices_config->stream_description);
+
+    if (ices_config->user)
+        xmlFree (ices_config->user);
+
+    run = ices_config->runners;
+    while (run)
+        run = config_free_runner (run);
+
+	free(ices_config);
+	ices_config = NULL;
+}
+
+int config_read(const char *fn)
+{
+    xmlDocPtr doc = NULL;
+    int ret = 0;
+    uid_t euid = geteuid();
+
+#ifdef _POSIX_SAVED_IDS
+    if (seteuid (getuid()) < 0)
+#else
+    uid_t ruid = getuid();
+    if (setreuid (euid, ruid) < 0)
+#endif
+    {
+        fprintf (stderr, "failed to drop priviledges for reading config file\n");
+    }
+    xmlInitParser ();
+    doc = xmlParseFile(fn);
+    xmlCleanupParser ();
+
+    ices_config = allocate_config();
+    ices_config->cfgfile = fn;
+    if (_parse_root (xmlDocGetRootElement (doc), ices_config))
+        ret = -1;
+
+    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;
+}
+
+

Added: icecast/branches/ices-kh/src/cfgparse.h
===================================================================
--- icecast/branches/ices-kh/src/cfgparse.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/cfgparse.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,155 @@
+/* cfgparse.h
+ * - setup, and global structures built from setup
+ *
+ * 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
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifndef __CFGPARSE_H__
+#define __CFGPARSE_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"
+
+extern int realtime_enabled;
+
+struct cfg_tag
+{
+    const char *name;
+    int (*retrieve) (xmlNodePtr node, void *x);
+    void *storage;
+};
+
+
+
+struct output_module
+{
+    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;
+};
+
+
+
+struct output_state
+{
+    struct output_module *head;
+
+    long serial;
+    int in_use;
+    int headers;
+    ogg_packet packets[3];
+    int new_headers;
+    ogg_int64_t granules, start_pos;
+    unsigned granule_overlap;
+
+    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  realtime;
+    char *pidfile;
+    char *logpath;
+    char *logfile;
+    int  loglevel;
+    unsigned logsize;
+    int  log_stderr;
+    char *user;
+    const char *cfgfile;
+
+    /* <metadata> */
+    char *stream_name;
+    char *stream_genre;
+    char *stream_description;
+    char *stream_url;
+
+    input_module_t *inputs;
+
+    unsigned runner_count;
+    struct runner *runners;
+
+    /* private */
+    int log_id;
+    int shutdown;
+    int next_track;
+    int has_encoding;
+    int input_once_thru;
+
+    struct _config_tag *next;
+};
+
+
+
+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);
+void config_shutdown(void);
+
+int config_read(const char *filename);
+void config_dump(void);
+
+
+#endif /* __CFGPARSE_H__ */
+
+
+
+
+

Added: icecast/branches/ices-kh/src/encode.c
===================================================================
--- icecast/branches/ices-kh/src/encode.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/encode.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,262 @@
+/* encode.c
+ * - runtime encoding of PCM data.
+ *
+ * $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
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisenc.h>
+
+#include "cfgparse.h"
+#include "encode.h"
+
+#define MODULE "encode/"
+#include "logging.h"
+
+
+struct encoder
+{
+    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];
+};
+
+
+
+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);
+    }
+}
+
+
+struct encoder *encode_create (void)
+{
+    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)
+    {
+        vorbis_comment_add (&s->vc, str);
+    }
+}
+
+
+int encode_setup (struct encoder *s, struct encoder_settings *settings)
+{
+    float quality;
+    long nom_br, max_br, min_br, rate, channels;
+
+    /* 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;
+    }
+
+    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)
+            settings->managed = 1;
+
+    quality = settings->quality;
+
+    /* Have vorbisenc choose a mode for us */
+    vorbis_info_init (&s->vi);
+
+    do
+    {
+        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;
+        }
+        else
+        {
+            if (nom_br < 0)
+            {
+                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_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))
+                    break;
+            }
+            else
+            {
+                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))
+                    break;
+                if (vorbis_encode_ctl (&s->vi, OV_ECTL_RATEMANAGE_SET, NULL))
+                    break;
+            }
+        }
+
+        if (vorbis_encode_setup_init (&s->vi))
+            break;
+
+        vorbis_analysis_init (&s->vd, &s->vi);
+        vorbis_block_init (&s->vd, &s->vb);
+
+        vorbis_comment_add (&s->vc, "EncodedBy=" PACKAGE_STRING);
+        vorbis_analysis_headerout (&s->vd, &s->vc, &s->headers[0],&s->headers[1],&s->headers[2]);
+
+        s->in_header = 3;
+        s->samplerate = settings->samplerate;
+        s->in_use = 1;
+
+        return 0;
+    } while (0);
+
+    LOG_INFO0("Failed to configure encoder, verify settings");
+    vorbis_info_clear(&s->vi);
+
+    return -1;
+}
+
+
+void encode_data_float(struct encoder *s, float **pcm, size_t samples)
+{
+    float **buf, **src, **dest;
+    int i;
+    unsigned size;
+
+    if (samples == 0)
+    {
+        LOG_DEBUG0 ("request for encoding 0 samples");
+        return;
+    }
+    if (s->magic != 1) printf ("structure has gone bad\n");
+
+    buf = vorbis_analysis_buffer(&s->vd, samples);
+
+    i=s->vi.channels;
+    src = pcm;
+    dest = buf;
+    size = samples*sizeof(float);
+    for(; i ; i--)
+    {
+        memcpy(*dest, *src, size);
+        dest++;
+        src++;
+    }
+
+    vorbis_analysis_wrote(&s->vd, samples);
+}
+
+
+
+int encode_packetout(struct encoder *s, ogg_packet *op)
+{
+    if (s->in_header)
+    {
+        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;
+    }
+    while (vorbis_bitrate_flushpacket (&s->vd, op) == 0)
+    {
+        if (vorbis_analysis_blockout (&s->vd, &s->vb) == 0)
+            return 0;
+
+        vorbis_analysis (&s->vb, NULL);
+        vorbis_bitrate_addblock (&s->vb);
+    }
+    return 1;
+}
+
+
+int encode_endstream (struct encoder *s)
+{
+    vorbis_analysis_wrote(&s->vd, 0);
+    return 1;
+}
+
+
+int parse_encode (xmlNodePtr node, void *x)
+{
+    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 }
+    };
+
+    enc->min_br = -1;
+    enc->max_br = -1;
+    enc->nom_br = -1;
+
+    return parse_xml_tags ("encode", node->xmlChildrenNode, encode_tags);
+}
+
+

Added: icecast/branches/ices-kh/src/encode.h
===================================================================
--- icecast/branches/ices-kh/src/encode.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/encode.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,63 @@
+/* encode.h
+ * - encoding functions
+ *
+ * $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
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifndef __ENCODE_H
+#define __ENCODE_H
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+
+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
+

Added: icecast/branches/ices-kh/src/ices.c
===================================================================
--- icecast/branches/ices-kh/src/ices.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/ices.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,198 @@
+/* ices.c
+ * - Main startup, thread launching, and cleanup code.
+ *
+ * $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>
+ *
+ * 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 <unistd.h>
+#include <pwd.h>
+#include <sys/types.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 "inputmodule.h"
+
+#define MODULE "ices-core/"
+#include "logging.h"
+
+void start_processing ()
+{
+    thread_type *thread;
+
+    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)
+    {
+        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;
+    }
+
+    if (ices_config->user)
+        pw = getpwnam (ices_config->user);
+
+    if (pw)
+    {
+        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;
+        /* Start up new session, to lose old session and process group */
+        switch (fork())
+        {
+        case 0: break; /* child continues */
+        case -1: perror ("fork"); ret = -1;
+        default:
+            exit (ret);
+        }
+
+        /* Disassociate process group and controlling terminal */
+        setsid();
+
+        /* Become a NON-session leader so that a */
+        /* control terminal can't be reacquired */
+        switch (fork())
+        {
+        case 0: break; /* child continues */
+        case -1: perror ("fork"); ret = -1;
+        default:
+            exit (ret);
+        }
+    }
+
+	log_initialize();
+    thread_initialize();
+    shout_init();
+	signals_setup();
+    drop_priviledges();
+
+	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);
+        if (log < 0)
+            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);
+
+	ices_config->log_id = log;
+
+	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");
+        if (f)
+        {
+            fprintf (f, "%i", getpid());
+            fclose (f);
+        }
+    }
+
+    start_processing ();
+
+	LOG_INFO0("Shutdown in progress");
+
+    if (ices_config->pidfile)
+        remove (ices_config->pidfile);
+
+	log_close(log);
+
+ fail:
+    shout_shutdown();
+	config_shutdown();
+	thread_shutdown();
+	log_shutdown();
+
+	return 0;
+}
+
+

Added: icecast/branches/ices-kh/src/im_alsa.c
===================================================================
--- icecast/branches/ices-kh/src/im_alsa.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_alsa.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,319 @@
+/* im_alsa.c
+ * - Raw PCM input from ALSA devices
+ *
+ * $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...
+ * 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>
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include "cfgparse.h"
+#include "metadata.h"
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#include "im_alsa.h"
+
+#define MODULE "input-alsa/"
+#include "logging.h"
+
+#define SAMPLES 8192
+
+
+static int alsa_initialise_buffer (input_module_t *mod, input_buffer *ib)
+{
+	im_alsa_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 alsa_close_module(input_module_t *mod)
+{
+	if (mod)
+	{
+		if (mod->internal)
+		{
+			im_alsa_state *s = mod->internal;
+			if (s->fd != NULL)
+				snd_pcm_close(s->fd);
+			free(s);
+		}
+	}
+}
+
+
+void alsa_shutdown_module (input_module_t *mod)
+{
+    im_alsa_state *s = mod->internal;
+
+    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.
+ *
+ */
+static int alsa_read(input_module_t *mod)
+{
+	int result;
+	im_alsa_state *s = mod->internal;
+    int dead_air;
+    input_buffer *ib;
+
+    while (1)
+    {
+        if (s->user_terminated)
+        {
+            s->user_terminated = 0;
+            return 0;
+        }
+
+        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;
+            }
+        }
+
+        result = snd_pcm_readi (s->fd, s->read_buffer, s->samples);
+
+        /* 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 ();
+    }
+
+	return 0;
+}
+
+int alsa_init_module(input_module_t *mod)
+{
+	im_alsa_state *s;
+	module_param_t *current;
+	char *device = "plughw:0,0"; /* default device */
+	int channels, rate;
+    unsigned int samples;
+
+    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;
+
+	mod->internal = calloc(1, sizeof(im_alsa_state));
+    if (mod->internal == NULL)
+        return -1;
+	s = mod->internal;
+
+	s->fd = NULL; /* Set it to something invalid, for now */
+	rate = 44100; /* Defaults */
+	channels = 2;
+    samples = SAMPLES;
+    s->periods = 2;
+    s->buffer_time = 500000;
+
+	current = mod->module_params;
+
+	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);
+
+		current = current->next;
+	}
+    s->rate = rate;
+    s->channels = channels;
+    s->samples = samples;
+    s->device = device;
+
+    s->read_buffer_len = s->samples*2*s->channels;
+    s->read_buffer = malloc (s->read_buffer_len);
+
+    LOG_INFO0("ALSA driver initialised");
+
+    return 0;
+}
+
+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;
+
+	snd_pcm_hw_params_alloca(&hwparams);
+
+	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)
+    {
+        LOG_ERROR2("Could not set sample rate to %d: %s", exact_rate, snd_strerror(err));
+        goto fail;
+    }
+    if (exact_rate != s->rate)
+    {
+        LOG_WARN2("samplerate %d Hz not supported by your hardware try using "
+                "%d instead", s->rate, exact_rate);
+        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));
+        goto fail;
+    }
+    if ((err = snd_pcm_hw_params_set_periods(s->fd, hwparams, s->periods, 0)) < 0)
+    {
+        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;
+	}
+    s->newtrack = 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 0;
+
+fail:
+	alsa_shutdown_module(mod); /* safe, this checks for valid contents */
+	return -1;
+}
+
+

Added: icecast/branches/ices-kh/src/im_alsa.h
===================================================================
--- icecast/branches/ices-kh/src/im_alsa.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_alsa.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,57 @@
+/* im_alsa.h
+ * - read pcm data from oss devices
+ *
+ * $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...
+ * 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_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 "inputmodule.h"
+
+typedef struct
+{
+	int rate;
+	int channels;
+    unsigned buffer_time;
+    unsigned periods;
+
+	snd_pcm_t *fd;
+    const char *device;
+	int newtrack;
+    int user_terminated;
+    int samples;
+    void *read_buffer;
+    unsigned read_buffer_len;
+} im_alsa_state;
+
+
+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/ices-kh/src/im_jack.c
===================================================================
--- icecast/branches/ices-kh/src/im_jack.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_jack.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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/ices-kh/src/im_jack.h
===================================================================
--- icecast/branches/ices-kh/src/im_jack.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_jack.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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__ */

Added: icecast/branches/ices-kh/src/im_oss.c
===================================================================
--- icecast/branches/ices-kh/src/im_oss.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_oss.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,342 @@
+/* im_oss.c
+ * - Raw PCM input from OSS devices
+ *
+ * $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>
+ *
+ * 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.
+
+
+ * Modified to work with non-blocking ices.
+
+ */
+
+#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 "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 SAMPLES    8192
+
+static void oss_return_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
+{
+    ib->critical = 0;
+    ib->eos = 0;
+    return;
+}
+
+
+static void oss_free_buffer (input_module_t *mod, input_buffer *ib)
+{
+    im_oss_state *s = mod->internal;
+    float **ptr;
+    int i;
+
+    ptr = ib->buf;
+    for (i=s->channels; i; i--)
+    {
+        if (ptr)
+        {
+            free (*ptr);
+            *ptr = NULL;
+        }
+        ptr++;
+    }
+    free (ib->buf);
+    ib->buf = NULL;
+}
+
+
+static int oss_initialise_buffer (input_module_t *mod, input_buffer *ib)
+{
+    im_oss_state *s = mod->internal;
+    float **ptr;
+    int i;
+
+    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--)
+    {
+        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;
+}
+
+
+/* Core streaming function for this module
+ * This is what actually produces the data which gets streamed.
+ *
+ */
+static int oss_read(input_module_t *mod)
+{
+	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;
+        }
+
+        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;
+            }
+        }
+
+        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 ((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;
+}
+
+
+
+void oss_close_module (input_module_t *mod)
+{
+	im_oss_state *s = mod->internal;
+
+    LOG_INFO0("Closing OSS module");
+    if (s->fd > -1)
+    {
+       close (s->fd);
+       s->fd = -1;
+    }
+}
+
+void oss_shutdown_module (input_module_t *mod)
+{
+	im_oss_state *s = mod->internal;
+
+    LOG_INFO0 ("Shutdown OSS module");
+    free (s);
+    mod->internal = NULL;
+}
+
+
+
+int oss_init_module(input_module_t *mod)
+{
+    im_oss_state *s;
+    module_param_t *current;
+
+    mod->name = "OSS";
+    mod->type = ICES_INPUT_PCM;
+    mod->getdata = oss_read;
+    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));
+    if (mod->internal == NULL)
+        return -1;
+
+	s = mod->internal;
+
+	s->fd = -1; /* Set it to something invalid, for now */
+	s->samplerate = 44100; /* Defaults */
+	s->channels = 2;
+
+	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, "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);
+
+		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);
+
+    LOG_INFO0 ("Module OSS initialised");
+    return 0;
+}
+
+
+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;
+    rate = s->samplerate;
+
+    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_ERROR0 ("Cannot get file state");
+        goto fail;
+    }
+    flags &= ~O_NONBLOCK;
+    if (fcntl(s->fd, F_SETFL, flags) == -1)
+    {
+        LOG_ERROR0 ("Cannot set dsp file state");
+        goto fail;
+    }
+	/* 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",
+				s->devicename, strerror(errno));
+		goto fail;
+	}
+	if(format != AFMT_S16_LE)
+	{
+		LOG_ERROR0("Couldn't set sample format to AFMT_S16_LE");
+		goto fail;
+	}
+
+	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;
+	}
+
+	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:
+	oss_close_module(mod); /* safe, this checks for valid contents */
+	return -1;
+}
+
+
+
+

Added: icecast/branches/ices-kh/src/im_oss.h
===================================================================
--- icecast/branches/ices-kh/src/im_oss.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_oss.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,43 @@
+/* im_oss.h
+ * - read pcm data from oss devices
+ *
+ * $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>
+ *
+ * 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_OSS_H__
+#define __IM_OSS_H__
+
+#include "inputmodule.h"
+#include <ogg/ogg.h>
+
+typedef struct
+{
+	int channels;
+	int samplerate;
+	int aux_data;
+    int samples;
+    int default_len;
+    int user_terminated;
+
+	int fd;
+    char *devicename;
+	int newtrack;
+    void *read_buffer;
+    unsigned read_buffer_len;
+} im_oss_state;
+
+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/ices-kh/src/im_pcm.c
===================================================================
--- icecast/branches/ices-kh/src/im_pcm.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_pcm.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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/ices-kh/src/im_pcm.h
===================================================================
--- icecast/branches/ices-kh/src/im_pcm.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_pcm.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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__ */

Added: icecast/branches/ices-kh/src/im_playlist.c
===================================================================
--- icecast/branches/ices-kh/src/im_playlist.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_playlist.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,466 @@
+/* playlist.c
+ * - Basic playlist functionality
+ *
+ * $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
+ * 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 <thread/thread.h>
+
+#include "cfgparse.h"
+#include "stream.h"
+
+#include "inputmodule.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 OGG_BUFSIZE 4096
+#define ALLOCATION_DELAY 500*1000  /* uS */
+
+typedef struct _playlist_module
+{
+    char *name;
+    int (*init)(module_param_t *, struct playlist_state *);
+} playlist_module_t;
+
+static playlist_module_t pmodules[] = {
+    { "basic", playlist_basic_initialise},
+    { "script", playlist_script_initialise},
+    {NULL,NULL}
+};
+
+static void playlist_clear_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
+{
+    if (ib)
+    {
+        ib->critical = 0;
+        if (ib->buf)
+        {
+            ogg_packet *op = ib->buf;
+            free (op->packet);
+            free (op);
+            ib->buf = NULL;
+        }
+    }
+}
+
+int playlist_open_module(input_module_t *mod)
+{
+    module_param_t *current;
+
+    LOG_INFO0("open playlist module");
+
+    current = mod->module_params;
+    while (current)
+    {
+        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;
+        }
+        current = current->next;
+    }
+    return -1;
+}
+
+
+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);
+    }
+}
+
+
+void playlist_close_module(input_module_t *mod)
+{
+    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);
+    }
+}
+
+void playlist_shutdown_module(input_module_t *mod)
+{
+    input_buffer *ib;
+
+    LOG_INFO0 ("Shutdown playlist module");
+
+    if (mod->internal)
+    {
+        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);
+    }
+}
+
+
+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)
+    {
+        if (pl->errors > 5)
+        {
+            LOG_WARN0("Too many consecutive errors - exiting");
+            return 0;
+        }
+
+        newfn = pl->get_filename(pl->data);
+        if (newfn == NULL)
+        {
+            LOG_DEBUG0 ("no filename returned");
+            return 0;  /* No more files available */
+        }
+
+        if (strcmp (newfn, "-"))
+        {
+            pl->current_file = fopen(newfn, "rb");
+            if (pl->current_file == NULL)
+            {
+                LOG_WARN2("Error opening file \"%s\": %s", newfn, strerror(errno));
+                pl->free_filename (pl->data, newfn);
+                pl->errors++;
+                continue;
+            }
+        }
+        else
+        {
+            pl->current_file = stdin;
+        }
+        pl->errors = 0;
+
+        break;
+    }
+    pl->free_filename(pl->data, pl->filename);
+    pl->filename = newfn;
+
+    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
+    {
+        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 (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)
+        {
+            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
+        {
+            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));
+}
+
+
+static void flush_file (input_module_t *mod)
+{
+    struct playlist_state *pl = (struct playlist_state *)mod->internal;
+
+    /* 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);
+}
+
+
+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)
+    {
+        ogg_packet packet;
+        int result;
+
+        while (ogg_stream_packetpeek (&pl->os, &packet) > 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 (1)
+                {
+                    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 ();
+                }
+                /* 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;
+            }
+            /* copy the packet */
+            pl->prev_packet = copy_ogg_packet (&packet);
+            ogg_stream_packetout (&pl->os, NULL);
+            if (pl->sleep)
+                input_sleep();
+        }
+        result = ogg_sync_pageout (&pl->oy, &page);
+        if (result < 0)
+        {
+            LOG_WARN1("Corrupt or missing data in stream from %s", pl->filename);
+            break;
+        }
+        if (result == 0)
+            break;
+        /* ok, we have a page, now do we initialise the stream */
+        if (ogg_page_bos (&page))
+        {
+            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);
+    }
+    return len;
+}
+
+
+/* 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))
+    {
+        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;
+}
+
+
+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;
+}
+
+

Added: icecast/branches/ices-kh/src/im_playlist.h
===================================================================
--- icecast/branches/ices-kh/src/im_playlist.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_playlist.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,56 @@
+/* im_playlist.h
+ * - Basic playlist functionality
+ *
+ * $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>
+ *
+ * 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_PLAYLIST_H__
+#define __IM_PLAYLIST_H__
+
+#include "inputmodule.h"
+#include <ogg/ogg.h>
+
+struct playlist_state
+{
+	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 */
+    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 */
+};
+
+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);
+
+
+#endif  /* __IM_PLAYLIST_H__ */

Added: icecast/branches/ices-kh/src/im_sun.c
===================================================================
--- icecast/branches/ices-kh/src/im_sun.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_sun.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,349 @@
+/* im_sun.c
+ * - Raw PCM input from Solaris audio devices
+ *
+ * $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 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
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#define SAMPLES    8192
+
+
+#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/audioio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+#include <fcntl.h>
+
+
+#include "thread/thread.h"
+#include "stream.h"
+#include "signals.h"
+#include "inputmodule.h"
+#include "metadata.h"
+
+#include "im_sun.h"
+
+#define MODULE "input-sun/"
+#include "logging.h"
+
+#define BUFSIZE 8192
+
+#define MAX_DEAD_AUDIO_BYTES 48000*2*2
+
+
+static void sun_return_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
+{
+    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 (ptr)
+        {
+            free (*ptr);
+            *ptr = NULL;
+        }
+        ptr++;
+    }
+    free (ib->buf);
+    ib->buf = NULL;
+}
+
+static int sun_initialise_buffer (input_module_t *mod, input_buffer *ib)
+{
+    im_sun_state *s = mod->internal;
+    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--)
+    {
+        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;
+}
+
+
+
+void sun_close_module (input_module_t *mod)
+{
+    im_sun_state *s = mod->internal;
+
+    LOG_INFO0("Closing Sun audio module");
+    if (s->fd > -1)
+    {
+       close (s->fd);
+       s->fd = -1;
+    }
+}
+
+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.
+ *
+ */
+static int sun_read(input_module_t *mod)
+{
+    im_sun_state *s = mod->internal;
+    input_buffer *ib;
+    int len;
+    int dead_air;
+
+    while (1)
+    {
+        if (s->user_terminated)
+        {
+            s->user_terminated = 0;
+            return 0;
+        }
+
+        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;
+        }
+
+        ib->samples = len/(ib->channels*2);
+
+#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);
+    }
+
+    return 0;
+}
+
+
+
+int sun_init_module(input_module_t *mod)
+{
+    im_sun_state *s;
+    module_param_t *current;
+    char *device = "/dev/audio"; /* default device */
+    int sample_rate = 44100;
+    int channels = 2;
+    int samples = SAMPLES;
+
+    mod->name = "Sun Audio";
+
+    mod->type = ICES_INPUT_PCM;
+    mod->subtype = INPUT_PCM_LE_16;
+    mod->getdata = sun_read;
+    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));
+    do
+    {
+        if (mod->internal == NULL)
+            break;
+        s = mod->internal;
+
+        s->fd = -1; /* Set it to something invalid, for now */
+
+        current = mod->module_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;
+        }
+
+        /* 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 */
+    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;
+        }
+
+        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);
+#endif
+#ifdef __OpenBSD__
+        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");
+            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",
+                s->device, channels, sample_rate);
+
+        s->newtrack = 1;
+        return 0;
+    }
+    while (0);
+
+    sun_close_module(mod); /* safe, this checks for valid contents */
+    return NULL;
+}
+
+

Added: icecast/branches/ices-kh/src/im_sun.h
===================================================================
--- icecast/branches/ices-kh/src/im_sun.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/im_sun.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,45 @@
+/* im_sun.h
+ * - read pcm data from sun devices
+ *
+ * $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...
+ * 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_SUN_H__
+#define __IM_SUN_H__
+
+#include <sys/audioio.h>
+#include "inputmodule.h"
+#include <ogg/ogg.h>
+
+typedef struct
+{
+	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;
+
+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__ */
+

Added: icecast/branches/ices-kh/src/input.c
===================================================================
--- icecast/branches/ices-kh/src/input.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/input.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,443 @@
+/* input.c
+ *  - Main producer control loop. Fetches data from input modules, and controls
+ *    submission of these to the runner threads. Timing control happens here.
+ *    originally based on the work by Michael Smith
+ *
+ * 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
+ * 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 <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 "runner.h"
+#include "thread/thread.h"
+#include "timing/timing.h"
+#include "cfgparse.h"
+#include "metadata.h"
+#include "inputmodule.h"
+#include "im_playlist.h"
+#include "im_pcm.h"
+#include "signals.h"
+
+#ifdef HAVE_OSS_AUDIO
+#include "im_oss.h"
+#endif
+
+#ifdef HAVE_ALSA_AUDIO
+#include "im_alsa.h"
+#endif
+
+#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
+#endif
+
+#define MODULE "input/"
+#include "logging.h"
+
+#define MAX_BUFFER_FAILURES 15
+
+char dead_audio [DEAD_AIR_BYTES];
+
+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_JACK
+	{ "jack",       jack_init_module,       jack_open_module,      jack_close_module,      jack_shutdown_module },
+#endif
+#ifdef HAVE_OSS_AUDIO
+	{ "oss",        oss_init_module,        oss_open_module,       oss_close_module,       oss_shutdown_module },
+#endif
+#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;
+
+void input_sleep (void)
+{
+    signed long sleep;
+
+    sleep = control.senttime - ((timing_get_time() - control.starttime) *1000);
+
+    if (sleep > 1000000)
+    {
+        LOG_WARN1 ("Sleeping for over 1 second (%ld), resetting timer", sleep);
+        control.starttime = timing_get_time();
+        control.senttime = 0;
+        sleep = 0;
+    }
+#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);
+    }
+}
+
+void input_adv_sleep (unsigned long adv)
+{
+    control.senttime += adv; /* in uS */
+    /* printf ("Adding %lu\n", adv); */
+}
+
+
+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++)
+    {
+        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;
+        }
+    }
+}
+
+
+
+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;
+        }
+    }
+}
+
+static int initialise_input_modules (void)
+{
+    input_module_t *mod;
+
+    mod = ices_config->inputs;
+    while (mod)
+    {
+        int i;
+        input_buffer *ib;
+
+        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--)
+        {
+            ib = calloc (1, sizeof (input_buffer));
+            if (ib == NULL)
+               return -1;
+            if (mod->initialise_buffer && mod->initialise_buffer (mod, ib) < 0)
+               return -1;
+
+            *mod->free_list_tail = ib;
+            mod->free_list_tail = &ib->next;
+        }
+        LOG_DEBUG4 ("Module %d (%s) has pre-allocated %d buffers out of %d",
+                mod->id, mod->name, mod->prealloc_count, mod->buffer_count);
+
+        mod = mod->next;
+    }
+
+    return 0;
+}
+
+
+void input_free_buffer(input_buffer *ib)
+{
+    input_module_t *mod;
+    if (ib == NULL)
+        return;
+
+    mod = ib->mod;
+
+    if (ib->serial != mod->expected)
+    {
+        LOG_DEBUG2("expected %lld, saw %lld", mod->expected, ib->serial);
+        mod->expected = ib->serial + 1;
+    }
+    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) */
+    {
+        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); */
+    }
+    else
+#endif
+    {
+        *mod->free_list_tail = ib;
+        mod->free_list_tail = &ib->next;
+    }
+
+    return;
+}
+
+input_buffer *input_alloc_buffer (input_module_t *mod)
+{
+    input_buffer *ib;
+
+    do
+    {
+        ib = mod->free_list;
+        if (ib == NULL || ib->next == NULL)
+        {
+            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); */
+        }
+        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);
+
+    ib->next = NULL;
+    ib->serial = mod->allotted_serial++;
+
+    return ib;
+}
+
+
+
+void process_input(input_module_t *mod)
+{
+	if (!mod)
+	{
+		LOG_ERROR0("NULL input module");
+		return;
+	}
+
+    move_to_next_input = 0;
+    control.samples = control.oldsamples = 0;
+
+    while (mod->getdata (mod))
+        ;
+}
+
+static input_module_t *open_next_input_module (input_module_t *mod)
+{
+    input_module_t *next_mod = mod;
+
+    if (ices_config->shutdown || mod == NULL)
+        return NULL;
+    do
+    {
+        LOG_DEBUG1 ("checking module %d", next_mod->id);
+        if (next_mod->failures < 10)
+        {
+            if (next_mod->open_module)
+            {
+                time_t start = time (NULL);
+
+                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;
+                }
+            }
+            else
+                next_mod->failures++;
+        }
+        else
+            LOG_WARN2 ("Too many failures on input module %d (%s)", next_mod->id, next_mod->name);
+
+        next_mod = next_mod->next;
+
+    } while (next_mod != mod && next_mod);
+
+    return NULL;
+}
+
+
+static void free_modules()
+{
+    input_module_t *mod = ices_config->inputs;
+    input_buffer *next, *ib;
+
+    LOG_DEBUG0 ("freeing up module storage");
+    while (mod)
+    {
+        ib = mod->free_list;
+        while (ib)
+        {
+            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;
+    }
+}
+
+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);
+}
+
+
+void *input_loop(void *arg)
+{
+    input_module_t *mod;
+    struct runner *r = ices_config->runners;
+
+    start_runners();
+    thread_sleep (300000);
+
+    if (initialise_input_modules () < 0)
+    {
+        printf ("Unable to initialise input modules\n");
+        return NULL;
+    }
+
+    /* start the clock */
+    control.starttime = timing_get_time();
+
+    mod = open_next_input_module (ices_config->inputs);
+
+    while (ices_config->shutdown == 0)
+    {
+        if (mod == NULL)
+            break;
+
+        process_input (mod);
+
+        if (mod->close_module)
+        {
+            LOG_INFO0("Closing input module");
+            mod->close_module (mod);
+        }
+
+        mod = mod->next;
+        if (mod == NULL)
+        {
+            if (ices_config->input_once_thru)
+            {
+               ices_config->shutdown = 1;
+               break;
+            }
+            else
+                mod = ices_config->inputs;
+        }
+
+        mod = open_next_input_module (mod);
+    }
+
+	LOG_DEBUG0("All input stopped, shutting down.");
+
+    runner_close (r);
+    free_modules();
+
+    return NULL;
+}
+

Added: icecast/branches/ices-kh/src/inputmodule.h
===================================================================
--- icecast/branches/ices-kh/src/inputmodule.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/inputmodule.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,162 @@
+/* inputmodule.h
+ * - the interface for input modules to implement.
+ *
+ * $Id: inputmodule.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 __INPUTMODULE_H__
+#define __INPUTMODULE_H__
+
+#include <vorbis/codec.h>
+
+#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;
+
+
+#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;
+    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 *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__ */
+

Added: icecast/branches/ices-kh/src/logging.h
===================================================================
--- icecast/branches/ices-kh/src/logging.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/logging.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,48 @@
+/* logging.h
+ * - macros used for logging. #define MODULE before including
+ *
+ * $Id: logging.h,v 1.5 2002/08/03 12:11:57 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 __LOGGING_H
+#define __LOGGING_H
+
+#include "log/log.h"
+
+#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_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_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)
+
+
+#endif /* __LOGGING_H */
+

Added: icecast/branches/ices-kh/src/metadata.c
===================================================================
--- icecast/branches/ices-kh/src/metadata.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/metadata.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,120 @@
+/* metadata.c
+ * - Metadata manipulation
+ *
+ * $Id: metadata.c,v 1.6 2002/07/20 12:52:06 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "cfgparse.h"
+#include "inputmodule.h"
+
+#define MODULE "metadata/"
+#include "logging.h"
+
+volatile int metadata_update_signalled = 0;
+
+
+void metadata_thread_signal(input_module_t *mod, input_buffer *buffer)
+{
+	static char line[1024];
+    char **md = NULL;
+    int comments = 0;
+    FILE *file;
+
+    metadata_update_signalled = 0;
+
+    if (mod->metadata_filename == NULL)
+        return;
+    file = fopen(mod->metadata_filename, "r");
+    if (file == 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(fgets(line, 1024, file))
+    {
+        if(line[0] == '\n')
+            break;
+        else
+        {
+            char **old_buf;
+            unsigned len = strlen(line);
+
+            if(line[len-1] == '\n')
+                line[len-1] = '\0';
+            old_buf = md;
+            md = realloc(md, (comments+2)*sizeof(char *));
+            if (md)
+            {
+                md[comments] = malloc(len+1);
+                strcpy(md[comments], line);
+                comments++;
+            }
+            else
+                md = old_buf;
+        }
+    }
+
+    fclose(file);
+
+    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_update(char **md, vorbis_comment *vc)
+{
+    if(md)
+    {
+        while(*md)
+        {
+            LOG_INFO1 ("Adding comment %s", *md);
+            vorbis_comment_add(vc, *md++);
+        }
+    }
+}
+
+void metadata_free (char **md)
+{
+    if(md)
+    {
+        char **comment = md;
+        while(*comment)
+        {
+            free(*comment);
+            comment++;
+        }
+        free(md);
+    }
+}

Added: icecast/branches/ices-kh/src/metadata.h
===================================================================
--- icecast/branches/ices-kh/src/metadata.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/metadata.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,27 @@
+/* metadata.h
+ * - metadata stuff.
+ *
+ * $Id: metadata.h,v 1.3 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 __METADATA_H__
+#define __METADATA_H__
+
+#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/ices-kh/src/om_file.c
===================================================================
--- icecast/branches/ices-kh/src/om_file.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/om_file.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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/ices-kh/src/om_file.h
===================================================================
--- icecast/branches/ices-kh/src/om_file.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/om_file.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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/ices-kh/src/om_shout.c
===================================================================
--- icecast/branches/ices-kh/src/om_shout.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/om_shout.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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/ices-kh/src/om_shout.h
===================================================================
--- icecast/branches/ices-kh/src/om_shout.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/om_shout.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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 */

Added: icecast/branches/ices-kh/src/playlist_basic.c
===================================================================
--- icecast/branches/ices-kh/src/playlist_basic.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/playlist_basic.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,236 @@
+/* playlist_basic.c
+ * - Simple built-in unscripted playlist
+ *
+ * $Id: playlist_basic.c,v 1.7 2002/08/10 04:26:52 msmith Exp $
+ *
+ * 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
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "cfgparse.h"
+#include "inputmodule.h"
+#include "im_playlist.h"
+#include "playlist_basic.h"
+
+#define MODULE "playlist-basic/"
+#include "logging.h"
+
+static void shuffle(char **buf, int len)
+{
+    int n,d;
+    char *temp;
+
+    n = len;
+    while(n > 1)
+    {
+        d = (int) ((double)n * rand()/(RAND_MAX+1.0));
+        temp = buf[d];
+        buf[d] = buf[n-1];
+        buf[n-1] = temp;
+        --n;
+    }
+    LOG_DEBUG0("Playlist has been shuffled");
+}
+
+static int load_playlist(basic_playlist *data)
+{
+    FILE *file;
+    char buf[1024];
+    int buflen;
+
+    file = fopen(data->file, "r");
+
+    if (file == NULL)
+    {
+        LOG_ERROR2("Playlist file %s could not be opened: %s",
+                data->file, strerror(errno));
+        return -1;
+    }
+
+    if(data->pl)
+    {
+        int i;
+        for(i = 0; i < data->len; i++)
+            free(data->pl[i]);
+        free(data->pl);
+    }
+    data->pl = NULL;
+    data->len = 0;
+    buflen = 0;
+    while (1)
+    {
+        if(fgets(buf,1024, file) == NULL) break;
+        if(buf[0]==0) break;
+
+        if(buf[0]=='\n' || (buf[0]=='\r' && buf[1]=='\n'))
+            continue;
+
+        if(buf[0] == '#') /* Commented out entry */
+            continue;
+
+        buf[strlen(buf)-1] = 0;
+
+        /* De-fuck windows files. */
+        if(strlen(buf) > 0 && buf[strlen(buf)-1] == '\r')
+            buf[strlen(buf)-1] = 0;
+
+        if(buflen < data->len+1)
+        {
+            char **tmp;
+            buflen += 100;
+            tmp = realloc(data->pl, buflen*sizeof(char *));
+            if (tmp == NULL)
+                break;
+            data->pl = tmp;
+        }
+
+        data->pl[data->len++] = strdup(buf);
+    }
+
+    if(data->random)
+        shuffle(data->pl, data->len);
+
+    fclose (file);
+
+    return 0;
+}
+
+static void playlist_basic_clear(void *data)
+{
+    basic_playlist *pl = data;
+    if(pl)
+    {
+        if(pl->pl)
+        {
+            int i;
+            for(i=0; i < pl->len; i++)
+                free(pl->pl[i]);
+			free(pl->pl);
+        }
+		free(pl);
+	}
+}
+
+static char *playlist_basic_get_next_filename(void *data)
+{
+	basic_playlist *pl = (basic_playlist *)data;
+    char *ptr = NULL, *dest = NULL;
+    int reload_playlist = 0;
+	struct stat st;
+
+	if (stat(pl->file, &st))
+	{
+		LOG_ERROR2("Couldn't stat file \"%s\": %s", pl->file, strerror(errno));
+		return NULL;
+	}
+
+    if (pl->pl)
+    {
+        if (st.st_mtime != pl->mtime)
+        {
+            reload_playlist = 1;
+            LOG_INFO1("Reloading playlist after file \"%s\" changed", pl->file);
+            pl->mtime = st.st_mtime;
+        }
+    }
+    else
+    {
+        LOG_INFO1("Loading playlist from file \"%s\"", pl->file);
+        reload_playlist = 1;
+        pl->mtime = st.st_mtime;
+    }
+
+    if (reload_playlist)
+    {
+        if (load_playlist(pl) < 0)
+            return NULL;
+        if (pl->restartafterreread)
+            pl->pos = 0;
+	}
+
+	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++];
+
+    if ((dest = malloc (strlen (ptr)+1)) == NULL)
+        return NULL;
+    strcpy (dest, ptr);
+    return dest;
+}
+
+static void playlist_basic_free_filename(void *data __attribute__((unused)), char *fn)
+{
+    if (fn) free (fn);
+}
+
+int playlist_basic_initialise(module_param_t *params, struct playlist_state *pl)
+{
+	basic_playlist *data;
+
+	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;
+
+	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;
+	}
+
+	return 0;
+}
+

Added: icecast/branches/ices-kh/src/playlist_basic.h
===================================================================
--- icecast/branches/ices-kh/src/playlist_basic.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/playlist_basic.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,35 @@
+/* playlist_basic.h
+ * - Simple unscripted playlist
+ *
+ * $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>
+ *
+ * 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 __PLAYLIST_BASIC_H__
+#define __PLAYLIST_BASIC_H__
+
+typedef struct
+{
+	char **pl;
+	int len;
+	int pos;
+	char *file; /* Playlist file */
+	time_t mtime;
+	int random;
+	int once;
+    int restartafterreread;
+
+} basic_playlist;
+
+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__ */
+

Added: icecast/branches/ices-kh/src/playlist_script.c
===================================================================
--- icecast/branches/ices-kh/src/playlist_script.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/playlist_script.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,129 @@
+/* playlist_script.c
+ * - Gets a filename to play back based on output from a program/shell script
+ *   run each time.
+ *
+ * $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>
+ *
+ * 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 <errno.h>
+#include <string.h>
+
+#include "cfgparse.h"
+#include "inputmodule.h"
+#include "im_playlist.h"
+
+#define MODULE "playlist-script/"
+#include "logging.h"
+
+typedef struct {
+    char *program;
+} script_playlist;
+
+static void playlist_script_clear(void *data) {
+    if(data)
+        free(data);
+}
+
+static char *playlist_script_get_filename(void *data) {
+    script_playlist *pl = data;
+    char *prog = pl->program;
+    FILE *pipe;
+    char *buf = calloc(1,1024);
+
+    if(!buf)
+        return NULL;
+
+    pipe = popen(prog, "r");
+
+    if(!pipe) {
+        LOG_ERROR1("Couldn't open pipe to program \"%s\"", prog);
+        return NULL;
+    }
+
+    if(fgets(buf, 1024, pipe) == NULL) {
+        LOG_ERROR1("Couldn't read filename from pipe to program \"%s\"", prog);
+        free(buf);
+        pclose(pipe);
+        return NULL;
+    }
+
+    pclose(pipe);
+
+    if(buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) {
+        LOG_ERROR1("Got newlines instead of filename from program \"%s\"", prog);
+        free(buf);
+        return NULL;
+    }
+
+    if(buf[strlen(buf)-1] == '\n')
+        buf[strlen(buf)-1] = 0;
+    else
+        LOG_WARN1("Retrieved overly long filename \"%s\" from script, this may fail", buf);
+
+    /* De-fuck windows filenames. */
+    if(strlen(buf) > 0 && buf[strlen(buf)-1] == '\r')
+        buf[strlen(buf)-1] = 0;
+
+    LOG_DEBUG2("Program/script (\"%s\") returned filename \"%s\"", prog, buf);
+
+    return buf;
+}
+
+static void playlist_script_free_filename(void *data __attribute__((unused)), char *fn)
+{
+    free(fn);
+}
+
+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)
+        return -1;
+
+    data = (script_playlist *)pl->data;
+
+    while(params != NULL) {
+        if(!strcmp(params->name, "program")) {
+            if(data->program) free(data->program);
+            data->program = 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);
+        params = params->next;
+    }
+
+    if(!data->program) {
+        LOG_ERROR0("No program name specified for playlist module");
+        free(data);
+        return -1;
+    }
+
+    return 0;
+}
+

Added: icecast/branches/ices-kh/src/reencode.c
===================================================================
--- icecast/branches/ices-kh/src/reencode.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/reencode.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,191 @@
+/* reencode.c
+ * - runtime reencoding of vorbis audio (usually to lower bitrates).
+ *
+ * $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) 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
+ * 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 <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+#include "cfgparse.h"
+#include "reencode.h"
+#include "stream.h"
+#include "encode.h"
+#include "audio.h"
+
+#define MODULE "reencode/"
+#include "logging.h"
+
+extern ogg_packet *copy_ogg_packet (ogg_packet *packet);
+
+struct reencode *reencode_new_init (struct encoder_settings *settings)
+{
+    struct reencode *reenc = calloc (1, sizeof (struct reencode));
+    if (reenc)
+    {
+        reenc->encoder = encode_create();
+        if (reenc->encoder == NULL)
+        {
+            free (reenc);
+            return NULL;
+        }
+
+        vorbis_info_init (&reenc->vi);
+        vorbis_comment_init (&reenc->vc);
+        reenc->settings = settings;
+        reenc->need_headers = 3;
+        LOG_DEBUG0("Reencoder setup complete");
+    }
+    return reenc;
+}
+
+
+
+void reencode_free(struct reencode *s)
+{
+    if (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);
+    }
+}
+
+
+
+static int reencode_vorbis_header (struct reencode *s, ogg_packet *op)
+{
+    if (s->need_headers)
+    {
+        /* LOG_DEBUG0("processing vorbis header"); */
+        if (vorbis_synthesis_headerin (&s->vi, &s->vc, op) < 0)
+        {
+            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;
+            }
+
+            s->settings->encode_rate = s->vi.rate;
+            if (s->settings->samplerate && s->settings->samplerate != s->vi.rate)
+            {
+                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;
+        }
+
+        return 0;
+    }
+    LOG_WARN0("function called when not expecting header");
+    return -1;
+}
+
+
+int reencode_packetin (struct reencode *s, ogg_packet *packet)
+{
+    int ret = 0;
+    float **pcm;
+    int samples;
+
+    if (s->need_headers == 0)
+    {
+        if (vorbis_synthesis (&s->vb, packet) == 0)
+        {
+            vorbis_synthesis_blockin (&s->vd, &s->vb);
+        }
+
+        /* 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)
+        {
+            if (s->downmix)
+            {
+                downmix_buffer_float(s->downmix, pcm, samples, s->vi.channels);
+                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);
+            ret = 1;
+        }
+        if (packet->e_o_s)
+            encode_endstream (s->encoder);
+        return ret;
+    }
+    return reencode_vorbis_header (s, packet);
+}
+
+
+
+
+int reencode_packetout(struct reencode *s, ogg_packet *op)
+{
+    if (s)
+    {
+        if (s->need_headers)
+            return 0;
+        return encode_packetout (s->encoder, op);
+    }
+    return -1;
+}
+

Added: icecast/branches/ices-kh/src/reencode.h
===================================================================
--- icecast/branches/ices-kh/src/reencode.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/reencode.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,71 @@
+/* reencode.h
+ * - reencoding functions
+ *
+ * $Id: reencode.h,v 1.4 2002/08/03 14:41:10 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 __REENCODE_H
+#define __REENCODE_H
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+typedef struct _reencode_tag reencode;
+
+#include "encode.h"
+#include "inputmodule.h"
+#include "audio.h"
+
+struct instance;
+
+struct reencode
+{
+    struct encoder *encoder;
+    struct encoder_settings *settings;
+
+    int out_samplerate;
+    int out_channels;
+
+    int in_samplerate;
+    int in_channels;
+
+    int in_use;
+
+    int need_headers;
+
+    vorbis_info vi;
+    vorbis_comment vc;
+    vorbis_dsp_state vd;
+    vorbis_block vb;
+
+    struct downmix *downmix;
+    struct resample *resamp;
+
+};
+
+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 */
+

Added: icecast/branches/ices-kh/src/resample.c
===================================================================
--- icecast/branches/ices-kh/src/resample.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/resample.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,461 @@
+/* resample.c: see resample.h for interesting stuff */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include "resample.h"
+
+/* Some systems don't define this */
+#ifndef M_PI
+#define M_PI       3.14159265358979323846
+#endif
+
+static int hcf(int arg1, int arg2)
+{
+    int mult = 1;
+
+    while (~(arg1 | arg2) & 1)
+        arg1 >>= 1, arg2 >>= 1, mult <<= 1;
+
+    while (arg1 > 0)
+    {
+        if (~(arg1 & arg2) & 1)
+        {
+            arg1 >>= (~arg1 & 1);
+            arg2 >>= (~arg2 & 1);
+        }
+        else if (arg1 < arg2)
+            arg2 = (arg2 - arg1) >> 1;
+        else
+            arg1 = (arg1 - arg2) >> 1;
+    }
+
+    return arg2 * mult;
+}
+
+
+static void filt_sinc(float *dest, int N, int step, double fc, double gain, int width)
+{
+    double s = fc / step;
+    int mid, x;
+    float *endpoint = dest + N,
+        *base = dest,
+        *origdest = dest;
+
+    assert(width <= N);
+
+    if ((N & 1) == 0)
+    {
+        *dest = 0.0;
+        dest += width;
+        if (dest >= endpoint)
+            dest = ++base;
+        N--;
+    }
+
+    mid = N / 2;
+    x = -mid;
+
+    while (N--)
+    {
+        *dest = (x ? sin(x * M_PI * s) / (x * M_PI) * step : fc) * gain;
+        x++;
+        dest += width;
+        if (dest >= endpoint)
+            dest = ++base;
+    }
+    assert(dest == origdest + width);
+}
+
+
+static double I_zero(double x)
+{
+    int n = 0;
+    double u = 1.0,
+        s = 1.0,
+        t;
+
+    do
+    {
+        n += 2;
+        t = x / n;
+        u *= t * t;
+        s += u;
+    } while (u > 1e-21 * s);
+
+    return s;
+}
+
+
+static void win_kaiser(float *dest, int N, double alpha, int width)
+{
+    double I_alpha, midsq;
+    int x;
+    float *endpoint = dest + N,
+        *base = dest,
+        *origdest = dest;
+
+    assert(width <= N);
+
+    if ((N & 1) == 0)
+    {
+        *dest = 0.0;
+        dest += width;
+        if (dest >= endpoint)
+            dest = ++base;
+        N--;
+    }
+
+    x = -(N / 2);
+    midsq = (double)(x - 1) * (double)(x - 1);
+    I_alpha = I_zero(alpha);
+
+    while (N--)
+    {
+        *dest *= I_zero(alpha * sqrt(1.0 - ((double)x * (double)x) / midsq)) / I_alpha;
+        x++;
+        dest += width;
+        if (dest >= endpoint)
+            dest = ++base;
+    }
+    assert(dest == origdest + width);
+}
+
+
+int resampler_init(struct resampler *state, int channels, int outfreq, int infreq, resampler_parameter op1, ...)
+{
+    double beta = 16.0,
+        cutoff = 0.80,
+        gain = 1.0;
+    int taps = 45;
+
+    int factor;
+
+    assert(state);
+    assert(channels > 0);
+    assert(outfreq > 0);
+    assert(infreq > 0);
+    assert(taps > 0);
+
+    if (state == NULL || channels <= 0 || outfreq <= 0 || infreq <= 0 || taps <= 0)
+        return -1;
+
+    if (op1 != RES_END)
+    {
+        va_list argp;
+        va_start(argp, op1);
+        do
+        {
+            switch (op1)
+            {
+            case RES_GAIN:
+                gain = va_arg(argp, double);
+                break;
+
+            case RES_CUTOFF:
+                cutoff = va_arg(argp, double);
+                assert(cutoff > 0.01 && cutoff <= 1.0);
+                break;
+
+            case RES_TAPS:
+                taps = va_arg(argp, int);
+                assert(taps > 2 && taps < 1000);
+                break;
+
+            case RES_BETA:
+                beta = va_arg(argp, double);
+                assert(beta > 2.0);
+                break;
+            default:
+                assert("arglist" == "valid");
+                return -1;
+            }
+            op1 = va_arg(argp, resampler_parameter);
+        } while (op1 != RES_END);
+        va_end(argp);
+    }
+
+    factor = hcf(infreq, outfreq);
+    outfreq /= factor;
+    infreq /= factor;
+
+    /* adjust to rational values for downsampling */
+    if (outfreq < infreq)
+    {
+        /* push the cutoff frequency down to the output frequency */
+        cutoff = cutoff * outfreq / infreq;
+
+        /* compensate for the sharper roll-off requirement
+         * by using a bigger hammer */
+        taps = taps * infreq/outfreq;
+    }
+
+    assert(taps >= (infreq + outfreq - 1) / outfreq);
+
+    if ((state->table = calloc(outfreq * taps, sizeof(float))) == NULL)
+        return -1;
+    if ((state->pool = calloc(channels * taps, sizeof(SAMPLE))) == NULL)
+    {
+        free(state->table);
+        state->table = NULL;
+        return -1;
+    }
+
+    state->poolfill = taps / 2 + 1;
+    state->channels = channels;
+    state->outfreq = outfreq;
+    state->infreq = infreq;
+    state->taps = taps;
+    state->offset = 0;
+
+    filt_sinc(state->table, outfreq * taps, outfreq, cutoff, gain, taps);
+    win_kaiser(state->table, outfreq * taps, beta, taps);
+
+    return 0;
+}
+
+
+static SAMPLE sum(float const *scale, int count, SAMPLE const *source, SAMPLE const *trigger, SAMPLE const *reset, int srcstep)
+{
+    float total = 0.0;
+
+    while (count--)
+    {
+        total += *source * *scale;
+
+        if (source == trigger)
+            source = reset, srcstep = 1;
+        source -= srcstep;
+        scale++;
+    }
+
+    return total;
+}
+
+
+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,
+        *poolend = pool + state->taps,
+        *newpool = pool;
+    SAMPLE const *refill, *base, *endpoint;
+    int    lencheck;
+
+
+    assert(state);
+    assert(pool);
+    assert(poolfill);
+    assert(dest);
+    assert(source);
+
+    assert(state->poolfill != -1);
+
+    lencheck = resampler_push_check(state, srclen);
+
+    /* fill the pool before diving in */
+    while (poolhead < poolend && srclen > 0)
+    {
+        *poolhead++ = *source;
+        source += srcstep;
+        srclen--;
+    }
+
+    if (srclen <= 0)
+        return 0;
+
+    base = source;
+    endpoint = source + srclen * srcstep;
+
+    while (source < endpoint)
+    {
+        *dest = sum(state->table + *offset * state->taps, state->taps, source, base, poolend, srcstep);
+        dest += dststep;
+        *offset += state->infreq;
+        while (*offset >= state->outfreq)
+        {
+            *offset -= state->outfreq;
+            source += srcstep;
+        }
+    }
+
+    assert(dest == destbase + lencheck * dststep);
+
+    /* pretend that source has that underrun data we're not going to get */
+    srclen += (source - endpoint) / srcstep;
+
+    /* if we didn't get enough to completely replace the pool, then shift things about a bit */
+    if (srclen < state->taps)
+    {
+        refill = pool + srclen;
+        while (refill < poolend)
+            *newpool++ = *refill++;
+
+        refill = source - srclen * srcstep;
+    }
+    else
+        refill = source - state->taps * srcstep;
+
+    /* pull in fresh pool data */
+    while (refill < endpoint)
+    {
+        *newpool++ = *refill;
+        refill += srcstep;
+    }
+
+    assert(newpool > pool);
+    assert(newpool <= poolend);
+
+    *poolfill = newpool - pool;
+
+    return (dest - destbase) / dststep;
+}
+
+
+int resampler_push_max_input(struct resampler const * const state, size_t maxoutput)
+{
+    return maxoutput * state->infreq / state->outfreq;
+}
+
+
+int resampler_push_check(struct resampler const * const state, size_t srclen)
+{
+    if (state->poolfill < state->taps)
+        srclen -= state->taps - state->poolfill;
+
+    return (srclen * state->outfreq - state->offset + state->infreq - 1) / state->infreq;
+}
+
+
+int resampler_push(struct resampler *state, SAMPLE **dstlist, SAMPLE const **srclist, size_t srclen)
+{
+    int result = -1, poolfill = -1, offset = -1, i;
+
+    assert(state);
+    assert(dstlist);
+    assert(srclist);
+    assert(state->poolfill >= 0);
+
+    for (i = 0; i < state->channels; i++)
+    {
+        poolfill = state->poolfill;
+        offset = state->offset;
+        result = push(state, state->pool + i * state->taps, &poolfill, &offset, dstlist[i], 1, srclist[i], 1, srclen);
+    }
+    state->poolfill = poolfill;
+    state->offset = offset;
+
+    return result;
+}
+
+
+int resampler_push_interleaved(struct resampler *state, SAMPLE *dest, SAMPLE const *source, size_t srclen)
+{
+    int result = -1, poolfill = -1, offset = -1, i;
+
+    assert(state);
+    assert(dest);
+    assert(source);
+    assert(state->poolfill >= 0);
+
+    for (i = 0; i < state->channels; i++)
+    {
+        poolfill = state->poolfill;
+        offset = state->offset;
+        result = push(state, state->pool + i * state->taps, &poolfill, &offset, dest + i, state->channels, source + i, state->channels, srclen);
+    }
+    state->poolfill = poolfill;
+    state->offset = offset;
+
+    return result;
+}
+
+
+int resampler_drain(struct resampler *state, SAMPLE **dstlist)
+{
+    SAMPLE *tail;
+    int result = -1, poolfill = -1, offset = -1, i;
+
+    assert(state);
+    assert(dstlist);
+    assert(state->poolfill >= 0);
+
+    if ((tail = calloc(state->taps, sizeof(SAMPLE))) == NULL)
+        return -1;
+
+    for (i = 0; i < state->channels; i++)
+    {
+        poolfill = state->poolfill;
+        offset = state->offset;
+        result = push(state, state->pool + i * state->taps, &poolfill, &offset, dstlist[i], 1, tail, 1, state->taps / 2 - 1);
+    }
+
+    free(tail);
+
+    state->poolfill = -1;
+
+    return result;
+}
+
+
+int resampler_drain_interleaved(struct resampler *state, SAMPLE *dest)
+{
+    SAMPLE *tail;
+    int result = -1, poolfill = -1, offset = -1, i;
+
+    assert(state);
+    assert(dest);
+    assert(state->poolfill >= 0);
+
+    if ((tail = calloc(state->taps, sizeof(SAMPLE))) == NULL)
+        return -1;
+
+    for (i = 0; i < state->channels; i++)
+    {
+        poolfill = state->poolfill;
+        offset = state->offset;
+        result = push(state, state->pool + i * state->taps, &poolfill, &offset, dest + i, state->channels, tail, 1, state->taps / 2 - 1);
+    }
+
+    free(tail);
+
+    state->poolfill = -1;
+
+    return result;
+}
+
+
+void resampler_clear(struct resampler *state)
+{
+    assert(state);
+    assert(state->table);
+    assert(state->pool);
+
+    free(state->table);
+    free(state->pool);
+    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;
+}
+

Added: icecast/branches/ices-kh/src/resample.h
===================================================================
--- icecast/branches/ices-kh/src/resample.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/resample.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,112 @@
+/* This program is licensed under the GNU Library General Public License,
+ * version 2, a copy of which is included with this program (LICENCE.LGPL).
+ *
+ * (c) 2002 Simon Hosie <gumboot at clear.net.nz>
+ *
+ *
+ * A resampler
+ *
+ * reference:
+ *     'Digital Filters', third edition, by R. W. Hamming  ISBN 0-486-65088-X
+ *
+ * history:
+ *    2002-05-31    ready for the world (or some small section thereof)
+ *
+ *
+ * TOOD:
+ *     zero-crossing clipping in coefficient table
+ */
+
+#ifndef _RESAMPLE_H_INCLUDED
+#define _RESAMPLE_H_INCLUDED
+
+#include <cfgparse.h>
+
+typedef float SAMPLE;
+
+struct resampler
+{
+    unsigned int channels, infreq, outfreq, taps;
+    float *table;
+    SAMPLE *pool;
+
+    /* dynamic bits */
+    int poolfill;
+    int offset;
+};
+
+typedef enum
+{
+    RES_END,
+    RES_GAIN,    /* (double)1.0 */
+    RES_CUTOFF,    /* (double)0.80 */
+    RES_TAPS,    /* (int)45 */
+    RES_BETA    /* (double)16.0 */
+} resampler_parameter;
+
+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
+ * of the filter.
+ *
+ * This function allocates memory, and requires that resampler_clear() be called when
+ * the buffer is no longer needed.
+ *
+ *
+ * All counts/lengths used in the following functions consider only the data in
+ * a single channel, and in numbers of samples rather than bytes, even though
+ * functionality will be mirrored across as many channels as specified here.
+ */
+
+
+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
+ *  specified in counts of elements, NOT in bytes.
+ */
+
+
+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(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.
+ *
+ * resampler_push(): srclist and dstlist point to lists of pointers, each of which
+ * indicates the beginning of a list of samples.
+ *
+ * resampler_push_interleaved(): source and dest point to the beginning of a list of
+ * interleaved samples.
+ */
+
+
+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.
+ *
+ * After either of these functions are called, *state should only re-used in a
+ * final call to resampler_clear().
+ */
+
+
+void resampler_clear(struct resampler *state);
+/*
+ * Free allocated buffers, etc.
+ */
+
+
+int parse_resample (xmlNodePtr node, void *resample_ptr);
+
+#endif
+

Added: icecast/branches/ices-kh/src/runner.c
===================================================================
--- icecast/branches/ices-kh/src/runner.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/runner.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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/ices-kh/src/runner.h
===================================================================
--- icecast/branches/ices-kh/src/runner.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/runner.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -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
+

Added: icecast/branches/ices-kh/src/signals.c
===================================================================
--- icecast/branches/ices-kh/src/signals.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/signals.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,77 @@
+/* signals.c
+ * - signal handling/setup
+ *
+ * $Id: signals.c,v 1.4 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include "thread/thread.h"
+
+#include "cfgparse.h"
+#include "stream.h"
+#include "inputmodule.h"
+
+#define MODULE "signals/"
+#include "logging.h"
+
+extern volatile int metadata_update_signalled;
+int move_to_next_input;
+
+void signal_usr1_handler(int signum __attribute__((unused)))
+{
+    /* LOG_INFO0("Metadata update requested"); */
+    metadata_update_signalled = 1;
+
+    signal(SIGUSR1, signal_usr1_handler);
+}
+
+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);
+
+    ices_config->next_track = 1;
+    signal(SIGHUP, signal_hup_handler);
+}
+
+void signal_int_handler(int signum __attribute__((unused)))
+{
+    /* LOG_INFO0("Shutdown requested..."); */
+    ices_config->shutdown = 1;
+    signal(SIGINT, signal_int_handler);
+}
+
+
+void signals_setup(void)
+{
+	signal(SIGINT, signal_int_handler);
+	signal(SIGTERM, signal_int_handler);
+	signal(SIGUSR1, signal_usr1_handler);
+	signal(SIGUSR2, signal_usr2_handler);
+	signal(SIGPIPE, SIG_IGN);
+}
+
+

Added: icecast/branches/ices-kh/src/signals.h
===================================================================
--- icecast/branches/ices-kh/src/signals.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/signals.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,29 @@
+/* signals.h
+ * - signal handling/setup
+ *
+ * $Id: signals.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 __SIGNALS_H
+#define __SIGNALS_H
+
+#include <signal.h>
+
+extern int move_to_next_input;
+
+
+void signal_hup_handler(int signum);
+void signal_int_handler(int signum);
+
+void signals_setup(void);
+
+#endif
+
+

Added: icecast/branches/ices-kh/src/stream.c
===================================================================
--- icecast/branches/ices-kh/src/stream.c	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/stream.c	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,479 @@
+/* stream_shared.c
+ * - Stream utility functions.
+ *
+ * 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
+ * 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 <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+
+#include <thread/thread.h>
+
+#include "cfgparse.h"
+#include "inputmodule.h"
+#include "stream.h"
+#include "reencode.h"
+#include "encode.h"
+#include "audio.h"
+#include "metadata.h"
+
+#define MODULE "stream/"
+#include "logging.h"
+
+
+struct pcm2vorbis_encode
+{
+    struct encoder *enc;
+    struct resample *resamp;
+    struct downmix *downmix;
+};
+
+
+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);
+
+        /* clear stored headers */
+        free (state->packets[0].packet);
+        /* ogg_packet_clear (&state->packets[1]); */
+        free (state->packets[1].packet);
+        free (state->packets[2].packet);
+
+        /* 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;
+    }
+}
+
+
+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");
+
+    /* 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); */
+
+    mod = state->head;
+    while (mod)
+    {
+        mod->output_send (mod, op, samples);
+        mod = mod->next;
+    }
+
+    state->new_headers = 0;
+
+    return 0;
+}
+
+
+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;
+}
+
+
+
+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;
+}
+
+
+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)
+    {
+        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
+    {
+        encode_data_float (s->enc, buffer->buf, buffer->samples);
+    }
+    while (encode_packetout (s->enc, &op) > 0)
+    {
+        if (ret == 0 && _output_oggpacket (stream, &op, 0) < 0)
+        {
+            ret = -1;
+            break;
+        }
+    }
+    return ret;
+}
+
+
+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
+    {
+        struct codec_ops *ops = stream->ops;
+
+        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)
+        {
+            s->downmix = downmix_initialise();
+            if (s->downmix == NULL)
+                break;
+            channels = 1;
+        }
+
+        if (buffer->samplerate != samplerate)
+        {
+            s->resamp = resample_initialise (channels, buffer->samplerate, samplerate);
+            if (s->resamp == NULL)
+                break;
+        }
+
+        if (buffer->metadata)
+        {
+            char **md = buffer->metadata;
+            while(*md)
+            {
+                LOG_INFO1 ("Adding comment %s", *md);
+                encode_comment (enc, *md++);
+            }
+        }
+
+        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;
+
+        return 0;
+    } while (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;
+}
+
+
+
+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;
+            }
+            else
+                break;
+        }
+    }
+    return ret;
+}
+
+
+static int process_ogg_init (struct instance *stream, input_buffer *buffer)
+{
+    if (stream->passthru)
+        stream->ops = &passthru_ptks_ops;
+    else
+    {
+        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;
+    }
+    return 0;
+}
+
+
+static int send_vorbis_packet (struct instance *stream, input_buffer *buffer)
+{
+    ogg_packet *packet = (ogg_packet *)buffer->buf;
+
+    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
+};
+

Added: icecast/branches/ices-kh/src/stream.h
===================================================================
--- icecast/branches/ices-kh/src/stream.h	2004-06-24 18:22:39 UTC (rev 6844)
+++ icecast/branches/ices-kh/src/stream.h	2004-06-24 18:23:46 UTC (rev 6845)
@@ -0,0 +1,39 @@
+/* stream.h
+ * - Core streaming functions/main loop.
+ *
+ * $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>
+ *
+ * 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 __STREAM_H
+#define __STREAM_H
+
+#include "runner.h"
+
+struct codec_ops
+{
+    void *data;
+    int  (*process_buffer)(struct instance *stream, input_buffer *buffer);
+    void (*flush_data)(struct instance *stream);
+};
+
+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;
+
+
+void process_critical (struct instance *stream, input_buffer *buffer);
+void output_clear(struct output_state *state);
+
+#endif
+
+



More information about the commits mailing list