[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", ¶m->name) < 0)
+ {
+ free (param);
+ return -1;
+ }
+ if (get_xml_string (node, ¶m->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, ¶m);
+#ifdef HAVE_SCHED_GET_PRIORITY_MAX
+ param . sched_priority = sched_get_priority_max (SCHED_RR);
+#endif
+
+ if (pthread_setschedparam (pthread_self(), SCHED_RR, ¶m))
+ 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, ¶m);
+ param . sched_priority = sched_get_priority_min (SCHED_OTHER);
+
+ if (pthread_setschedparam (pthread_self(), SCHED_OTHER, ¶m))
+ {
+ 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