[xiph-commits] r7098 - in icecast/branches/kh: . ices ices/conf
j at dactyl.lonelymoon.com
j
Sun Jul 11 11:40:09 PDT 2004
ices/src
Message-ID: <20040711184009.C9C109AAAB at dactyl.lonelymoon.com>
Author: j
Date: Sun Jul 11 11:40:09 2004
New Revision: 7098
Added:
icecast/branches/kh/ices/
icecast/branches/kh/ices/NEWS
icecast/branches/kh/ices/conf/ices-fixit.xml
icecast/branches/kh/ices/conf/ices-jack.xml
icecast/branches/kh/ices/conf/ices-pcm.xml
icecast/branches/kh/ices/conf/ices-pcm2.xml
icecast/branches/kh/ices/conf/ices-switch.xml
icecast/branches/kh/ices/src/im_jack.c
icecast/branches/kh/ices/src/im_jack.h
icecast/branches/kh/ices/src/im_pcm.c
icecast/branches/kh/ices/src/im_pcm.h
icecast/branches/kh/ices/src/om_file.c
icecast/branches/kh/ices/src/om_file.h
icecast/branches/kh/ices/src/om_shout.c
icecast/branches/kh/ices/src/om_shout.h
icecast/branches/kh/ices/src/runner.c
icecast/branches/kh/ices/src/runner.h
Modified:
icecast/branches/kh/ices/AUTHORS
icecast/branches/kh/ices/Makefile.am
icecast/branches/kh/ices/README
icecast/branches/kh/ices/TODO
icecast/branches/kh/ices/conf/Makefile.am
icecast/branches/kh/ices/conf/ices-live.xml
icecast/branches/kh/ices/conf/ices-playlist.xml
icecast/branches/kh/ices/configure.in
icecast/branches/kh/ices/src/Makefile.am
icecast/branches/kh/ices/src/audio.c
icecast/branches/kh/ices/src/audio.h
icecast/branches/kh/ices/src/cfgparse.c
icecast/branches/kh/ices/src/cfgparse.h
icecast/branches/kh/ices/src/encode.c
icecast/branches/kh/ices/src/encode.h
icecast/branches/kh/ices/src/ices.c
icecast/branches/kh/ices/src/im_alsa.c
icecast/branches/kh/ices/src/im_alsa.h
icecast/branches/kh/ices/src/im_oss.c
icecast/branches/kh/ices/src/im_oss.h
icecast/branches/kh/ices/src/im_playlist.c
icecast/branches/kh/ices/src/im_playlist.h
icecast/branches/kh/ices/src/im_sun.c
icecast/branches/kh/ices/src/im_sun.h
icecast/branches/kh/ices/src/input.c
icecast/branches/kh/ices/src/inputmodule.h
icecast/branches/kh/ices/src/logging.h
icecast/branches/kh/ices/src/metadata.c
icecast/branches/kh/ices/src/metadata.h
icecast/branches/kh/ices/src/playlist_basic.c
icecast/branches/kh/ices/src/playlist_basic.h
icecast/branches/kh/ices/src/playlist_script.c
icecast/branches/kh/ices/src/reencode.c
icecast/branches/kh/ices/src/reencode.h
icecast/branches/kh/ices/src/resample.c
icecast/branches/kh/ices/src/resample.h
icecast/branches/kh/ices/src/signals.c
icecast/branches/kh/ices/src/signals.h
icecast/branches/kh/ices/src/stream.c
icecast/branches/kh/ices/src/stream.h
Log:
reimport of ices-kh to brances/kh/ices; this time as a branch of trunk/ices
Copied: icecast/branches/kh/ices (from rev 6976, icecast/trunk/ices)
Property changes on: icecast/branches/kh/ices
___________________________________________________________________
Name: branch-point
+ 7096
Name: svn:externals
+ m4 http://svn.xiph.org/icecast/trunk/m4
src/log http://svn.xiph.org/icecast/trunk/log
src/net http://svn.xiph.org/icecast/branches/kh/net
src/timing http://svn.xiph.org/icecast/trunk/timing
src/thread http://svn.xiph.org/icecast/branches/kh/thread
Modified: icecast/branches/kh/ices/AUTHORS
===================================================================
--- icecast/trunk/ices/AUTHORS 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/AUTHORS 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,3 +1 @@
-Michael Smith <msmith at labyrinth.net.au>
-Jack Moffitt <jack at icecast.org>
Karl Heyes <karl at xiph.org>
Modified: icecast/branches/kh/ices/Makefile.am
===================================================================
--- icecast/trunk/ices/Makefile.am 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/Makefile.am 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,11 +1,11 @@
## Process this file with automake to produce Makefile.in
-AUTOMAKE_OPTIONS = foreign 1.6 dist-bzip2
+AUTOMAKE_OPTIONS = 1.6 foreign dist-bzip2
ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = debian src conf doc
+SUBDIRS = src conf
-EXTRA_DIST = README AUTHORS COPYING TODO m4/shout.m4 m4/vorbis.m4 m4/ogg.m4 m4/xiph_compiler.m4 m4/xiph_xml2.m4
+EXTRA_DIST = README AUTHORS COPYING TODO NEWS m4
# SCCS Definitions (for BitKeeper)
GET = true
Added: icecast/branches/kh/ices/NEWS
===================================================================
--- icecast/trunk/ices/NEWS 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/NEWS 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,427 @@
+1.59
+----
+. fix ALSA silence issue, stupid bug...a one-liner as well
+. Allow downmix to accept >2 channels
+. put back original timing allocation code in im_jack, also
+ base ringbuffer size on buffer_size and sleep time
+
+1.58
+----
+. fix build issue for saved id
+. update jack input module to use ringbuffer
+
+1.57
+----
+. handle "-" in playlist to refer to stdin
+. for live inputs make sure module only fails after 100 buffer allocation
+ failures in succession, instead of in total.
+. report initial failed log file opening
+
+1.56
+----
+. small update to JACK input
+. make sure non-blocking sockets are used in libshout
+
+1.55
+----
+. removed more unused code
+. plug some memory leaks, affected
+. tweaked parameters to input modules, higher max/preloaded buffers and
+ a longer recovery delay for playlist.
+
+1.54
+----
+. Add JACK input, feedback if possible
+. various internal API cleanups
+. buffer allocation messages are handled more gracefully
+. fix quirk with missing metadata filename
+
+1.53
+----
+. fix compile issue on OBSD wrt im_sun module
+. change unknown XML element message, state filename and quote tags in question
+. small C99 cleanups, check for __func__
+. XML call API change, was causing a memory corruption in parsing.
+
+1.52
+----
+. fix up minor compile issue.
+. push shout_t creation/population to connection time, allows instance
+ metadata to after encode section.
+. fixup alsa input code.
+
+1.51
+----
+. YP bitrate is expected to be in kbps
+. added URL to global and per-instance metadata.
+. fixup metadata settings better. string copies were not being done
+
+1.50
+----
+. function rename cleanup for xml parser
+. implement background tag, it can now makes itself daemon
+. only free buffers when there's a surplus. prealloc is minimum.
+. only warn on so many reconnect attempts, keep retrying.
+. supply nominal-bitrate to the shout audio info at connection time.
+. take out logging messages in sig handlers. Functions are not signal safe
+. moved the dead air buffer into global, saves using duplicate buffers
+
+1.49
+----
+. update encode_initialise, allow for setting VBR mode by bitrate, also
+ constrained VBR mode doesn't work as expected, vorbisenc issue.
+. add pidfile option for external handling
+. update im_sun to handle the different endian on sun/sparc sun/x86 and
+ openbsd
+. change buffer allocation routine. preallocation is still done but on a
+ smaller scale and the buffer allocation routine allocs/initialises buffers
+ when needed and below limit. The free routine will free one buffer every so
+ often to cut down on surplus buffers.
+
+1.48
+----
+. Stupid timer fix, on timing error the reset wasn't working out right
+. fixup im_sun better, should work ok for OBSD now. maybe solaris as well
+. don't try reading metadata file if it isn't stated
+
+1.47
+----
+. changed sched_getparam to pthread_* to work on OBSD
+
+1.46
+----
+. fix managed mode selection
+. make serial numbers random for each outgoing stream
+
+1.45
+----
+. call shout close even if connection failed. This prevents zombie
+ connections from building up.
+
+1.44
+----
+. increase delay on failed buffer allocations to 100ms, and increase
+ timer to account for it, this should prevent a bursts on recovery.
+. Add logsize xml parameter in kbytes, default is 2048.
+. tweak to make buffer count dependant on number of runners
+
+1.43
+----
+. downmix is now a bool type, so true,yes as well as 1 is valid
+. in playlist loop on buffer allocation failure, this should stop needless
+ logging as the previous message is noted.
+
+1.42
+----
+. dead code removal
+. reset timer if input jumps more than 1 second.
+. when sleeping enabled (normal) and buffer allocation fails then instead
+ of dropping out, log one message, sleep for a short while and retry again
+. implement bool tpye XML tags in config. true. yes are acceptable as well
+ as a numeric.
+
+1.41
+----
+. separate user-derived rate/channels, from input derived ones.
+
+1.40
+----
+. fix granulepos for file fixing, for extra compliance.
+. added "once" tag for handling each input once only. Needed for file fix.
+
+1.39
+----
+. Made playlist honor USR1 USR2 signals again
+. fixup ALSA, but untested until I install the drivers again.
+. minor cleanups, removing unused bits etc
+
+1.38
+----
+. don't share the code_ops structure as it contains stream specific info
+. drop the vorbis array from the buffers, it was a hack
+. ALSA driver not fully changed over yet
+
+1.37
+----
+First 3 could break things as they are fairly substantial changes
+. big input flow control change, callback approach
+. various structure re-arrangement
+. playlist now sends as out as ogg packets instead of ogg pages, also
+ samples are calculated per packet.
+. put quotes around filenames, allows for easy tracking in logs.
+. reencoder takes packets instead of pages
+. make random playlist more random
+
+1.36
+----
+. Fixed bug in playlist script, the rate regulation was disabled.
+. removed odd printf
+
+1.35
+----
+. fix reconnect_delay/attempts parsing issue.
+. Added YP option for advertising the shout stream
+. Updated m4 scripts in line with CVS
+. Added 2Meg log cycle
+
+1.34
+----
+. move various code out of cfgparse into specifics files.
+. elimate passthru in encode.
+. set samplerate/channels on every buffer from the playlist
+ fixes an assert possibility
+
+1.33
+----
+. Add skip option to pcm input, to allow for removal of initial
+ data on certain connections, typically shoutcast
+. avoid segv bugs on flushed shout connections.
+. make file save time-reset work again.
+. fixed a bug on first file created as the first audio page
+ would only contain one packet.
+. updated timing code to be more precise on ogg files with gaps
+ at the beginning.
+. Added sleep param to playlist to not sleep when reading ogg
+ files. not to be used with shout connections but fine for
+ disk writes.
+
+1.32
+----
+. fix-up NULL dereference on exit for passthru vorbis case,
+. do instance cleanup at end of runner.
+. add better timer logic into playlist, so that long sleeps
+ don't occur on ogg files with gaps at the start.
+
+1.31
+----
+. requires libshout kh14 (API change, more inline with CVS)
+. ogg test now check for a ogg v1.0 call
+. om_shout now flushes pages every 2 seconds, if not less
+
+1.30
+----
+. lots of dead code removal
+. place specifc output modules in separate files, more modular
+ this allows for specifying multiple savestreams and shout
+ connections per instance. (note config file change)
+. separate reencode packetin from pagein for later work.
+. various little cleanups, name changes etc
+
+1.29
+----
+. minor cleanups for distributing files.
+. add optional debug for checking pthread flags.
+. a few memory leaks fixes.
+
+1.28
+----
+. Adeed calls to reencode to take pages and produce packets for output,
+ this removes the page build -> spilt -> re-build logic.
+. Add comment tag for reencode for encoder identification
+. push the drop priviledge code to before the log file creation.
+. drop any special privs when reading the config file.
+
+1.27
+----
+. Added encode API based on packets instead of pages
+. Added output API based on packets instead of pages.
+. Changed PCM->vorbis to use the packet-based encoding route, saves
+ needless splitting and recalculation of granulepos. reencode still
+ uses pages.
+. Added realtime option for setting scheduling policy
+. Added support for dropping setuid or change to different user.
+
+1.26
+----
+. added fmask/dmask for file creation permissions (0600/0700)
+. updated auto* build to CVS style
+. added reset-time tag (default 1) in savestream to modify granulepos
+ on file change
+
+1.25
+----
+. made im_pcm do back off when buffers have run out
+. renamed setup.* to cfgparse.* to match CVS
+
+1.24
+----
+. renamed the res_ functions/structure as they can clash with the resolver
+. cleaned up the build a bit more
+. split acinclude in the separate m4 macros
+. another fix for getting the samplerate on reencoding correct
+
+1.23
+----
+. reencoding with resampling/downmixing was having problems
+. im_pcm args support failed for old source parameter.
+
+1.22
+----
+. more playlist work, was not starting the reencoder, and did not provide
+ the sample counts correctly
+. im_oss didn't increase senttime which caused a large catchup occurring
+ for any following modules, leading to buffer starvation.
+. Provide a mechanism for providing args for the command on im_pcm. Useful
+ for ogg123 as it doesn't deal with close output pipes correctly.
+
+1.21
+----
+. The input_sleep routine was working the sleep time wrong
+. playlist needed work for the EOS handling
+
+1.20
+----
+. improved the save stream naming, so that subdirs are created when needed
+. change default duration of a file to 1 hour and allow 0 to disable
+. add <on-metadata> tag in save stream to enable metadata to switch the file.
+
+1.19
+----
+. made input modules handle input module switchover better.
+ Needed EOS handling for input module
+ fix for negative granulepos on switchover.
+. removed debugging printf
+. Allowed for creating sub-directories when writing files.
+. use shout_audio_info to state samplerate and channels at connection start, this
+ probably needs done better as that can change mid connection.
+
+1.18
+----
+. updated config file for stream info
+. various autoconf build updates
+
+1.17
+---
+. removed the metadata section, now you just state the stream info per instance or
+ globally.
+. changed build setup, now checks for pthreads better and supplies much of the
+ build information without much trouble
+
+1.16
+----
+. minor fix for reconnection of streams.
+. CVS alsa input copied but not integrated
+, comment out "increase max buffers" until a better place can be found.
+
+1.15
+----
+. trap for max input buffers in im_oss, reading audio into an unused buffer until
+ either an input buffer is availble or a certain amount of time has elapsed, help
+ with sudden loads.
+. set eos on on shutdown in input thread (oss, pcm)
+. don't do select in pcm module when no time is to be elapsed.
+. add timestamp to input module, and trap from restarting inputs too quickly
+. input thread now closes the first input which causes a chain of pipe closes, a
+ close of the runner pipe acts as the runner shutdown
+. improved debug messages
+. fixed up packet granulepos calculation so that valid ogg pages can be created.
+ This needs more work as some cases still have problems, like input switch
+. bother file and shout streams should start at granulepos 0.
+. timing_sleep need not call select if no sleeping required.
+
+1.14
+----
+. continuing from before, the outgoing libshout stream is now built separately from
+ the internal ogg pages. This applies to all setups. Ther serial numbers for file
+ and libshout streams are incremented each time.
+. im_pcm im_oss now set eos on signal then critical on next buffer.
+. libshout startup is pushed to the output ogg page stage, this simplifies the
+ high end flow control in the runner, and prevents encoder/reencoder initialising
+ on libshout connect.
+. fixed some parsing, show warnings for unknown tags.
+
+1.13
+----
+. Changed what save stream stores, now builds a separate ogg stream
+. default timeout to 5 seconds on im_pcm.
+. allow for no specifying a hostname, eg write to disk only
+
+1.12
+----
+. warn if trying to encode 0 samples.
+. make OSS module uninterleave
+. sort out metadata for OSS and pcm inputs, check the file at start as well.
+. playlist is available now, may need more testing.
+. log read comments from metadata file.
+. re-organise the setup/shutdown on stream change.
+. allow for writing stream to disk, even when the connection to icecast is gone
+. allow for file name expansion (strftime) for file to write
+
+1.11
+----
+. Changes to the autoconf build.
+. pushed PCM uninterleaving into input thread.
+. start to break up re-encoding, currently broken
+. Made iintial OSS input work, uninterleave work still needed.
+. Changed how silence detection worked for PCM input, it had
+ problems with EOS detection.
+. Changed // to /* */
+
+1.10
+----
+. force encoder quality values between -1 and 10 inclusive.
+. Change trigger level for samples per page before flushing.
+. Change pcm module to emit silence buffers when nothing has
+ been read. Useful for masking latencies of external sources
+. Change resolution of senttime back to uS as the ms count has
+ frational part causing sync drift
+
+1.9
+---
+. fixed stereo bug on playing some tracks.
+. terminate properly on unrunnable scripts.
+
+1.8
+---
+. Allocate encoder/reencoder at instance creation
+. Compile condition for using pipe as oppose to pthread conditionals
+. Change encode parsing
+ - drop samplerate/channels out of encode parsing.
+ - Add don't encodde vorbis.
+ - Handle comments
+ - warn of unrecognised options
+. don't allocate shout structure twice per instance
+. Free up hostname/mount/password from xml after passing to shout
+. Change encoder setup/shutdown routines.
+. Add checks for samples in page. If 0 don't try the vorbis code.
+. Add close stdin in im_pcm for the spawned script, detach keyboard.
+. Update re-encoder routines.
+. Added reencode_clear in flush routine to clear up memory sooner.
+. Added require_critical to stream to act as a per stream critical flag.
+. Removed maxqueuelength
+
+1.7
+---
+. Re-introduce pipes between runners to see if there is any performance
+ difference between these and the signals used by pthreads.
+
+1.6
+---
+. Chenged free buffers check from WARN to ERROR, as it's pretty final.
+. Completely zap MAX_INPUT_BUFFERS and increase the MIN_INPUT_BUFFER
+. Change wait period after the input thread has started runners to 1sec.
+. Don't treat comments as unknown in input parameter list.
+. moved default_len to pcm-specifc structure.
+. Added samples parameter to pcm input, default to 12288.
+. experimental - try quicker uninterleave in encode_data.
+
+
+1.5:
+----
+
+. Replace MAX_INPUT_BUFFER with MIN_INPUT_BUFFERS
+. Get main input code to initialise the buffers, calling a module specific
+function.
+. Changed im_pcm read to loop until a buffer is full instead of just sending
+only what was read.
+. put -march in configure.in. Needs proper extra args processing for makefile
+. Changed id values to start from 1 instead of 0
+. Changed sample_in_page limit to force a flush.
+. Added buffers tag in input_module to allow for overriding default.
+. Remove im_pcm specific buffers parameter.
+. misc indent cleanups, zap input_fluish_queue.
+. move control structure.
+. reduce sleep after runners started from to 500ms
+. cleanup send_to_runner.
+. LOG messages cleanup
+
Modified: icecast/branches/kh/ices/README
===================================================================
--- icecast/trunk/ices/README 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/README 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,19 +1,3 @@
-This is ices 2.0
+this is ices 2.0
-This is a beta release of ices 2.0. It's the primary source client
-for icecast 2.0. It has proven to stable and well featured.
-
-Documentation is in the doc subdirectory, and some sample configuration
-files exist in the conf subdirectory.
-
-To build and install, run
- ./configure
- make
- make install
-
-You WILL need to edit the config file you choose to use before running
-this application.
-
-For help, email us at icecast at xiph.org, icecast-dev at xiph.org, or find us
-on IRC at irc.freenode.net, in #icecast.
-
+it's the primary source client for icecast 2.0
Modified: icecast/branches/kh/ices/TODO
===================================================================
--- icecast/trunk/ices/TODO 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/TODO 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,5 +1,34 @@
-- cue files?
-- stream rewriting for serial number increment, etc.
-- graceful shutdown is broken (at least for resampling/live input, possibly
- others)
+. loss of audio sync on playlist USR1/USR2 is cause by the timer accounting for the
+page granulepos but not all the packets are extracted and sent to icecast.
+. do the vorbis granulepos calculations in the playlist and send out packets instead
+of pages. The output is ok now for taking packets.
+
+. playlist needs re-org to do packet sending and possible program spawn for external
+decode like mp3 transcoding. Use libcurl for direct ogg URL to packet
+sending.
+
+. pcm input needs to drop the silence detection logic, and also default to do no
+sleeping as most uses are externally timed anyway.
+
+. ALSA needs to be worked on to be usable
+
+. make output mechanism more modular, so that multiple output can be done
+
+ <shout>
+ <hostname>....</hostname>
+ <port>....</port>
+ </shout>
+
+. make passthough work better, currently sends pages which are then split up.
+
+. separate cfg parsing to individual modules, with the core left in cfgparser.c
+
+considerations.
+
+libcurl 7.10 needed for no signal support
+
+getting the playlist to produce packets instead of pages will resolve the extended
+sleep check better and may resolve the lost samples on USR1/2.
+
+
Modified: icecast/branches/kh/ices/conf/Makefile.am
===================================================================
--- icecast/trunk/ices/conf/Makefile.am 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/Makefile.am 2004-07-11 18:40:08 UTC (rev 7098)
@@ -2,5 +2,5 @@
AUTOMAKE_OPTIONS = foreign
-dist_pkgdata_DATA = ices-live.xml ices-playlist.xml
+EXTRA_DIST = ices-live.xml ices-playlist.xml ices-pcm.xml ices-pcm2.xml ices-switch.xml ices-fixit.xml
Added: icecast/branches/kh/ices/conf/ices-fixit.xml
===================================================================
--- icecast/trunk/ices/conf/ices-fixit.xml 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-fixit.xml 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<!--
+This config is used to take a list of ogg files from a playlist and
+processes them without any speed restrictions. The sleep param is the
+tag that disables the timing, if set to 0, make sure that an icecast
+server isn't used as that will saturate the service quickly.
+
+The once tag will cause ices to stop after the last input ends, normally
+it would loop back to the first one, in the case below the playlist would
+be restarted again.
+
+The passthru tag prevents ogg vorbis data being re-encoded to the
+default encoder settings (q3)
+-->
+<ices>
+
+ <background>0</background> <!-- run in background? (unimplemented) -->
+ <logpath>/tmp</logpath> <!-- where logs, etc go. -->
+ <logfile>ices.log</logfile>
+ <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+ <logsize>2048</logsize> <!-- the size the log file must be before rotation -->
+ <consolelog>0</consolelog> <!-- logfile is ignored if this is set to 1 -->
+ <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
+
+ <stream>
+ <name>Example stream name</name>
+ <genre>Example genre</genre>
+ <description>A short description of your stream</description>
+
+ <once>1</once>
+ <input>
+ <module>playlist</module>
+ <param name="type">basic</param>
+ <param name="file">playlist.txt</param>
+ <param name="random">0</param>
+ <param name="once">1</param>
+ <param name="sleep">0</param>
+ </input>
+
+ <runner>
+ <instance>
+ <passthru>1</passthru>
+ <savestream>
+ <!-- this splits the file on each header update -->
+ <!-- save-1.ogg save-2.ogg .... -->
+ <on-metadata>1</on-metadata>
+ <fmask>0644</fmask>
+ <dmask>0755</dmask>
+ </savestream>
+
+ </instance>
+ </runner>
+
+ </stream>
+</ices>
Added: icecast/branches/kh/ices/conf/ices-jack.xml
===================================================================
--- icecast/trunk/ices/conf/ices-jack.xml 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-jack.xml 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<ices>
+ <background>0</background> <!-- run in background? (unimplemented) -->
+ <realtime>0</realtime> <!-- disable realtime, enabled by default -->
+ <user>ices</user> <!-- user to change to when started as root -->
+ <logpath>/tmp</logpath> <!-- where log goes. -->
+ <logsize>2048</logsize> <!-- the size the log has to get to before cycling -->
+ <logfile>ices.log</logfile>
+ <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+ <consolelog>0</consolelog> <!-- logfile is ignored if this is set to 1 -->
+ <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
+
+ <stream>
+ <!-- global settings for all streams - optional-->
+ <name>Example stream name</name>
+ <genre>Example genre</genre>
+ <description>A short description of your stream</description>
+
+ <!-- input module -->
+ <!-- This example uses the 'jack' module. It takes input from the -->
+ <!-- jack ports and processes it for live -->
+ <!-- encoding. If metadatafilename is set then at start and on USR1 -->
+ <!-- the file is read and the comments are added into the stream -->
+ <input>
+ <module>jack</module>
+ <param name="channels">2</param> <!-- number channels that will be available as jack ports-->
+ <param name="clientname">ices</param> <!-- jackclient name -->
+ <param name="metadatafilename">metadata</param>
+ </input>
+ <!-- more input section can be stated here, and can be switched manually -->
+ <!-- by USR2 or whenever the previous input finishes. The order is -->
+ <!-- dictated in here and loops aronnd to the first one listed -->
+
+ <!-- A runner is a thread that applies the input data to each outgoing -->
+ <!-- stream instance defined within it. Multiple runners can be stated -->
+ <!-- for use on multiple processors. -->
+ <runner>
+ <!-- stream instance, used to associate a set of encoding settings -->
+ <!-- with output. At the moment 2 outputs can be used, shout and -->
+ <!-- savestream. Any number or combination of these outputs can be used -->
+ <instance>
+ <!-- per instance setting, overriding the global settings - optional-->
+ <name>test transmission</name>
+ <genre>various</genre>
+ <description>low bandwidth stream</description>
+
+ <!-- You define hostname and port for the server here, along with -->
+ <!-- the source password and mountpoint. If you miss them out -->
+ <!-- then any processing will still occur but it won't be sent to -->
+ <!-- icecast, useful for encode to file only -->
+ <shout>
+ <hostname>localhost</hostname>
+ <port>8000</port>
+ <password>hackme</password>
+ <mount>/example1.ogg</mount>
+ </shout>
+
+ <!-- resample input to the stated samplerate - optional
+ the input can change samplerate so this can be used to fix it
+ at a certain rate -->
+ <!--
+ <resample>
+ <out-rate>22050</out-rate>
+ </resample>
+ -->
+
+ <!-- stereo->mono downmixing, enabled by setting this to 1 - optional -->
+ <!--
+ <downmix>1</downmix>
+ -->
+ <!-- Live encoding/reencoding: -->
+ <encode>
+ <quality>1.1</quality>
+ <!-- usual options for encoding, except from samplerate and chanels -->
+ <!-- they are set from input/resample/downmix setting -->
+ </encode>
+ </instance>
+
+ <!-- more instances can be defined -->
+ <instance>
+ <!-- This instance just writes to file, no connection to icecast -->
+ <encode>
+ <quality>1.1</quality>
+ </encode>
+
+ <!-- writing of files, all but the filename have the defaults-->
+ <!-- listed -->
+ <savestream>
+ <!-- filename expansion, look at strftime for details -->
+ <filename>/home/ices/saved-file/%X/stream-%M.ogg</filename>
+ <!-- file creation mask, eg 0644 -->
+ <fmask>0600</fmask>
+ <!-- directory creation mask -->
+ <dmask>0700</dmask>
+ <!-- seconds to record, 0 disables, defaults to 1 hour -->
+ <duration>7200</duration>
+ <!-- switch file on stream change -->
+ <on-metadata>1</on-metadata>
+ <!-- Normally on switchover from a duration timeout, the
+ timecode is reset, needed for some players, but disabling
+ this prevents modification -->
+ <reset-time>0</reset-time>
+ </savestream>
+
+ </instance>
+
+ </runner>
+
+ <runner>
+ ....
+ </runner>
+
+ </stream>
+</ices>
+
Modified: icecast/branches/kh/ices/conf/ices-live.xml
===================================================================
--- icecast/trunk/ices/conf/ices-live.xml 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-live.xml 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,94 +1,114 @@
<?xml version="1.0"?>
<ices>
- <!-- run in background -->
- <background>0</background>
- <!-- where logs go. -->
- <logpath>/var/log/ices</logpath>
- <logfile>ices.log</logfile>
- <!-- 1=error, 2=warn, 3=infoa ,4=debug -->
- <loglevel>4</loglevel>
- <!-- logfile is ignored if this is set to 1 -->
- <consolelog>0</consolelog>
+ <background>0</background> <!-- run in background? (unimplemented) -->
+ <realtime>0</realtime> <!-- disable realtime, enabled by default -->
+ <user>ices</user> <!-- user to change to when started as root -->
+ <logpath>/tmp</logpath> <!-- where log goes. -->
+ <logsize>2048</logsize> <!-- the size the log has to get to before cycling -->
+ <logfile>ices.log</logfile>
+ <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+ <consolelog>0</consolelog> <!-- logfile is ignored if this is set to 1 -->
+ <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
- <!-- optional filename to write process id to -->
- <!-- <pidfile>/home/ices/ices.pid</pidfile> -->
+ <stream>
+ <!-- global settings for all streams - optional-->
+ <name>Example stream name</name>
+ <genre>Example genre</genre>
+ <description>A short description of your stream</description>
- <stream>
- <!-- metadata used for stream listing -->
- <metadata>
- <name>Example stream name</name>
- <genre>Example genre</genre>
- <description>A short description of your stream</description>
- <url>http://mysite.org</url>
- </metadata>
+ <!-- input module -->
+ <!-- This example uses the 'oss' module. It takes input from the -->
+ <!-- oss audio device (i.e. line-in), and processes it for live -->
+ <!-- encoding. If metadatafilename is set then at start and on USR1 -->
+ <!-- the file is read and the comments are added into the stream -->
+ <input>
+ <module>oss</module>
+ <param name="rate">44100</param> <!-- samplerate -->
+ <param name="channels">2</param> <!-- number of channels -->
+ <param name="device">/dev/dsp</param> <!-- audio device -->
+ <param name="metadatafilename">metadata</param>
+ </input>
+ <!-- more input section can be stated here, and can be switched manually -->
+ <!-- by USR2 or whenever the previous input finishes. The order is -->
+ <!-- dictated in here and loops aronnd to the first one listed -->
- <!-- Input module.
+ <!-- A runner is a thread that applies the input data to each outgoing -->
+ <!-- stream instance defined within it. Multiple runners can be stated -->
+ <!-- for use on multiple processors. -->
+ <runner>
+ <!-- stream instance, used to associate a set of encoding settings -->
+ <!-- with output. At the moment 2 outputs can be used, shout and -->
+ <!-- savestream. Any number or combination of these outputs can be used -->
+ <instance>
+ <!-- per instance setting, overriding the global settings - optional-->
+ <name>test transmission</name>
+ <genre>various</genre>
+ <description>low bandwidth stream</description>
+
+ <!-- You define hostname and port for the server here, along with -->
+ <!-- the source password and mountpoint. If you miss them out -->
+ <!-- then any processing will still occur but it won't be sent to -->
+ <!-- icecast, useful for encode to file only -->
+ <shout>
+ <hostname>localhost</hostname>
+ <port>8000</port>
+ <password>hackme</password>
+ <mount>/example1.ogg</mount>
+ </shout>
- This example uses the 'oss' module. It takes input from the
- OSS audio device (e.g. line-in), and processes it for live
- encoding. -->
- <input>
- <module>oss</module>
- <param name="rate">44100</param>
- <param name="channels">2</param>
- <param name="device">/dev/dsp</param>
- <!-- Read metadata (from stdin by default, or -->
- <!-- filename defined below (if the latter, only on SIGUSR1) -->
- <param name="metadata">1</param>
- <param name="metadatafilename">test</param>
- </input>
+ <!-- resample input to the stated samplerate - optional
+ the input can change samplerate so this can be used to fix it
+ at a certain rate -->
+ <resample>
+ <out-rate>22050</out-rate>
+ </resample>
- <!-- Stream instance.
+ <!-- stereo->mono downmixing, enabled by setting this to 1 - optional -->
+ <downmix>1</downmix>
- You may have one or more instances here. This allows you to
- send the same input data to one or more servers (or to different
- mountpoints on the same server). Each of them can have different
- parameters. This is primarily useful for a) relaying to multiple
- independent servers, and b) encoding/reencoding to multiple
- bitrates.
+ <!-- Live encoding/reencoding: -->
+ <encode>
+ <quality>0</quality>
+ <!-- usual options for encoding, except from samplerate and chanels -->
+ <!-- they are set from input/resample/downmix setting -->
+ </encode>
+ </instance>
- If one instance fails (for example, the associated server goes
- down, etc), the others will continue to function correctly.
- This example defines a single instance doing live encoding at
- low bitrate. -->
+ <!-- more instances can be defined -->
+ <instance>
+ <!-- This instance just writes to file, no connection to icecast -->
+ <encode>
+ <quality>0</quality>
+ </encode>
- <instance>
- <!-- Server details.
+ <!-- writing of files, all but the filename have the defaults-->
+ <!-- listed -->
+ <savestream>
+ <!-- filename expansion, look at strftime for details -->
+ <filename>/home/ices/saved-file/%X/stream-%M.ogg</filename>
+ <!-- file creation mask, eg 0644 -->
+ <fmask>0600</fmask>
+ <!-- directory creation mask -->
+ <dmask>0700</dmask>
+ <!-- seconds to record, 0 disables, defaults to 1 hour -->
+ <duration>7200</duration>
+ <!-- switch file on stream change -->
+ <on-metadata>1</on-metadata>
+ <!-- Normally on switchover from a duration timeout, the
+ timecode is reset, needed for some players, but disabling
+ this prevents modification -->
+ <reset-time>0</reset-time>
+ </savestream>
+
+ </instance>
- You define hostname and port for the server here, along
- with the source password and mountpoint. -->
+ </runner>
- <hostname>localhost</hostname>
- <port>8000</port>
- <password>hackme</password>
- <mount>/example1.ogg</mount>
- <yp>1</yp> <!-- allow stream to be advertised on YP, default 0 -->
+ <runner>
+ ....
+ </runner>
- <!-- Live encoding/reencoding:
+ </stream>
+</ices>
- channels and samplerate currently MUST match the channels
- and samplerate given in the parameters to the oss input
- module above or the remsaple/downmix section below. -->
-
- <encode>
- <quality>0</quality>
- <samplerate>22050</samplerate>
- <channels>1</channels>
- </encode>
-
- <!-- stereo->mono downmixing, enabled by setting this to 1 -->
- <downmix>1</downmix>
-
- <!-- resampling.
-
- Set to the frequency (in Hz) you wish to resample to, -->
-
- <resample>
- <in-rate>44100</in-rate>
- <out-rate>22050</out-rate>
- </resample>
- </instance>
-
- </stream>
-</ices>
Added: icecast/branches/kh/ices/conf/ices-pcm.xml
===================================================================
--- icecast/trunk/ices/conf/ices-pcm.xml 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-pcm.xml 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<ices>
+
+ <background>0</background> <!-- run in background? (unimplemented) -->
+ <logpath>/tmp</logpath> <!-- where logs, etc go. -->
+ <logfile>ices.log</logfile>
+ <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+ <logsize>2048</logsize> <!-- the size the log file must be before rotation -->
+ <consolelog>0</consolelog> <!-- logfile is ignored if this is set to 1 -->
+ <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
+
+ <stream>
+ <input>
+ <module>pcm</module>
+ <param name="rate">44100</param>
+ <param name="channels">2</param>
+ <param name="metadatafilename">test</param> <!-- read metadata from file on USR1 -->
+ <!-- pipe for extracting raw pcm, if not defined stdin is assumed -->
+ <param name="command">/home/ices/get_pcm.sh</param>
+ <param name="timeout">5</param> <!-- If missing or zero then no timeout on reading PCM -->
+ </input>
+
+ <runner>
+ <instance>
+ <shout>
+ <hostname>the.hackers.club</hostname>
+ <port>8000</port>
+ <password>hackme</password>
+ <mount>/example1.ogg</mount>
+
+ <reconnectdelay>5</reconnectdelay>
+ <reconnectattempts>-1</reconnectattempts>
+ </shout>
+
+ <encode>
+ <quality>0</quality>
+ </encode>
+ </instance>
+
+ <instance>
+ <shout>
+ <hostname>localhost</hostname>
+ <port>8000</port>
+ <password>hackme</password>
+ <mount>/example.ogg</mount>
+
+ <reconnectdelay>5</reconnectdelay>
+ <reconnectattempts>-1</reconnectattempts>
+ </shout>
+
+ <encode>
+ <quality>-1</quality>
+ </encode>
+ </instance>
+ </runner>
+
+ </stream>
+</ices>
Added: icecast/branches/kh/ices/conf/ices-pcm2.xml
===================================================================
--- icecast/trunk/ices/conf/ices-pcm2.xml 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-pcm2.xml 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<ices>
+
+ <background>0</background> <!-- run in background? (unimplemented) -->
+ <logpath>/tmp</logpath> <!-- where logs, etc go. -->
+ <logfile>ices.log</logfile>
+ <logsize>2048</logsize> <!-- the size the log file must be before rotation -->
+ <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+ <consolelog>0</consolelog> <!-- logfile is ignored if this is set to 1 -->
+ <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
+
+ <stream>
+
+ <input>
+ <module>pcm</module>
+ <param name="rate">44100</param>
+ <param name="channels">2</param>
+ <param name="metadatafilename">test</param>
+ <param name="command">/usr/bin/ogg123</param>
+ <param name="arg">--device=raw</param>
+ <param name="arg">--quiet</param>
+ <param name="arg">--file=-</param>
+ <param name="arg">/path/or/url.ogg</param>
+ <!-- limit of about 10 args currently, needs to be implemented better -->
+ </input>
+
+ <runner>
+ <instance>
+ <shout>
+ <hostname>localhost</hostname>
+ <port>8000</port>
+ <password>hackme</password>
+ <mount>/example1.ogg</mount>
+ <reconnectdelay>5</reconnectdelay>
+ <reconnectattempts>-1</reconnectattempts>
+ <shout>
+
+ <shout>
+ <hostname>other.host</hostname>
+ <port>8000</port>
+ <password>hackme</password>
+ <mount>/example1.ogg</mount>
+ </shout>
+
+ <encode>
+ <quality>0</quality>
+ </encode>
+ </instance>
+
+ <instance>
+ <hostname>localhost</hostname>
+ <port>8000</port>
+ <password>hackme</password>
+ <mount>/example2.ogg</mount>
+
+ <reconnectdelay>5</reconnectdelay>
+ <reconnectattempts>-1</reconnectattempts>
+
+ <encode>
+ <quality>-1</quality>
+ </encode>
+ </instance>
+ </runner>
+
+ </stream>
+</ices>
Modified: icecast/branches/kh/ices/conf/ices-playlist.xml
===================================================================
--- icecast/trunk/ices/conf/ices-playlist.xml 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-playlist.xml 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,99 +1,47 @@
<?xml version="1.0"?>
<ices>
- <!-- run in background -->
- <background>0</background>
- <!-- where logs, etc go. -->
- <logpath>/var/log/ices</logpath>
- <logfile>ices.log</logfile>
- <!-- 1=error,2=warn,3=info,4=debug -->
- <loglevel>4</loglevel>
- <!-- set this to 1 to log to the console instead of to the file above -->
- <consolelog>0</consolelog>
- <!-- optional filename to write process id to -->
- <!-- <pidfile>/home/ices/ices.pid</pidfile> -->
+ <background>0</background> <!-- run in background? (unimplemented) -->
+ <logpath>/tmp</logpath> <!-- where logs, etc go. -->
+ <logfile>ices.log</logfile>
+ <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+ <logsize>2048</logsize> <!-- the size the log file must be before rotation -->
+ <consolelog>0</consolelog> <!-- set this to 1 to log to the console instead
+ of to the file above -->
+ <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
- <stream>
- <!-- metadata used for stream listing (not currently used) -->
- <metadata>
- <name>Example stream name</name>
- <genre>Example genre</genre>
- <description>A short description of your stream</description>
- </metadata>
+ <stream>
+
+ <!-- input module -->
+ <input>
+ <module>playlist</module>
+ <param name="type">basic</param> <!-- Only 'basic' and script implemented -->
+ <param name="file">playlist.txt</param> <!-- be sure this exists -->
+ <param name="random">0</param> <!-- random play -->
+ <param name="once">0</param> <!-- if set to 1 , plays once through, then exits. -->
+ </input>
- <!-- input module
+ <runner>
+ <!-- Stream instance -->
+ <instance>
+ <shout>
+ <!-- Server details: -->
+ <hostname>localhost</hostname>
+ <port>8000</port>
+ <password>hackme</password>
+ <mount>/example1.ogg</mount>
- The module used here is the playlist module - it has
- 'submodules' for different types of playlist. There are
- two currently implemented, 'basic', which is a simple
- file-based playlist, and 'script' which invokes a command
- to returns a filename to start playing. -->
+ <reconnectdelay>2</reconnectdelay>
+ <reconnectattempts>5</reconnectattempts>
+ </shout>
- <input>
- <module>playlist</module>
- <param name="type">basic</param>
- <param name="file">playlist.txt</param>
- <!-- random play -->
- <param name="random">0</param>
- <!-- if the playlist get updated that start at the beginning -->
- <param name="restart-after-reread">0</param>
- <!-- if set to 1 , plays once through, then exits. -->
- <param name="once">0</param>
- </input>
-
- <!-- Stream instance
- You may have one or more instances here. This allows you to
- send the same input data to one or more servers (or to different
- mountpoints on the same server). Each of them can have different
- parameters. This is primarily useful for a) relaying to multiple
- independent servers, and b) encoding/reencoding to multiple
- bitrates.
- If one instance fails (for example, the associated server goes
- down, etc), the others will continue to function correctly.
- This example defines two instances as two mountpoints on the
- same server. -->
- <instance>
- <!-- Server details:
- You define hostname and port for the server here, along with
- the source password and mountpoint. -->
- <hostname>localhost</hostname>
- <port>8000</port>
- <password>hackme</password>
- <mount>/example1.ogg</mount>
-
- <!-- Reconnect parameters:
- When something goes wrong (e.g. the server crashes, or the
- network drops) and ices disconnects from the server, these
- control how often it tries to reconnect, and how many times
- it tries to reconnect. Delay is in seconds.
- If you set reconnectattempts to -1, it will continue
- indefinately. Suggest setting reconnectdelay to a large value
- if you do this.
- -->
- <reconnectdelay>2</reconnectdelay>
- <reconnectattempts>5</reconnectattempts>
-
- <!-- maxqueuelength:
- This describes how long the internal data queues may be. This
- basically lets you control how much data gets buffered before
- ices decides it can't send to the server fast enough, and
- either shuts down or flushes the queue (dropping the data)
- and continues.
- For advanced users only.
- -->
- <maxqueuelength>80</maxqueuelength>
-
- <!-- Live encoding/reencoding:
- Currrently, the parameters given here for encoding MUST
- match the input data for channels and sample rate. That
- restriction will be relaxed in the future.
- -->
- <encode>
- <nominal-bitrate>64000</nominal-bitrate> <!-- bps. e.g. 64000 for 64 kbps -->
- <samplerate>44100</samplerate>
- <channels>2</channels>
- </encode>
- </instance>
-
+ <encode>
+ <!-- VBR mode can be selected via quality or nominal-bitrate -->
+ <!-- selecting enabling managed has to be done separately as -->
+ <!-- it's slower, but can be used to limit a high threshold -->
+ <nominal-bitrate>65536</nominal-bitrate> <!-- bps. e.g. 64 kbps -->
+ </encode>
+ </instance>
+ </runner>
</stream>
</ices>
Added: icecast/branches/kh/ices/conf/ices-switch.xml
===================================================================
--- icecast/trunk/ices/conf/ices-switch.xml 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/conf/ices-switch.xml 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<ices>
+
+ <background>0</background> <!-- run in background? (unimplemented) -->
+ <logpath>/tmp</logpath> <!-- where logs, etc go. -->
+ <logfile>ices.log</logfile>
+ <loglevel>4</loglevel> <!-- 1=error,2=warn,3=info,4=debug -->
+ <logsize>2048</logsize> <!-- the size the log file must be before rotation -->
+ <consolelog>0</consolelog> <!-- logfile is ignored if this is set to 1 -->
+ <pidfile>/var/ices/ices.pid</pidfile> <!-- file to write process id to -->
+
+ <stream>
+
+ <input>
+ <module>oss</module>
+ <param name="rate">44100</param> <!-- samplerate -->
+ <param name="channels">2</param> <!-- number of channels -->
+ <param name="device">/dev/dsp</param> <!-- audio device -->
+ <param name="metadatafilename">metadata</param>
+ </input>
+
+ <input>
+ <module>pcm</module>
+ <param name="rate">44100</param>
+ <param name="channels">2</param>
+ <param name="metadatafilename">test</param>
+ <param name="command">/usr/bin/ogg123</param>
+ <param name="arg">--device=raw</param>
+ <param name="arg">--quiet</param>
+ <param name="arg">--file=-</param>
+ <param name="arg">/path/or/url.ogg</param>
+ <!-- limit of about 10 args currently -->
+ </input>
+
+ <runner>
+ <instance>
+ <shout>
+ <hostname>localhost</hostname>
+ <port>8000</port>
+ <password>hackme</password>
+ <mount>/example1.ogg</mount>
+
+ <reconnectdelay>5</reconnectdelay>
+ <reconnectattempts>-1</reconnectattempts>
+ </shout>
+
+ <encode>
+ <quality>0</quality>
+ </encode>
+ </instance>
+
+ <instance>
+ <shout>
+ <hostname>localhost</hostname>
+ <port>8000</port>
+ <password>hackme</password>
+ <mount>/example2.ogg</mount>
+
+ <reconnectdelay>5</reconnectdelay>
+ <reconnectattempts>-1</reconnectattempts>
+ </shout>
+
+ <!-- by default passthru is enabled which means that any
+ vorbis data coming from the input will inot be
+ re-encoded. Set to 0 if you want to re-encode tor
+ the settings in the encode section -->
+ <passthru>0</passthru>
+
+ <encode>
+ <quality>-1</quality>
+ </encode>
+ </instance>
+ </runner>
+
+ </stream>
+</ices>
Modified: icecast/branches/kh/ices/configure.in
===================================================================
--- icecast/trunk/ices/configure.in 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/configure.in 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script.
-AC_INIT([IceS], 2.0-Beta4, [icecast at xiph.org])
+AC_INIT([IceS], [2.0-kh59], [karl at xiph.org])
AC_PREREQ(2.54)
AC_CONFIG_SRCDIR(src/ices.c)
@@ -8,42 +8,36 @@
AM_MAINTAINER_MODE
AC_PROG_CC
-AM_PROG_LIBTOOL
+AC_PROG_LIBTOOL
+XIPH_C__FUNC__
dnl Set some options based on environment
-dnl BSD headers break when _XOPEN_SOURCE is defined but without it seems
-dnl to be fine
case "$host" in
- *bsd*)
+ *bsd*|*irix*)
;;
*) AC_DEFINE(_XOPEN_SOURCE, 600, [Define if you have POSIX and XPG specifications])
;;
esac
case "$host" in
*-*-irix*)
- DEBUG="-g -signed -D_REENTRANT"
- XIPH_CPPFLAGS="-O2 -w -signed -D_REENTRANT"
- PROFILE="-p -g3 -O2 -signed -D_REENTRANT"
+ DEBUG="-g -signed"
+ XIPH_CFLAGS="-O2 -w -signed"
+ PROFILE="-p -g3 -O2 -signed"
;;
*-*-solaris*)
- AC_DEFINE(__EXTENSIONS__, 1, [define to 1 to get resolve header problem on solaris])
- DEBUG="-v -g -D_REENTRANT"
- XIPH_CPPFLAGS="-xO4 -fast -w -fsimple -native -xcg92 -D_REENTRANT"
- PROFILE="-v -xpg -g -xO4 -fast -native -fsimple -xcg92 -Dsuncc -D_REENTRANT"
+ AC_DEFINE(__EXTENSIONS__, 1, [define to 1 for IPv6 functions on solaris])
+ DEBUG="-v -g"
+ XIPH_CFLAGS="-xO4 -fast -w -fsimple -native -xcg92"
+ PROFILE="-v -xpg -g -xO4 -fast -native -fsimple -xcg92 -Dsuncc"
;;
*)
- DEBUG="-g -D_REENTRANT"
- XIPH_CPPFLAGS="-O -D_REENTRANT"
- PROFILE="-g -p -D_REENTRANT"
+ AC_DEFINE(GNU_SOURCE, 1, [define to 1 to enable some GNU extensions])
+ DEBUG="-g"
+ XIPH_CFLAGS="-ffast-math -fsigned-char"
+ PROFILE="-g -p"
;;
esac
-if test -n "$GCC"; then
- AC_DEFINE(_GNU_SOURCE, ,[Define if you have POSIX and GNU specifications])
- XIPH_CPPFLAGS="-ffast-math -fsigned-char"
- DEBUG="-g"
- PROFILE="-g -pg"
-fi
dnl Checks for programs.
@@ -51,15 +45,16 @@
dnl Checks for header files.
AC_HEADER_STDC
-AC_CHECK_HEADERS([stropts.h])
+AC_HEADER_TIME
+AC_CHECK_HEADERS([grp.h alloca.h stropts.h])
dnl Check for OSS
AC_CHECK_HEADER(sys/soundcard.h, have_oss=yes, have_oss=no)
AC_CHECK_HEADER(machine/soundcard.h, have_oss=yes, )
-AM_CONDITIONAL(HAVE_OSS,test "$have_oss" = yes)
+AM_CONDITIONAL(HAVE_OSS_AUDIO,test "$have_oss" = yes)
if test "$have_oss" = yes; then
- AC_DEFINE(HAVE_OSS,,[Define to enable OSS input module])
+ AC_DEFINE(HAVE_OSS_AUDIO,,[Define to enable OSS input module])
fi
dnl Check for Sun audio
@@ -82,60 +77,111 @@
dnl Check for ALSA audio
-AC_CHECK_HEADER(alsa/asoundlib.h, have_alsa=yes, have_alsa=no)
-AM_CONDITIONAL(HAVE_ALSA,test "$have_alsa" = yes)
+AC_CHECK_HEADER([alsa/asoundlib.h], ices_have_alsa=yes, ices_have_alsa=no, [
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+])
+AM_CONDITIONAL(HAVE_ALSA_AUDIO,test "$ices_have_alsa" = yes)
-if test "$have_alsa" = yes; then
+if test "$ices_have_alsa" = yes; then
ALSA_LIBS="-lasound"
- AC_DEFINE(HAVE_ALSA, ,[Define to enable ALSA input module])
+ AC_DEFINE(HAVE_ALSA_AUDIO, ,[Define to enable ALSA input module])
fi
+dnl Check for JACK audio
+
+AC_CHECK_HEADER([jack/jack.h], ices_have_jack=yes, ices_have_jack=no, [
+])
+AM_CONDITIONAL(HAVE_JACK,test "$ices_have_jack" = yes)
+
+if test "$ices_have_jack" = yes; then
+ JACK_LIBS="-ljack"
+ AC_DEFINE(HAVE_JACK, ,[Define to enable JACK input module])
+fi
+
+
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
+xt_ices_LIBS="$LIBS"
+LIBS=""
+AC_SEARCH_LIBS([nanosleep],[rt],
+ [AC_DEFINE([HAVE_NANOSLEEP],1,[Define if you have the nanosleep function])
+ XIPH_VAR_PREPEND([XIPH_LIBS],[$LIBS])
+ ])
+LIBS="$xt_ices_LIBS"
+
+
dnl Check for types
AC_CHECK_TYPES([uint64_t],,AC_ERROR([could not find a uint64_t type]))
dnl Checks for library functions.
+AC_FUNC_STRERROR_R
-XIPH_PATH_XML
-XIPH_VAR_APPEND([XIPH_CFLAGS], [$XML_CFLAGS])
-XIPH_VAR_PREPEND([XIPH_LIBS], [$XML_LIBS])
+dnl -- configure options --
-XIPH_PATH_SHOUT(, AC_MSG_ERROR([must have libshout installed!]))
-if test "$SHOUT_THREADSAFE" != "yes"
-then
- AC_MSG_ERROR([This libshout isn't threadsafe])
+dnl deal with xml-config
+AC_MSG_RESULT([checking for XML configuration])
+AC_ARG_VAR([XMLCONFIG],[XML configuration program])
+AC_ARG_WITH(xml-config,
+ [AC_HELP_STRING([--with-xml-config=PATH],
+ [use xml-config in PATH to find libxml])],
+ [XMLCONFIG="$withval"],
+ [AC_PATH_PROGS(XMLCONFIG, [xml2-config xml-config], "")]
+)
+if test "x$XMLCONFIG" = "x"; then
+ AC_MSG_ERROR([XML configuration could not be found])
fi
+if ! test -x "$XMLCONFIG"; then
+ AC_MSG_ERROR([$XMLCONFIG cannot be executed])
+fi
+XML_LIBS="$($XMLCONFIG --libs)"
+XML_CFLAGS="$($XMLCONFIG --cflags)"
+xt_ices_LIBS="$LIBS"
+xt_ices_CFLAGS="$CFLAGS"
+LIBS="$XML_LIBS"
+CFLAGS="$CFLAGS $XML_CFLAGS"
+AC_CHECK_FUNC(xmlParseFile,, [AC_MSG_ERROR([There was a problem linking with libxml])])
+LIBS="$xt_ices_LIBS"
+CFLAGS="$xt_ices_CFLAGS"
+XIPH_VAR_PREPEND([XIPH_LIBS],[$XML_LIBS])
+XIPH_VAR_APPEND([XIPH_CFLAGS],[$XML_CFLAGS])
+XIPH_PATH_SHOUT(,AC_MSG_ERROR([Need libshout-kh release]))
XIPH_VAR_APPEND([XIPH_CPPFLAGS], [$SHOUT_CPPFLAGS])
XIPH_VAR_APPEND([XIPH_CFLAGS], [$SHOUT_CFLAGS])
-XIPH_VAR_PREPEND([XIPH_LIBS], [$SHOUT_LIBS])
+XIPH_VAR_PREPEND([XIPH_LIBS], [-lvorbisenc $SHOUT_LIBS])
-XIPH_PATH_VORBIS(, AC_MSG_ERROR([must have Ogg Vorbis v1.0 installed!]))
-XIPH_VAR_APPEND([XIPH_CPPFLAGS], [$VORBIS_CFLAGS $VORBISENC_CFLAGS])
-XIPH_VAR_PREPEND([XIPH_LIBS], [$VORBISENC_LIBS $VORBIS_LIBS])
+xt_save_LIBS=$LIBS
+xt_save_CFLAGS=$CFLAGS
+LIBS="$SHOUT_LIBS $LIBS"
+CFLAGS="$CFLAGS $SHOUT_CFLAGS"
+AC_CHECK_FUNCS(sched_get_priority_max)
+CFLAGS=$xt_save_CFLAGS
+LIBS=$xt_save_LIBS
dnl Make substitutions
AC_SUBST(ALSA_LIBS)
+AC_SUBST(JACK_LIBS)
AC_SUBST(XML_LIBS)
AC_SUBST(XML_CFLAGS)
AC_SUBST(LIBTOOL_DEPS)
AC_SUBST(DEBUG)
AC_SUBST(PROFILE)
+AC_SUBST(XIPH_LIBS)
AC_SUBST(XIPH_CFLAGS)
AC_SUBST(XIPH_CPPFLAGS)
-AC_SUBST(XIPH_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(LIBS)
+AC_SUBST(LDFLAGS)
-AC_OUTPUT(
- Makefile
- conf/Makefile
- doc/Makefile
- debian/Makefile
- src/Makefile
- src/log/Makefile
- src/timing/Makefile
- src/thread/Makefile
- src/avl/Makefile
-)
+AC_OUTPUT(Makefile conf/Makefile src/Makefile src/log/Makefile)
Modified: icecast/branches/kh/ices/src/Makefile.am
===================================================================
--- icecast/trunk/ices/src/Makefile.am 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/Makefile.am 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,16 +1,18 @@
## Process this with automake to create Makefile.in
-AUTOMAKE_OPTIONS = foreign 1.6
+AUTOMAKE_OPTIONS = foreign
-SUBDIRS = log timing thread avl
+SUBDIRS = log
bin_PROGRAMS = ices
+AM_CFLAGS = @XIPH_CFLAGS@
AM_CPPFLAGS = @XIPH_CPPFLAGS@
-AM_CFLAGS = @XIPH_CFLAGS@
+CFLAGS= -g -O20
+EXTRA_DIST = thread net timing avl
EXTRA_ices_SOURCES = im_oss.c im_sun.c im_alsa.c
-if HAVE_OSS
+if HAVE_OSS_AUDIO
oss = im_oss.c
endif
@@ -18,20 +20,24 @@
sun = im_sun.c
endif
-if HAVE_ALSA
+if HAVE_ALSA_AUDIO
alsa = im_alsa.c
endif
-dist_noinst_HEADERS = cfgparse.h input.h inputmodule.h im_playlist.h signals.h stream.h reencode.h encode.h playlist_basic.h logging.h im_stdinpcm.h event.h stream_shared.h metadata.h audio.h resample.h im_sun.h im_oss.h im_alsa.h
+if HAVE_JACK
+jack = im_jack.c
+endif
-ices_SOURCES = input.c cfgparse.c stream.c ices.c signals.c im_playlist.c reencode.c encode.c playlist_basic.c im_stdinpcm.c stream_shared.c metadata.c playlist_script.c audio.c resample.c $(oss) $(sun) $(alsa)
+input_hdrs = im_pcm.h im_sun.h im_oss.h im_alsa.h im_jack.h im_playlist.h playlist_basic.h
+output_hdrs = om_shout.h om_file.h
+dist_noinst_HEADERS = cfgparse.h inputmodule.h signals.h runner.h reencode.h encode.h logging.h stream.h metadata.h audio.h resample.h $(input_hdrs) $(output_hdrs)
-ices_LDADD = log/libicelog.la \
- timing/libicetiming.la \
- thread/libicethread.la \
- avl/libiceavl.la \
- @ALSA_LIBS@ @XIPH_LIBS@
+input_srcs = im_pcm.c im_playlist.c playlist_basic.c playlist_script.c $(oss) $(sun) $(alsa) $(jack)
+output_srcs = om_shout.c om_file.c
+ices_SOURCES = input.c cfgparse.c runner.c ices.c signals.c reencode.c encode.c stream.c metadata.c audio.c resample.c $(input_srcs) $(output_srcs)
+ices_LDADD = log/libicelog.la @ALSA_LIBS@ @JACK_LIBS@ @XIPH_LIBS@
+
debug:
$(MAKE) all CFLAGS="@DEBUG@"
Modified: icecast/branches/kh/ices/src/audio.c
===================================================================
--- icecast/trunk/ices/src/audio.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/audio.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -2,7 +2,7 @@
* stereo->mono downmixing
* resampling
*
- * $Id: audio.c,v 1.10 2003/08/01 22:38:04 karl Exp $
+ * $Id: audio.c,v 1.6 2003/03/15 02:24:18 karl Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -11,9 +11,8 @@
* it under the terms of this license. A copy should be included
* with this source.
*/
-
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include <config.h>
#endif
#include <stdio.h>
@@ -28,15 +27,15 @@
#define MODULE "audio/"
#include "logging.h"
-downmix_state *downmix_initialise(void) {
- downmix_state *state = calloc(1, sizeof(downmix_state));
+struct downmix *downmix_initialise(void) {
+ struct downmix *state = calloc(1, sizeof(struct downmix));
LOG_INFO0("Enabling stereo->mono downmixing");
return state;
}
-void downmix_clear(downmix_state *s) {
+void downmix_clear(struct downmix *s) {
if(s) {
if (s->buffer)
free(s->buffer);
@@ -44,7 +43,7 @@
}
}
-void downmix_buffer_float(downmix_state *s, float **buf, int samples)
+void downmix_buffer_float(struct downmix *s, float **buf, int samples, unsigned channels)
{
int i;
@@ -57,13 +56,18 @@
}
for(i=0; i < samples; i++) {
- s->buffer[i] = (buf[0][i] + buf[1][i])*0.5;
+ unsigned count = 0;
+ float res = 0.0;
+ for (count = 0; count < channels; count++)
+ res += buf [count][i];
+ s->buffer[i] = res/channels;
}
}
-void downmix_buffer(downmix_state *s, signed char *buf, int len, int be)
+#if 0
+void downmix_buffer(struct downmix *s, signed char *buf, int len, int be)
{
int samples = len/4;
int i;
@@ -89,10 +93,11 @@
}
}
}
+#endif
-resample_state *resample_initialise(int channels, int infreq, int outfreq)
+struct resample *resample_initialise(int channels, int infreq, int outfreq)
{
- resample_state *state = calloc(1, sizeof(resample_state));
+ struct resample *state = calloc(1, sizeof(struct resample));
int failed = 1;
do
@@ -126,7 +131,7 @@
return state;
}
-void resample_clear(resample_state *s)
+void resample_clear(struct resample *s)
{
int c;
@@ -148,7 +153,7 @@
}
}
-void resample_buffer(resample_state *s, signed char *buf, int buflen, int be)
+void resample_buffer(struct resample *s, signed char *buf, int buflen, int be)
{
int c,i;
buflen /= 2*s->channels; /* bytes -> samples conversion */
@@ -181,7 +186,7 @@
resample_buffer_float(s, s->convbuf, buflen);
}
-void resample_buffer_float(resample_state *s, float **buf, int buflen)
+void resample_buffer_float(struct resample *s, float **buf, int buflen)
{
int c;
int res;
@@ -208,7 +213,7 @@
}
-void resample_finish(resample_state *s)
+void resample_finish(struct resample *s)
{
int ret;
Modified: icecast/branches/kh/ices/src/audio.h
===================================================================
--- icecast/trunk/ices/src/audio.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/audio.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -17,13 +17,15 @@
#include "resample.h"
-typedef struct {
+struct downmix
+{
float *buffer;
int buflen;
-} downmix_state;
+};
-typedef struct {
- resampler_state resampler;
+struct resample
+{
+ struct resampler resampler;
int channels;
float **buffers;
@@ -32,18 +34,18 @@
float **convbuf;
int convbuflen;
-} resample_state;
+};
-downmix_state *downmix_initialise(void);
-void downmix_clear(downmix_state *s);
-void downmix_buffer(downmix_state *s, signed char *buf, int len, int be);
-void downmix_buffer_float(downmix_state *s, float **buf, int samples);
+struct downmix *downmix_initialise(void);
+void downmix_clear(struct downmix *s);
+/* void downmix_buffer(struct downmix *s, signed char *buf, int len, int be); */
+void downmix_buffer_float(struct downmix *s, float **buf, int samples, unsigned channels);
-resample_state *resample_initialise(int channels, int infreq, int outfreq);
-void resample_clear(resample_state *s);
-void resample_buffer(resample_state *s, signed char *buf, int buflen, int be);
-void resample_buffer_float(resample_state *s, float **buf, int buflen);
-void resample_finish(resample_state *s);
+struct resample *resample_initialise(int channels, int infreq, int outfreq);
+void resample_clear(struct resample *s);
+void resample_buffer(struct resample *s, signed char *buf, int buflen, int be);
+void resample_buffer_float(struct resample *s, float **buf, int buflen);
+void resample_finish(struct resample *s);
#endif
Modified: icecast/branches/kh/ices/src/cfgparse.c
===================================================================
--- icecast/trunk/ices/src/cfgparse.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/cfgparse.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,8 +1,7 @@
/* cfgparse.c
- * - cfgparse file reading code, plus default settings.
+ * - setup file reading code, plus default settings.
*
- * $Id: cfgparse.c,v 1.10 2004/03/11 17:16:08 karl Exp $
- *
+ * Copyright (c) 2002-4 Karl Heyes <karl at xiph.org>
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
* This program is distributed under the terms of the GNU General
@@ -12,495 +11,430 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#ifdef HAVE_STRINGS_H
+ #include <strings.h>
+#endif
+
+#include <ogg/ogg.h>
+
/* these might need tweaking for other systems */
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
-#include <thread/thread.h>
+#include "thread/thread.h"
#include "cfgparse.h"
-#include "stream.h"
+#include "thread/thread.h"
+#include "inputmodule.h"
+#include "encode.h"
+#include "runner.h"
+#include "om_file.h"
+#include "om_shout.h"
-#define DEFAULT_BACKGROUND 0
+#define DEFAULT_PLAYLIST_MODULE "playlist"
+#define DEFAULT_STREAM_NAME "unnamed ices stream"
+#define DEFAULT_STREAM_GENRE "ices unset"
+#define DEFAULT_STREAM_DESCRIPTION "no description set"
#define DEFAULT_LOGPATH "/tmp"
#define DEFAULT_LOGFILE "ices.log"
#define DEFAULT_LOGLEVEL 1
#define DEFAULT_LOGSIZE 2048
#define DEFAULT_LOG_STDERR 1
-#define DEFAULT_STREAM_NAME "unnamed ices stream"
-#define DEFAULT_STREAM_GENRE "ices unset"
-#define DEFAULT_STREAM_DESCRIPTION "no description set"
-#define DEFAULT_PLAYLIST_MODULE "playlist"
-#define DEFAULT_HOSTNAME "localhost"
-#define DEFAULT_PORT 8000
-#define DEFAULT_PASSWORD "password"
-#define DEFAULT_USERNAME NULL
-#define DEFAULT_MOUNT "/stream.ogg"
-#define DEFAULT_MANAGED 0
-#define DEFAULT_MIN_BITRATE -1
-#define DEFAULT_NOM_BITRATE -1
-#define DEFAULT_MAX_BITRATE -1
-#define DEFAULT_QUALITY 3
-#define DEFAULT_REENCODE 0
-#define DEFAULT_DOWNMIX 0
-#define DEFAULT_RESAMPLE 0
-#define DEFAULT_RECONN_DELAY 2
-#define DEFAULT_RECONN_ATTEMPTS 10
-#define DEFAULT_MAXQUEUELENGTH 100 /* Make it _BIG_ by default */
-#define DEFAULT_SAVEFILENAME NULL /* NULL == don't save */
+#define DEFAULT_BACKGROUND 0
-/* helper macros so we don't have to write the same
-** stupid code over and over
-*/
-#define SET_STRING(x) \
- do {\
- if (x) free(x);\
- (x) = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);\
- } while (0)
+#ifdef DEBUG_CFG
+#define dprintf(...) printf(__VA_ARGS__)
+#else
+#define dprintf(...)
+#endif
-#define SET_INT(x) \
- do {\
- char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);\
- (x) = atoi(tmp);\
- if (tmp) free(tmp);\
- } while (0)
+/* this is the global config variable */
+config_t *ices_config;
-#define SET_FLOAT(x) \
- do {\
- char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);\
- (x) = atof(tmp);\
- if (tmp) free(tmp);\
- } while (0)
-#define SET_PARM_STRING(p,x) \
- do {\
- if (x) free(x);\
- (x) = (char *)xmlGetProp(node, p);\
- } while (0)
+int get_xml_float (xmlNodePtr node, void *x)
+{
+ char *tmp = (char *)xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
+ if (tmp == NULL)
+ return -1;
+ *(float*)x = atof (tmp);
+ xmlFree(tmp);
+ return 0;
+}
-/* this is the global config variable */
-config_t *ices_config;
+int get_xml_bool (xmlNodePtr node, void *x)
+{
+ char *str = (char *)xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
+ if (str == NULL)
+ return -1;
+ if (strcasecmp (str, "true") == 0)
+ *(int*)x = 1;
+ else
+ if (strcasecmp (str, "yes") == 0)
+ *(int*)x = 1;
+ else
+ *(int*)x = strtol (str, NULL, 0)==0 ? 0 : 1;
+ xmlFree (str);
+ xmlMemoryDump();
+ return 0;
+}
-static int _using_default_instance = 1;
-static void _free_instances(instance_t *instance)
+int get_xml_int (xmlNodePtr node, void *x)
{
- instance_t *next;
-
- next = NULL;
- do
- {
- config_free_instance(instance);
- next = instance->next;
- free(instance);
-
- instance = next;
- } while (next != NULL);
+ char *tmp = (char *)xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
+ if (tmp == NULL)
+ return -1;
+ *(int*)x = strtol(tmp, NULL, 0);
+ xmlFree(tmp);
+ xmlMemoryDump();
+ return 0;
}
-void config_free_instance(instance_t *instance)
+
+int get_xml_string (xmlNodePtr node, void *x)
{
- if (instance->hostname) free(instance->hostname);
- if (instance->password) free(instance->password);
- if (instance->user) free(instance->user);
- if (instance->mount) free(instance->mount);
- if (instance->queue)
+ char *str = (char *)xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
+ char *p = *(char**)x;
+ if (str == NULL)
+ return -1;
+ if (p)
{
- thread_mutex_destroy(&instance->queue->lock);
- free(instance->queue);
+ dprintf ("freeing \"%s\" (%p) alloc \"%s\"\n", p, p, str);
+ xmlMemoryDump();
+ xmlFree (p);
}
+ *(char **)x = str;
+ xmlMemoryDump();
+ // xmlMemoryStrdup (str);
+ return 0;
}
-static void _set_instance_defaults(instance_t *instance)
+
+int parse_xml_tags (const char *id, xmlNodePtr node, const struct cfg_tag *args)
{
- instance->hostname = strdup(DEFAULT_HOSTNAME);
- instance->port = DEFAULT_PORT;
- instance->password = strdup(DEFAULT_PASSWORD);
- instance->user = DEFAULT_USERNAME;
- instance->mount = strdup(DEFAULT_MOUNT);
- instance->managed = DEFAULT_MANAGED;
- instance->min_br = DEFAULT_MIN_BITRATE;
- instance->nom_br = DEFAULT_NOM_BITRATE;
- instance->max_br = DEFAULT_MAX_BITRATE;
- instance->quality = DEFAULT_QUALITY;
- instance->encode = DEFAULT_REENCODE;
- instance->downmix = DEFAULT_DOWNMIX;
- instance->resampleinrate = DEFAULT_RESAMPLE;
- instance->resampleoutrate = DEFAULT_RESAMPLE;
- instance->reconnect_delay = DEFAULT_RECONN_DELAY;
- instance->reconnect_attempts = DEFAULT_RECONN_ATTEMPTS;
- instance->max_queue_length = DEFAULT_MAXQUEUELENGTH;
- instance->savefilename = DEFAULT_SAVEFILENAME;
+ int ret = 0;
- instance->queue = calloc(1, sizeof(buffer_queue));
- thread_mutex_create(&instance->queue->lock);
+ for (; node != NULL && ret == 0; node = node->next)
+ {
+ const struct cfg_tag *argp;
- instance->next = NULL;
+ if (xmlIsBlankNode (node) || strcmp ((char*)node->name, "comment") == 0 ||
+ strcmp ((char*)node->name, "text") == 0)
+ continue;
+ argp = args;
+ dprintf ("Checking \"%s\"\n", node->name);
+ while (argp->name)
+ {
+ dprintf (" against \"%s\"\n", argp->name);
+ if (strcmp ((const char*)node->name, argp->name) == 0)
+ {
+ ret = argp->retrieve (node, argp->storage);
+ break;
+ }
+ argp++;
+ }
+ if (argp->name == NULL)
+ fprintf (stderr, "%s: unknown element \"%s\" parsing \"%s\"\n", ices_config->cfgfile, node->name, id);
+ }
+ dprintf (" Ret is %d\n", ret);
+ return ret;
}
-static void _parse_resample(instance_t *instance,xmlDocPtr doc, xmlNodePtr node)
+
+
+
+static void _free_params(module_param_t *param)
{
- do {
- if (node == NULL) break;
- if (xmlIsBlankNode(node)) continue;
+ while (param != NULL)
+ {
+ module_param_t *next;
- if(strcmp(node->name, "in-rate") == 0)
- SET_INT(instance->resampleinrate);
- else if(strcmp(node->name, "out-rate") == 0)
- SET_INT(instance->resampleoutrate);
- } while((node = node->next));
+ if (param->name) xmlFree(param->name);
+ if (param->value) xmlFree(param->value);
+ next = param->next;
+ free(param);
+ param = next;
+ }
}
-static void _parse_encode(instance_t *instance,xmlDocPtr doc, xmlNodePtr node)
+
+static input_module_t *_free_input_module (input_module_t *mod)
{
- instance->encode = 1;
- do {
- if (node == NULL) break;
- if (xmlIsBlankNode(node)) continue;
+ input_module_t *next = mod->next;
- if (strcmp(node->name, "nominal-bitrate") == 0)
- SET_INT(instance->nom_br);
- else if (strcmp(node->name, "minimum-bitrate") == 0)
- SET_INT(instance->min_br);
- else if (strcmp(node->name, "maximum-bitrate") == 0)
- SET_INT(instance->max_br);
- else if (strcmp(node->name, "quality") == 0)
- SET_FLOAT(instance->quality);
- else if (strcmp(node->name, "samplerate") == 0)
- SET_INT(instance->samplerate);
- else if (strcmp(node->name, "channels") == 0)
- SET_INT(instance->channels);
- else if (strcmp(node->name, "managed") == 0)
- SET_INT(instance->managed);
- } while ((node = node->next));
+ if (mod->module != DEFAULT_PLAYLIST_MODULE)
+ xmlFree (mod->module);
+ if (mod->module_params)
+ _free_params(mod->module_params);
+
+ free (mod);
+ return next;
}
-static void _parse_metadata(instance_t *instance, config_t *config,
- xmlDocPtr doc, xmlNodePtr node)
+
+
+static int _parse_input_param (xmlNodePtr node, void *arg)
{
- do
- {
- if (node == NULL) break;
- if (xmlIsBlankNode(node)) continue;
+ module_param_t *param, *p;
+ input_module_t *inp = arg;
- if (strcmp(node->name, "name") == 0) {
- if(instance)
- SET_STRING(instance->stream_name);
- else
- SET_STRING(config->stream_name);
+ param = (module_param_t *)calloc(1, sizeof(module_param_t));
+ if (param)
+ {
+ if (get_xml_param_name (node, "name", ¶m->name) < 0)
+ {
+ free (param);
+ return -1;
}
- else if (strcmp(node->name, "genre") == 0) {
- if(instance)
- SET_STRING(instance->stream_genre);
- else
- SET_STRING(config->stream_genre);
+ if (get_xml_string (node, ¶m->value) < 0)
+ {
+ xmlFree (param->name);
+ free (param);
+ return -1;
}
- else if (strcmp(node->name, "description") == 0) {
- if(instance)
- SET_STRING(instance->stream_description);
- else
- SET_STRING(config->stream_description);
+ if (inp->module_params == NULL)
+ inp->module_params = param;
+ else
+ {
+ p = inp->module_params;
+ while (p->next != NULL) p = p->next;
+ p->next = param;
}
- else if (strcmp(node->name, "url") == 0) {
- if(instance)
- SET_STRING(instance->stream_url);
- else
- SET_STRING(config->stream_url);
- }
- } while ((node = node->next));
+ return 0;
+ }
+ return -1;
}
-static void _parse_instance(config_t *config, xmlDocPtr doc, xmlNodePtr node)
+
+static int _known_module (xmlNodePtr node, void *arg)
{
- instance_t *instance, *i;
+ int current_module = 0, ret = -1;
+ input_module_t *inp = arg;
- instance = (instance_t *)calloc(1, sizeof(instance_t));
- _set_instance_defaults(instance);
+ if (get_xml_string (node, &inp->module) < 0)
+ return -1;
- do
+ while (modules[current_module].name)
{
- if (node == NULL) break;
- if (xmlIsBlankNode(node)) continue;
-
- if (strcmp(node->name, "hostname") == 0)
- SET_STRING(instance->hostname);
- else if (strcmp(node->name, "port") == 0)
- SET_INT(instance->port);
- else if (strcmp(node->name, "password") == 0)
- SET_STRING(instance->password);
- else if (strcmp(node->name, "username") == 0)
- SET_STRING(instance->user);
- else if (strcmp(node->name, "yp") == 0)
- SET_INT(instance->public_stream);
- else if (strcmp(node->name, "savefile") == 0)
- SET_STRING(instance->savefilename);
- else if (strcmp(node->name, "mount") == 0)
- SET_STRING(instance->mount);
- else if(strcmp(node->name, "reconnectdelay") == 0)
- SET_INT(instance->reconnect_delay);
- else if(strcmp(node->name, "reconnectattempts") == 0)
- SET_INT(instance->reconnect_attempts);
- else if(strcmp(node->name, "maxqueuelength") == 0)
- SET_INT(instance->max_queue_length);
- else if(strcmp(node->name, "downmix") == 0)
- SET_INT(instance->downmix);
- else if(strcmp(node->name, "resample") == 0)
- _parse_resample(instance, doc, node->xmlChildrenNode);
- else if (strcmp(node->name, "encode") == 0)
- _parse_encode(instance, doc, node->xmlChildrenNode);
- else if (strcmp(node->name, "metadata") == 0)
- _parse_metadata(instance, config, doc, node->xmlChildrenNode);
- } while ((node = node->next));
-
- instance->next = NULL;
-
- if (_using_default_instance)
- {
- _using_default_instance = 0;
- _free_instances(config->instances);
- config->instances = NULL;
+ if(!strcmp(inp->module, modules[current_module].name))
+ {
+ inp->initialise_module = modules[current_module].initialise;
+ inp->open_module = modules[current_module].open;
+ inp->close_module = modules[current_module].close;
+ inp->shutdown_module = modules[current_module].shutdown;
+ ret = 0;
+ break;
+ }
+ current_module++;
}
-
- if (config->instances == NULL)
- {
- config->instances = instance;
- }
- else
- {
- i = config->instances;
- while (i->next != NULL) i = i->next;
- i->next = instance;
- }
+ return ret;
}
-static void _parse_input(config_t *config, xmlDocPtr doc, xmlNodePtr node)
+static int _parse_input(xmlNodePtr node, void *arg)
{
- module_param_t *param, *p;
+ config_t *config = arg;
+ int save = 1;
+ input_module_t *mod = calloc (1, sizeof (input_module_t));
+ static unsigned input_id = 0;
- do
+ while (mod)
{
- if (node == NULL) break;
- if (xmlIsBlankNode(node)) continue;
-
- if (strcmp(node->name, "module") == 0)
- SET_STRING(config->playlist_module);
- else if (strcmp(node->name, "param") == 0) {
- param = (module_param_t *)calloc(1, sizeof(module_param_t));
- SET_PARM_STRING("name", param->name);
- SET_STRING(param->value);
- param->next = NULL;
-
- if (config->module_params == NULL)
- {
- config->module_params = param;
- }
- else
- {
- p = config->module_params;
- while (p->next != NULL) p = p->next;
- p->next = param;
- }
+ struct cfg_tag input_tag[] =
+ {
+ { "module", _known_module, mod },
+ { "param", _parse_input_param, mod },
+ { "buffers", get_xml_int, &mod->buffer_count },
+ { "prealloc", get_xml_int, &mod->prealloc_count },
+ { "save", get_xml_int, &save },
+ { NULL, NULL, NULL }
+ };
+ if (parse_xml_tags ("input", node->xmlChildrenNode, input_tag))
+ break;
+ mod->id = ++input_id;
+ /* mod->save = save; */
+ if (config->inputs == NULL)
+ config->inputs = mod;
+ else
+ {
+ input_module_t *i = config->inputs;
+ while (i->next != NULL) i = i->next;
+ i->next = mod;
}
- } while ((node = node->next));
+ return 0;
+ }
+ if (mod) free (mod);
+ return -1;
}
-static void _parse_stream(config_t *config, xmlDocPtr doc, xmlNodePtr node)
+
+
+
+static int _parse_stream (xmlNodePtr node, void *arg)
{
- do
+ config_t *config = arg;
+ struct cfg_tag stream_tag[] =
{
- if (node == NULL) break;
- if (xmlIsBlankNode(node)) continue;
+ { "name", get_xml_string, &config->stream_name },
+ { "genre", get_xml_string, &config->stream_genre },
+ { "description", get_xml_string, &config->stream_description },
+ { "url", get_xml_string, &config->stream_url },
+ { "once", get_xml_bool, &config->input_once_thru },
+ { "input", _parse_input, config },
+ { "runner", parse_runner, config },
+ { NULL, NULL, NULL }
+ };
- if (strcmp(node->name, "metadata") == 0)
- _parse_metadata(NULL, config, doc, node->xmlChildrenNode);
- else if (strcmp(node->name, "input") == 0)
- _parse_input(config, doc, node->xmlChildrenNode);
- else if (strcmp(node->name, "instance") == 0)
- _parse_instance(config, doc, node->xmlChildrenNode);
- } while ((node = node->next));
+ return parse_xml_tags ("stream", node->xmlChildrenNode, stream_tag);
}
-static void _parse_root(config_t *config, xmlDocPtr doc, xmlNodePtr node)
+
+static int _parse_root (xmlNodePtr node, void *arg)
{
- do
+ config_t *config = arg;
+ char *user = NULL;
+
+ if (config && node && strcmp((char*)node->name, "ices") == 0)
{
- if (node == NULL) break;
- if (xmlIsBlankNode(node)) continue;
-
- if (strcmp(node->name, "background") == 0)
- SET_INT(config->background);
- else if (strcmp(node->name, "logpath") == 0)
- SET_STRING(config->logpath);
- else if (strcmp(node->name, "logfile") == 0)
- SET_STRING(config->logfile);
- else if (strcmp(node->name, "loglevel") == 0)
- SET_INT(config->loglevel);
- else if (strcmp(node->name, "logsize") == 0)
- SET_INT(config->logsize);
- else if (strcmp(node->name, "consolelog") == 0)
- SET_INT(config->log_stderr);
- else if (strcmp(node->name, "pidfile") == 0)
- SET_STRING(config->pidfile);
- else if (strcmp(node->name, "stream") == 0)
- _parse_stream(config, doc, node->xmlChildrenNode);
- } while ((node = node->next));
+ int realtime = 1;
+ struct cfg_tag ices_tag[] =
+ {
+ { "background", get_xml_bool, &config->background },
+ { "realtime", get_xml_bool, &realtime },
+ { "user", get_xml_string, &user },
+ { "logpath", get_xml_string, &config->logpath },
+ { "logfile", get_xml_string, &config->logfile },
+ { "loglevel", get_xml_int, &config->loglevel },
+ { "logsize", get_xml_int, &config->logsize },
+ { "consolelog", get_xml_bool, &config->log_stderr },
+ { "pidfile", get_xml_string, &config->pidfile },
+ { "stream", _parse_stream, config },
+ { NULL, NULL, NULL }
+ };
+ if (parse_xml_tags ("ices", node->xmlChildrenNode, ices_tag))
+ return -1;
+
+ config->realtime = realtime;
+ config->user = user;
+ return 0;
+ }
+ return -1;
}
-static void _set_defaults(config_t *c)
+static config_t *allocate_config(void)
{
- instance_t *instance;
+ config_t *c = (config_t *)calloc(1, sizeof(config_t));
- c->background = DEFAULT_BACKGROUND;
- c->logpath = strdup(DEFAULT_LOGPATH);
- c->logfile = strdup(DEFAULT_LOGFILE);
- c->logsize = DEFAULT_LOGSIZE;
- c->loglevel = DEFAULT_LOGLEVEL;
+ if (c == NULL)
+ return NULL;
+ c->background = DEFAULT_BACKGROUND;
+ c->logpath = xmlStrdup (DEFAULT_LOGPATH);
+ c->logfile = xmlStrdup (DEFAULT_LOGFILE);
+ c->logsize = DEFAULT_LOGSIZE;
+ c->loglevel = DEFAULT_LOGLEVEL;
c->log_stderr = DEFAULT_LOG_STDERR;
+ c->stream_name = xmlStrdup (DEFAULT_STREAM_NAME);
+ c->stream_genre = xmlStrdup (DEFAULT_STREAM_GENRE);
+ c->stream_description = xmlStrdup (DEFAULT_STREAM_DESCRIPTION);
+ dprintf ("name initially at %p \"%s\"\n", c->stream_name, c->stream_name);
+ dprintf ("desc initially at %p \"%s\"\n", c->stream_description, c->stream_description);
+ dprintf ("genre initially at %p \"%s\"\n", c->stream_genre, c->stream_genre);
- c->stream_name = strdup(DEFAULT_STREAM_NAME);
- c->stream_genre = strdup(DEFAULT_STREAM_GENRE);
- c->stream_description = strdup(DEFAULT_STREAM_DESCRIPTION);
- c->stream_url = NULL;
-
- c->playlist_module = strdup(DEFAULT_PLAYLIST_MODULE);
-
- c->module_params = NULL;
-
- instance = (instance_t *)malloc(sizeof(instance_t));
- _set_instance_defaults(instance);
- c->instances = instance;
+ return c;
}
-static void _free_params(module_param_t *param)
-{
- module_param_t *next;
- next = NULL;
- do
- {
- if (param->name) free(param->name);
- if (param->value) free(param->value);
- next = param->next;
- free(param);
-
- param = next;
- } while (next != NULL);
-}
-
void config_initialize(void)
{
- ices_config = (config_t *)calloc(1, sizeof(config_t));
- _set_defaults(ices_config);
- srand(time(NULL));
- xmlInitParser();
+ srand(time(NULL));
}
void config_shutdown(void)
{
- if (ices_config == NULL) return;
+ struct runner *run;
+ input_module_t *mod;
- if (ices_config->module_params != NULL)
- {
- _free_params(ices_config->module_params);
- ices_config->module_params = NULL;
- }
+ if (ices_config == NULL) return;
- if (ices_config->instances != NULL)
+ mod = ices_config->inputs;
+ while (mod)
{
- _free_instances(ices_config->instances);
- ices_config->instances = NULL;
+ mod->shutdown_module (mod);
+ mod = _free_input_module (mod);
}
- free(ices_config);
- ices_config = NULL;
- xmlCleanupParser();
-}
+ if (ices_config->logpath != DEFAULT_LOGPATH)
+ xmlFree (ices_config->logpath);
-int config_read(const char *fn)
-{
- xmlDocPtr doc;
- xmlNodePtr node;
+ if (ices_config->logfile != DEFAULT_LOGFILE)
+ xmlFree (ices_config->logfile);
- if (fn == NULL || strcmp(fn, "") == 0) return -1;
+ if (ices_config->stream_name != DEFAULT_STREAM_NAME)
+ xmlFree (ices_config->stream_name);
- doc = xmlParseFile(fn);
- if (doc == NULL) return -1;
+ if (ices_config->stream_genre != DEFAULT_STREAM_GENRE)
+ xmlFree (ices_config->stream_genre);
- node = xmlDocGetRootElement(doc);
- if (node == NULL || strcmp(node->name, "ices") != 0)
- {
- xmlFreeDoc(doc);
- return 0;
- }
+ if (ices_config->stream_description != DEFAULT_STREAM_DESCRIPTION)
+ xmlFree (ices_config->stream_description);
- _parse_root(ices_config, doc, node->xmlChildrenNode);
+ if (ices_config->user)
+ xmlFree (ices_config->user);
- xmlFreeDoc(doc);
-
- return 1;
+ run = ices_config->runners;
+ while (run)
+ run = config_free_runner (run);
+
+ free(ices_config);
+ ices_config = NULL;
}
-void config_dump(void)
+int config_read(const char *fn)
{
- config_t *c = ices_config;
- module_param_t *param;
- instance_t *i;
+ xmlDocPtr doc = NULL;
+ int ret = 0;
+ uid_t euid = geteuid();
- fprintf(stderr, "ices config dump:\n");
- fprintf(stderr, "background = %d\n", c->background);
- fprintf(stderr, "logpath = %s\n", c->logpath);
- fprintf(stderr, "logfile = %s\n", c->logfile);
- fprintf(stderr, "loglevel = %d\n", c->loglevel);
- fprintf(stderr, "\n");
- fprintf(stderr, "stream_name = %s\n", c->stream_name);
- fprintf(stderr, "stream_genre = %s\n", c->stream_genre);
- fprintf(stderr, "stream_description = %s\n", c->stream_description);
- fprintf(stderr, "stream_url = %s\n", c->stream_url ? c->stream_url : "");
- fprintf(stderr, "\n");
- fprintf(stderr, "playlist_module = %s\n", c->playlist_module);
- param = c->module_params;
- while(param)
+#ifdef _POSIX_SAVED_IDS
+ if (seteuid (getuid()) < 0)
+#else
+ uid_t ruid = getuid();
+ if (setreuid (euid, ruid) < 0)
+#endif
{
- fprintf(stderr, "module_param: %s = %s\n", param->name, param->value);
- param = param->next;
+ fprintf (stderr, "failed to drop priviledges for reading config file\n");
}
- fprintf(stderr, "\ninstances:\n\n");
+ xmlInitParser ();
+ doc = xmlParseFile(fn);
+ xmlCleanupParser ();
- i = c->instances;
- while (i)
- {
- fprintf(stderr, "hostname = %s\n", i->hostname);
- fprintf(stderr, "port = %d\n", i->port);
- fprintf(stderr, "password = %s\n", i->password);
- fprintf(stderr, "mount = %s\n", i->mount);
- fprintf(stderr, "minimum bitrate = %d\n", i->min_br);
- fprintf(stderr, "nominal bitrate = %d\n", i->nom_br);
- fprintf(stderr, "maximum bitrate = %d\n", i->max_br);
- fprintf(stderr, "quality = %f\n", i->quality);
- fprintf(stderr, "managed = %d\n", i->managed);
- fprintf(stderr, "reencode = %d\n", i->encode);
- fprintf(stderr, "reconnect: %d times at %d second intervals\n",
- i->reconnect_attempts, i->reconnect_delay);
- fprintf(stderr, "maxqueuelength = %d\n", i->max_queue_length);
- fprintf(stderr, "\n");
+ ices_config = allocate_config();
+ ices_config->cfgfile = fn;
+ if (_parse_root (xmlDocGetRootElement (doc), ices_config))
+ ret = -1;
- i = i->next;
- }
-
- fprintf(stderr, "\n");
+ xmlFreeDoc (doc);
+#ifdef _POSIX_SAVED_IDS
+ if (seteuid (euid) < 0)
+#else
+ if (setreuid (ruid, euid) < 0)
+#endif
+ fprintf (stderr, "failed to reset privilidges after reading config file\n");
+
+ return ret;
}
-
-
-
-
Modified: icecast/branches/kh/ices/src/cfgparse.h
===================================================================
--- icecast/trunk/ices/src/cfgparse.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/cfgparse.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,8 @@
-/* config.h
- * - configuration, and global structures built from config
+/* cfgparse.h
+ * - setup, and global structures built from setup
*
- * $Id: cfgparse.h,v 1.7 2004/03/11 17:16:08 karl Exp $
- *
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2002 Karl Heyes <karl at pts.tele2.co.uk>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -11,106 +10,134 @@
* with this source.
*/
-#ifndef __CONFIG_H__
-#define __CONFIG_H__
+#ifndef __CFGPARSE_H__
+#define __CFGPARSE_H__
-#include "stream.h"
+#define USE_PIPES
+
+typedef struct _config_tag config_t;
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include <shout/shout.h>
+#include "thread/thread.h"
+
+
#include "inputmodule.h"
-typedef struct _module_param_tag
+extern int realtime_enabled;
+
+struct cfg_tag
{
- char *name;
- char *value;
+ const char *name;
+ int (*retrieve) (xmlNodePtr node, void *x);
+ void *storage;
+};
- struct _module_param_tag *next;
-} module_param_t;
-/* FIXME: orward declaraction because my headers are a mess. */
-struct buffer_queue;
-typedef struct _instance_tag
+struct output_module
{
- char *hostname;
- int port;
- char *password;
- char *user;
- char *mount;
- int reconnect_delay;
- int reconnect_attempts;
- int encode;
- int downmix;
- int resampleinrate;
- int resampleoutrate;
- int max_queue_length;
- char *savefilename;
+ int (*output_send) (struct output_module *mod, ogg_packet *op, unsigned samples);
+ void (*output_clear) (struct output_module *mod);
+ int disabled;
+ struct output_state *parent;
+ struct output_module *next;
+ void *specific;
+ int need_headers;
+ ogg_stream_state os;
+ long serial;
+ int in_use;
+ long packetno;
+ ogg_int64_t granule, start_pos;
+ int initial_packets;
+ int reset;
+};
- /* local metadata */
- char *stream_name;
- char *stream_genre;
- char *stream_description;
- char *stream_url;
- /* Parameters for re-encoding */
- int managed;
- int min_br, nom_br, max_br;
- float quality;
- int samplerate;
- int channels;
-
- /* private */
- FILE *savefile;
- int buffer_failures;
- int died;
- int kill;
- int skip;
- int public_stream;
- int wait_for_critical;
- struct buffer_queue *queue;
+struct output_state
+{
+ struct output_module *head;
- struct _instance_tag *next;
-} instance_t;
+ long serial;
+ int in_use;
+ int headers;
+ ogg_packet packets[3];
+ int new_headers;
+ ogg_int64_t granules, start_pos;
+ unsigned granule_overlap;
-typedef struct _config_tag
+ char *name;
+ char *genre;
+ char *description;
+ char *url;
+
+ ogg_stream_state in_os;
+ vorbis_info vi;
+ vorbis_comment vc;
+ int info_in_use;
+};
+
+
+
+struct _config_tag
{
- int background;
+ int background;
+ int realtime;
+ char *pidfile;
char *logpath;
char *logfile;
+ int loglevel;
unsigned logsize;
- char *pidfile;
- int loglevel;
- int log_stderr;
+ int log_stderr;
+ char *user;
+ const char *cfgfile;
- /* <stream> */
-
/* <metadata> */
-
char *stream_name;
char *stream_genre;
char *stream_description;
char *stream_url;
-
- /* <playlist> */
-
- char *playlist_module;
- module_param_t *module_params;
- /* <instance> */
+ input_module_t *inputs;
- instance_t *instances;
+ unsigned runner_count;
+ struct runner *runners;
/* private */
int log_id;
int shutdown;
- char *metadata_filename;
- cond_t queue_cond;
- cond_t event_pending_cond;
- mutex_t refcount_lock;
- mutex_t flush_lock;
- input_module_t *inmod;
+ int next_track;
+ int has_encoding;
+ int input_once_thru;
+
struct _config_tag *next;
-} config_t;
+};
+
+
+extern int get_xml_string (xmlNodePtr node, void *x);
+extern int get_xml_int (xmlNodePtr node, void *x);
+extern int get_xml_float (xmlNodePtr node, void *x);
+extern int get_xml_bool (xmlNodePtr node, void *x);
+
+int parse_xml_tags (const char *id, xmlNodePtr node, const struct cfg_tag *args);
+
+static __inline__ int get_xml_param_name (xmlNodePtr node, char *p, char **where)
+{
+ char *tmp = (char *)xmlGetProp(node, (void*)p);
+ if (tmp == NULL)
+ return -1;
+ *where = tmp;
+ return 0;
+}
+
+
extern config_t *ices_config;
void config_initialize(void);
@@ -119,9 +146,8 @@
int config_read(const char *filename);
void config_dump(void);
-void config_free_instance(instance_t *instance);
-#endif /* __CONFIG_H__ */
+#endif /* __CFGPARSE_H__ */
Modified: icecast/branches/kh/ices/src/encode.c
===================================================================
--- icecast/trunk/ices/src/encode.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/encode.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,10 @@
/* encode.c
* - runtime encoding of PCM data.
*
- * $Id: encode.c,v 1.19 2003/12/22 14:01:09 karl Exp $
+ * $Id: encode.c,v 1.12 2002/08/17 05:17:57 karl Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2002-3 Karl Heyes <karl at xiph.org>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -11,9 +12,7 @@
* with this source.
*/
-#ifdef HAVE_CONFIG_H
- #include <config.h>
-#endif
+#include <config.h>
#include <stdio.h>
#include <stdlib.h>
@@ -29,59 +28,103 @@
#define MODULE "encode/"
#include "logging.h"
-static mutex_t _serial_lock;
-static long _get_serial()
+struct encoder
{
- static long prev_serial = 0;
- long serial;
+ long magic;
+ vorbis_info vi;
+ vorbis_comment vc;
+ vorbis_dsp_state vd;
+ vorbis_block vb;
+
+ unsigned samplerate;
+ int in_use;
+ int in_header;
+
+ int flushed;
+ ogg_packet headers[3];
+};
- thread_mutex_lock (&_serial_lock);
- serial = prev_serial;
- while (serial == prev_serial)
- serial = rand();
- prev_serial = serial;
- thread_mutex_unlock (&_serial_lock);
- return serial;
+
+void encode_free (struct encoder *s)
+{
+ if (s)
+ {
+ LOG_DEBUG0("Freeing encoder engine");
+
+ vorbis_block_clear (&s->vb);
+ vorbis_dsp_clear (&s->vd);
+ vorbis_comment_clear (&s->vc);
+ vorbis_info_clear (&s->vi);
+ free (s);
+ }
}
-void encode_clear(encoder_state *s)
+
+struct encoder *encode_create (void)
{
- if(s)
+ struct encoder *s = calloc(1, sizeof(struct encoder));
+ if (s == NULL)
+ return NULL;
+ s->magic=1;
+ vorbis_comment_init (&s->vc);
+ return s;
+}
+
+
+void encode_comment (struct encoder *s, char *str)
+{
+ if (s)
{
- LOG_DEBUG0("Clearing encoder engine");
- ogg_stream_clear(&s->os);
- vorbis_block_clear(&s->vb);
- vorbis_dsp_clear(&s->vd);
- vorbis_info_clear(&s->vi);
- free(s);
+ vorbis_comment_add (&s->vc, str);
}
}
-encoder_state *encode_initialise(int channels, int rate, int managed,
- int min_br, int nom_br, int max_br, float quality, vorbis_comment *vc)
+int encode_setup (struct encoder *s, struct encoder_settings *settings)
{
- encoder_state *s = calloc(1, sizeof(encoder_state));
- ogg_packet h1,h2,h3;
+ float quality;
+ long nom_br, max_br, min_br, rate, channels;
- /* Have vorbisenc choose a mode for us */
- vorbis_info_init(&s->vi);
+ /* do some sanity check */
+ if (settings->quality < -1)
+ {
+ LOG_WARN1 ("Quality setting of %f is too low, setting to -1.0", settings->quality);
+ settings->quality = -1.0;
+ }
+ if (settings->quality > 10.0)
+ {
+ LOG_WARN1 ("Quality setting of %f is too high, setting to 10.0", settings->quality);
+ settings->quality = 10.0;
+ }
- if (max_br < 0 && nom_br < 0 && min_br < 0)
- managed = 0;
- if (managed == 0 && nom_br >= 0)
+ nom_br = settings->nom_br;
+ min_br = settings->min_br;
+ max_br = settings->max_br;
+ rate = settings->encode_rate;
+ channels = settings->encode_channels;
+
+ /* If none of these are set, it's obviously not supposed to be managed */
+ if (settings->nom_br < 0 && min_br < 0 && max_br < 0)
+ settings->managed = 0;
+
+ if (settings->managed == 0 && nom_br >= 0)
if (min_br >= 0 || max_br >= 0)
- managed = 1;
+ settings->managed = 1;
+
+ quality = settings->quality;
+
+ /* Have vorbisenc choose a mode for us */
+ vorbis_info_init (&s->vi);
+
do
{
- if (managed)
+ if (settings->managed)
{
LOG_INFO5("Encoder initialising with bitrate management: %d "
"channels, %d Hz, minimum bitrate %d, nominal %d, "
"maximum %d", channels, rate, min_br, nom_br, max_br);
-
if (vorbis_encode_setup_managed (&s->vi, channels,
rate, max_br, nom_br, min_br))
break;
@@ -90,31 +133,17 @@
{
if (nom_br < 0)
{
- LOG_INFO3("Encoder initialising in VBR mode: %d channel(s), "
+ LOG_INFO3 ("Encoder initialising in VBR mode: %d channel(s), "
"%d Hz, quality %f", channels, rate, quality);
if (min_br > 0 || max_br > 0)
- LOG_INFO0("ignoring min/max bitrate, not supported in VBR "
+ LOG_WARN0 ("ignoring min/max bitrate, not supported in VBR "
"mode, use nominal-bitrate instead");
- if (vorbis_encode_setup_vbr(&s->vi, channels, rate, quality*0.1))
+ if (vorbis_encode_setup_vbr (&s->vi, channels, rate, quality*0.1))
break;
-
-#if 0
- if (max_br > 0 || min_br > 0)
- {
- struct ovectl_ratemanage_arg ai;
- if (vorbis_encode_ctl(&s->vi, OV_ECTL_RATEMANAGE_GET, &ai))
- break;
- ai.bitrate_hard_min = min_br;
- ai.bitrate_hard_max = max_br;
- ai.management_active = 1;
- if (vorbis_encode_ctl(&s->vi, OV_ECTL_RATEMANAGE_SET, &ai))
- break;
- }
-#endif
}
else
{
- LOG_INFO3("Encoder initialising in VBR mode: %d "
+ LOG_INFO3 ("Encoder initialising in VBR mode: %d "
"channels, %d Hz, nominal %d", channels, rate, nom_br);
if (vorbis_encode_setup_managed (&s->vi, channels,
rate, max_br, nom_br, max_br))
@@ -123,181 +152,111 @@
break;
}
}
- if (vorbis_encode_setup_init(&s->vi))
+
+ if (vorbis_encode_setup_init (&s->vi))
break;
- vorbis_analysis_init(&s->vd, &s->vi);
- vorbis_block_init(&s->vd, &s->vb);
+ vorbis_analysis_init (&s->vd, &s->vi);
+ vorbis_block_init (&s->vd, &s->vb);
- ogg_stream_init(&s->os, _get_serial());
+ vorbis_comment_add (&s->vc, "EncodedBy=" PACKAGE_STRING);
+ vorbis_analysis_headerout (&s->vd, &s->vc, &s->headers[0],&s->headers[1],&s->headers[2]);
- vorbis_analysis_headerout(&s->vd, vc, &h1,&h2,&h3);
- ogg_stream_packetin(&s->os, &h1);
- ogg_stream_packetin(&s->os, &h2);
- ogg_stream_packetin(&s->os, &h3);
+ s->in_header = 3;
+ s->samplerate = settings->samplerate;
+ s->in_use = 1;
- s->in_header = 1;
- s->samplerate = rate;
- s->samples_in_current_page = 0;
- s->prevgranulepos = 0;
-
- return s;
+ return 0;
} while (0);
LOG_INFO0("Failed to configure encoder, verify settings");
vorbis_info_clear(&s->vi);
- free (s);
- return NULL;
+
+ return -1;
}
-void encode_data_float(encoder_state *s, float **pcm, int samples)
+
+void encode_data_float(struct encoder *s, float **pcm, size_t samples)
{
- float **buf;
+ float **buf, **src, **dest;
int i;
+ unsigned size;
- buf = vorbis_analysis_buffer(&s->vd, samples);
-
- for(i=0; i < s->vi.channels; i++)
+ if (samples == 0)
{
- memcpy(buf[i], pcm[i], samples*sizeof(float));
+ LOG_DEBUG0 ("request for encoding 0 samples");
+ return;
}
+ if (s->magic != 1) printf ("structure has gone bad\n");
- vorbis_analysis_wrote(&s->vd, samples);
+ buf = vorbis_analysis_buffer(&s->vd, samples);
- s->samples_in_current_page += samples;
-}
-
-/* Requires little endian data (currently) */
-void encode_data(encoder_state *s, signed char *buf, int bytes, int bigendian)
-{
- float **buffer;
- int i,j;
- int channels = s->vi.channels;
- int samples = bytes/(2*channels);
-
- buffer = vorbis_analysis_buffer(&s->vd, samples);
-
- if(bigendian)
+ i=s->vi.channels;
+ src = pcm;
+ dest = buf;
+ size = samples*sizeof(float);
+ for(; i ; i--)
{
- for(i=0; i < samples; i++)
- {
- for(j=0; j < channels; j++)
- {
- buffer[j][i]=((buf[2*(i*channels + j)]<<8) |
- (0x00ff&(int)buf[2*(i*channels + j)+1]))/32768.f;
- }
- }
+ memcpy(*dest, *src, size);
+ dest++;
+ src++;
}
- else
- {
- for(i=0; i < samples; i++)
- {
- for(j=0; j < channels; j++)
- {
- buffer[j][i]=((buf[2*(i*channels + j) + 1]<<8) |
- (0x00ff&(int)buf[2*(i*channels + j)]))/32768.f;
- }
- }
- }
vorbis_analysis_wrote(&s->vd, samples);
-
- s->samples_in_current_page += samples;
}
-/* Returns:
- * 0 No output at this time
- * >0 Page produced
- *
- * Caller should loop over this to ensure that we don't end up with
- * excessive buffering in libvorbis.
- */
-int encode_dataout(encoder_state *s, ogg_page *og)
-{
- ogg_packet op;
- int result;
- if(s->in_header)
+int encode_packetout(struct encoder *s, ogg_packet *op)
+{
+ if (s->in_header)
{
- result = ogg_stream_flush(&s->os, og);
- if(result==0)
- {
- s->in_header = 0;
- return encode_dataout(s,og);
- }
- else
- return 1;
+ memcpy (op, &s->headers[3-s->in_header], sizeof (*op));
+#if 0
+ if ((op->packet = malloc (op->bytes)) == NULL)
+ return 0;
+ memcpy (op->packet, s->headers[3-s->in_header].packet, op->bytes);
+#endif
+ s->in_header--;
+ return 1;
}
- else
+ while (vorbis_bitrate_flushpacket (&s->vd, op) == 0)
{
- while(vorbis_analysis_blockout(&s->vd, &s->vb)==1)
- {
- vorbis_analysis(&s->vb, NULL);
- vorbis_bitrate_addblock(&s->vb);
-
- while(vorbis_bitrate_flushpacket(&s->vd, &op))
- ogg_stream_packetin(&s->os, &op);
- }
-
- /* FIXME: Make this threshold configurable.
- * We don't want to buffer too many samples in one page when doing
- * live encoding - that's fine for non-live encoding, but breaks
- * badly when doing things live.
- * So, we flush the stream if we have too many samples buffered
- */
- if(s->samples_in_current_page > s->samplerate * 2)
- {
- /*LOG_DEBUG1("Forcing flush: Too many samples in current page (%d)",
- s->samples_in_current_page); */
- result = ogg_stream_flush(&s->os, og);
- }
- else
- result = ogg_stream_pageout(&s->os, og);
-
- if(result==0)
+ if (vorbis_analysis_blockout (&s->vd, &s->vb) == 0)
return 0;
- else /* Page found! */
- {
- s->samples_in_current_page -= ogg_page_granulepos(og) -
- s->prevgranulepos;
- s->prevgranulepos = ogg_page_granulepos(og);
- return 1;
- }
+
+ vorbis_analysis (&s->vb, NULL);
+ vorbis_bitrate_addblock (&s->vb);
}
+ return 1;
}
-void encode_finish(encoder_state *s)
+
+int encode_endstream (struct encoder *s)
{
- ogg_packet op;
vorbis_analysis_wrote(&s->vd, 0);
-
- while(vorbis_analysis_blockout(&s->vd, &s->vb)==1)
- {
- vorbis_analysis(&s->vb, NULL);
- vorbis_bitrate_addblock(&s->vb);
- while(vorbis_bitrate_flushpacket(&s->vd, &op))
- ogg_stream_packetin(&s->os, &op);
- }
-
+ return 1;
}
-int encode_flush(encoder_state *s, ogg_page *og)
+
+int parse_encode (xmlNodePtr node, void *x)
{
- int result = ogg_stream_pageout(&s->os, og);
+ struct encoder_settings *enc = x;
+ struct cfg_tag encode_tags[] =
+ {
+ { "nominal-bitrate", get_xml_int, &enc->nom_br },
+ { "quality", get_xml_float, &enc->quality },
+ { "minimum-bitrate", get_xml_int, &enc->min_br },
+ { "maximum-bitrate", get_xml_int, &enc->max_br },
+ { "managed", get_xml_bool, &enc->managed },
+ { NULL , NULL, NULL }
+ };
- if(result<=0)
- return 0;
- else
- return 1;
-}
+ enc->min_br = -1;
+ enc->max_br = -1;
+ enc->nom_br = -1;
-void encode_init()
-{
- thread_mutex_create (&_serial_lock);
+ return parse_xml_tags ("encode", node->xmlChildrenNode, encode_tags);
}
-void encode_close()
-{
- thread_mutex_destroy (&_serial_lock);
-}
+
Modified: icecast/branches/kh/ices/src/encode.h
===================================================================
--- icecast/trunk/ices/src/encode.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/encode.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,10 @@
/* encode.h
* - encoding functions
*
- * $Id: encode.h,v 1.5 2003/12/22 14:01:09 karl Exp $
+ * $Id: encode.h,v 1.3 2002/01/28 00:19:15 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2002-3 Karl Heyes <karl at karl@pts.tele2.co.uk>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -17,29 +18,46 @@
#include <ogg/ogg.h>
#include <vorbis/codec.h>
-typedef struct {
- ogg_stream_state os;
- vorbis_block vb;
- vorbis_dsp_state vd;
- vorbis_info vi;
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
- int samples_in_current_page;
- int samplerate;
- ogg_int64_t prevgranulepos;
- int in_header;
-} encoder_state;
-encoder_state *encode_initialise(int channels, int rate, int managed,
- int min_br, int nom_br, int max_br, float quality, vorbis_comment *vc);
-void encode_clear(encoder_state *s);
-void encode_data_float(encoder_state *s, float **pcm, int samples);
-void encode_data(encoder_state *s, signed char *buf, int bytes, int bigendian);
-int encode_dataout(encoder_state *s, ogg_page *og);
-void encode_finish(encoder_state *s);
-int encode_flush(encoder_state *s, ogg_page *og);
-void encode_init();
-void encode_close();
+struct encoder_settings
+{
+ int managed;
+ int min_br;
+ int max_br;
+ int nom_br;
+ float quality;
+ unsigned samplerate;
+ unsigned channels;
+ unsigned encode_rate;
+ unsigned encode_channels;
+};
+int parse_encode (xmlNodePtr node, void *x);
+
+struct encoder *encode_initialise (int channels, int rate, int managed,
+ int min_br, int nom_br, int max_br, float quality,
+ int serial, vorbis_comment *vc);
+
+int encode_setup (struct encoder *, struct encoder_settings *);
+struct encoder *encode_create (void);
+
+void encode_free (struct encoder *s);
+void encode_clear (struct encoder *s);
+void encode_comment (struct encoder *s, char *);
+
+int encode_endstream (struct encoder *s);
+void encode_data_float (struct encoder *s, float **pcm, size_t samples);
+int encode_packetout(struct encoder *s, ogg_packet *op);
+
+int encode_dataout (struct encoder *s, ogg_page *og);
+int encode_pageout (struct encoder *s, ogg_page *og);
+void encode_finish (struct encoder *s);
+int encode_flush (struct encoder *s, ogg_page *og);
+
+
#endif
Modified: icecast/branches/kh/ices/src/ices.c
===================================================================
--- icecast/trunk/ices/src/ices.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/ices.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* ices.c
* - Main startup, thread launching, and cleanup code.
*
- * $Id: ices.c,v 1.19 2004/03/11 17:16:08 karl Exp $
+ * $Id: ices.c,v 1.4 2002/01/29 09:20:27 msmith Exp $
*
* Copyright (c) 2001-2002 Michael Smith <msmith at labyrinth.net.au>
*
@@ -12,47 +12,105 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
-#include <thread/thread.h>
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#include "net/resolver.h"
+#include "thread/thread.h"
+
#include "cfgparse.h"
#include "stream.h"
#include "signals.h"
-#include "input.h"
+#include "inputmodule.h"
#define MODULE "ices-core/"
#include "logging.h"
-int main(int argc, char **argv)
+void start_processing ()
{
- char logpath[FILENAME_MAX];
- int log;
+ thread_type *thread;
- if (argc != 2)
+ thread = thread_create("input", input_loop, NULL, 0);
+ if (thread == NULL)
+ return;
+ thread_join (thread);
+}
+
+int realtime_enabled = 0;
+
+
+static void drop_priviledges()
+{
+ struct passwd *pw = NULL;
+
+ if (ices_config->realtime)
{
- fprintf(stderr, PACKAGE_STRING "\n"
- " (c) Copyright 2001-2004 The IceS Development Team <team at icecast.org>\n"
- " Michael Smith <msmith at icecast.org>\n"
- " Karl Heyes <karl at xiph.org>\n"
- " and others\n"
- "\n"
- "Usage: \"ices config.xml\"\n");
- return 1;
+ struct sched_param param;
+ int policy;
+
+ pthread_getschedparam (pthread_self(), &policy, ¶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;
}
- config_initialize();
+ if (ices_config->user)
+ pw = getpwnam (ices_config->user);
- if (config_read(argv[1]) <= 0)
+ if (pw)
{
- fprintf(stderr, "Failed to read config file \"%s\"\n", argv[1]);
- goto fail;
+ setgid (pw->pw_gid);
+ setgroups (0, NULL);
+ setuid (pw->pw_uid);
}
+ else
+ {
+ setgid (getgid());
+ /* setgroups (0, NULL); */
+ setuid (getuid());
+ }
+}
+
+static char logpath[FILENAME_MAX];
+
+int main(int argc, char **argv)
+{
+ int log;
+
+ if (argc != 2)
+ {
+ fprintf(stderr, PACKAGE_STRING "\n"
+ " (c) Copyright 2002-2004 Karl Heyes <karl at xiph.org>\n\n"
+ "Usage: \"ices config.xml\"\n");
+ return 1;
+ }
+
+ config_initialize();
+
+ /* right now you must have a config file, but we should probably
+ ** make it so you can specify all parameters on the commandline
+ ** too.
+ */
+ if (config_read(argv[1]) < 0)
+ {
+ fprintf(stderr, "Failed to read config file \"%s\"\n", argv[1]);
+ goto fail;
+ }
if (ices_config->background)
{
int ret = 0;
@@ -61,11 +119,11 @@
{
case 0: break; /* child continues */
case -1: perror ("fork"); ret = -1;
- default:
+ default:
exit (ret);
}
- /* Disassociate process group and controlling terminal */
+ /* Disassociate process group and controlling terminal */
setsid();
/* Become a NON-session leader so that a */
@@ -74,35 +132,41 @@
{
case 0: break; /* child continues */
case -1: perror ("fork"); ret = -1;
- default:
+ default:
exit (ret);
}
}
-
- log_initialize();
+
+ log_initialize();
thread_initialize();
shout_init();
- encode_init();
- signals_setup();
+ signals_setup();
+ drop_priviledges();
- snprintf(logpath, FILENAME_MAX, "%s/%s", ices_config->logpath,
- ices_config->logfile);
- if(ices_config->log_stderr)
+ snprintf (logpath, FILENAME_MAX, "%s/%s", ices_config->logpath,
+ ices_config->logfile);
+ if (ices_config->log_stderr)
log = log_open_file(stderr);
else
{
- log = log_open(logpath);
+ log = log_open (logpath);
if (log < 0)
- fprintf (stderr, "unable to open log %s\n", logpath);
+ fprintf (stderr, "Failed to open log file %s\n", logpath);
log_set_trigger (log, ices_config->logsize);
}
- /* Set the log level, if requested - defaults to 2 (WARN) otherwise */
- if (ices_config->loglevel)
- log_set_level(log, ices_config->loglevel);
+ /* Set the log level, if requested - defaults to 2 (WARN) otherwise */
+ if (ices_config->loglevel)
+ log_set_level(log, ices_config->loglevel);
- ices_config->log_id = log;
+ ices_config->log_id = log;
- LOG_INFO0(PACKAGE_STRING " started...");
+ LOG_INFO0("Streamer version " PACKAGE_STRING);
+ LOG_INFO1("libshout version %s ", shout_version(NULL, NULL,NULL));
+ if (realtime_enabled)
+ LOG_INFO0("realtime scheduling has been enabled");
+ else
+ LOG_INFO0("Unable to set realtime scheduling");
+
if (ices_config->pidfile != NULL)
{
FILE *f = fopen (ices_config->pidfile, "w");
@@ -111,32 +175,24 @@
fprintf (f, "%i", getpid());
fclose (f);
}
- else
- {
- LOG_WARN1("pidfile \"%s\" cannot be written to", ices_config->pidfile);
- xmlFree (ices_config->pidfile);
- ices_config->pidfile = NULL;
- }
}
- /* Start the core streaming loop */
- input_loop();
+ start_processing ();
+ LOG_INFO0("Shutdown in progress");
+
if (ices_config->pidfile)
remove (ices_config->pidfile);
- LOG_INFO0("Shutdown complete");
+ log_close(log);
- log_close(log);
-
fail:
- encode_close();
shout_shutdown();
- config_shutdown();
- thread_shutdown();
- log_shutdown();
+ config_shutdown();
+ thread_shutdown();
+ log_shutdown();
- return 0;
+ return 0;
}
Modified: icecast/branches/kh/ices/src/im_alsa.c
===================================================================
--- icecast/trunk/ices/src/im_alsa.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_alsa.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_alsa.c
* - Raw PCM input from ALSA devices
*
- * $Id: im_alsa.c,v 1.8 2004/03/01 20:58:02 karl Exp $
+ * $Id: im_alsa.c,v 1.1 2002/12/29 10:28:30 msmith Exp $
*
* by Jason Chu <jchu at uvic.ca>, based
* on im_oss.c which is...
@@ -14,7 +14,7 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include <config.h>
#endif
#include <stdio.h>
@@ -27,13 +27,14 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
+#include <signals.h>
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
-#include <thread/thread.h>
#include "cfgparse.h"
-#include "stream.h"
#include "metadata.h"
-#include "inputmodule.h"
#define ALSA_PCM_NEW_HW_PARAMS_API
#include "im_alsa.h"
@@ -43,194 +44,234 @@
#define SAMPLES 8192
-static void close_module(input_module_t *mod)
+
+static int alsa_initialise_buffer (input_module_t *mod, input_buffer *ib)
{
- if(mod)
- {
- if(mod->internal)
- {
- im_alsa_state *s = mod->internal;
- if(s->fd != NULL)
- snd_pcm_close(s->fd);
- thread_mutex_destroy(&s->metadatalock);
- free(s);
- }
- free(mod);
- }
-}
-static int event_handler(input_module_t *mod, enum event_type ev, void *param)
-{
- im_alsa_state *s = mod->internal;
+ im_alsa_state *s = mod->internal;
+ int i;
+ float **ptr;
- switch(ev)
+ if ((ib->buf = calloc (s->channels, sizeof (float*))) == NULL)
+ return -1;
+ ptr = ib->buf;
+ for (i=s->channels ; i; i--)
{
- case EVENT_SHUTDOWN:
- close_module(mod);
- break;
- case EVENT_NEXTTRACK:
- s->newtrack = 1;
- break;
- case EVENT_METADATAUPDATE:
- thread_mutex_lock(&s->metadatalock);
- if(s->metadata)
- {
- char **md = s->metadata;
- while(*md)
- free(*md++);
- free(s->metadata);
- }
- s->metadata = (char **)param;
- s->newtrack = 1;
- thread_mutex_unlock(&s->metadatalock);
- break;
- default:
- LOG_WARN1("Unhandled event %d", ev);
+ if ((*ptr = calloc (sizeof(float), s->samples)) == NULL)
return -1;
+ ptr++;
}
-
+ ib->type = ICES_INPUT_PCM;
+ ib->subtype = INPUT_PCM_LE_16;
+ ib->critical = 0;
+ ib->channels = s->channels;
+ ib->samplerate = s->rate;
+ ib->mod = mod;
return 0;
}
-static void metadata_update(void *self, vorbis_comment *vc)
+
+void alsa_close_module(input_module_t *mod)
{
- im_alsa_state *s = self;
- char **md;
+ if (mod)
+ {
+ if (mod->internal)
+ {
+ im_alsa_state *s = mod->internal;
+ if (s->fd != NULL)
+ snd_pcm_close(s->fd);
+ free(s);
+ }
+ }
+}
- thread_mutex_lock(&s->metadatalock);
- md = s->metadata;
+void alsa_shutdown_module (input_module_t *mod)
+{
+ im_alsa_state *s = mod->internal;
- if(md)
- {
- while(*md)
- vorbis_comment_add(vc, *md++);
- }
-
- thread_mutex_unlock(&s->metadatalock);
+ LOG_INFO0 ("Shutdown ALSA module");
+ free (s);
+ mod->internal = NULL;
}
+
/* Core streaming function for this module
* This is what actually produces the data which gets streamed.
*
- * returns: >0 Number of bytes read
- * 0 Non-fatal error.
- * <0 Fatal error.
*/
-static int alsa_read(void *self, ref_buffer *rb)
+static int alsa_read(input_module_t *mod)
{
- int result;
- im_alsa_state *s = self;
+ int result;
+ im_alsa_state *s = mod->internal;
+ int dead_air;
+ input_buffer *ib;
- rb->buf = malloc(SAMPLES*2*s->channels);
- if(!rb->buf)
- return -1;
- result = snd_pcm_readi(s->fd, rb->buf, SAMPLES);
+ while (1)
+ {
+ if (s->user_terminated)
+ {
+ s->user_terminated = 0;
+ return 0;
+ }
- rb->len = result*4;
- rb->aux_data = s->rate*s->channels*2;
+ dead_air = 100;
+ while ((ib = input_alloc_buffer (mod)) == NULL)
+ {
+ if (dead_air == 100)
+ LOG_WARN0 ("will skip input for a short time");
+ result = snd_pcm_readi(s->fd, dead_audio, DEAD_AIR_BYTES/(2*s->channels));
+ if (--dead_air == 0)
+ {
+ mod->failures++;
+ return 0;
+ }
+ }
- if(s->newtrack)
- {
- rb->critical = 1;
- s->newtrack = 0;
- }
+ result = snd_pcm_readi (s->fd, s->read_buffer, s->samples);
- if (result == -EPIPE)
- {
- snd_pcm_prepare(s->fd);
- return 0;
+ /* check what this condition means */
+ if (result == -EPIPE)
+ {
+ snd_pcm_prepare(s->fd);
+ input_free_buffer (ib);
+ return 0;
+ }
+ if (result == -EBADFD)
+ {
+ LOG_ERROR0("Bad descriptor passed to snd_pcm_readi");
+ input_free_buffer (ib);
+ return 0;
+ }
+ if (s->newtrack)
+ {
+ LOG_DEBUG0("setting buffer critical");
+ metadata_thread_signal (mod, ib);
+ ib->critical = 1;
+ s->newtrack = 0;
+ }
+ if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+ {
+ LOG_DEBUG0("Sending EOS");
+ s->newtrack = 1;
+ ib->eos = 1;
+ if (move_to_next_input || ices_config->shutdown)
+ s->user_terminated = 1;
+ }
+
+ ib->samples = result;
+ uninterleave_pcm_le ((signed char*)s->read_buffer, ib->channels, ib->samples, ib->buf);
+
+ input_adv_sleep ((uint64_t)ib->samples * 1000000 / s->rate);
+ send_for_processing (mod, ib);
+ input_sleep ();
}
- else if (result == -EBADFD)
- {
- LOG_ERROR0("Bad descriptor passed to snd_pcm_readi");
- free(rb->buf);
- return -1;
- }
- return rb->len;
+ return 0;
}
-input_module_t *alsa_open_module(module_param_t *params)
+int alsa_init_module(input_module_t *mod)
{
- input_module_t *mod = calloc(1, sizeof(input_module_t));
- im_alsa_state *s;
- module_param_t *current;
- char *device = "plughw:0,0"; /* default device */
- int format = AFMT_S16_LE;
- int channels, rate;
- int use_metadata = 1; /* Default to on */
- unsigned int buffered_time, exact_rate;
- int dir;
+ im_alsa_state *s;
+ module_param_t *current;
+ char *device = "plughw:0,0"; /* default device */
+ int channels, rate;
+ unsigned int samples;
- snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
- snd_pcm_hw_params_t *hwparams;
+ mod->name = "ALSA";
+ mod->type = ICES_INPUT_PCM;
+ mod->subtype = INPUT_PCM_LE_16;
+ mod->getdata = alsa_read;
+ /* mod->release_input_buffer = alsa_return_buffer; */
+ mod->initialise_buffer = alsa_initialise_buffer;
+ mod->buffer_count = ices_config->runner_count*10 + 5;
+ mod->prealloc_count = ices_config->runner_count * 4;
- int err;
+ mod->internal = calloc(1, sizeof(im_alsa_state));
+ if (mod->internal == NULL)
+ return -1;
+ s = mod->internal;
- mod->type = ICES_INPUT_PCM;
- mod->subtype = INPUT_PCM_LE_16;
- mod->getdata = alsa_read;
- mod->handle_event = event_handler;
- mod->metadata_update = metadata_update;
+ s->fd = NULL; /* Set it to something invalid, for now */
+ rate = 44100; /* Defaults */
+ channels = 2;
+ samples = SAMPLES;
+ s->periods = 2;
+ s->buffer_time = 500000;
- mod->internal = calloc(1, sizeof(im_alsa_state));
- s = mod->internal;
+ current = mod->module_params;
- s->fd = NULL; /* Set it to something invalid, for now */
- s->rate = 44100; /* Defaults */
- s->channels = 2;
- s->buffer_time = 500000;
- s->periods = 2;
+ while(current)
+ {
+ if(!strcmp(current->name, "rate"))
+ rate = atoi(current->value);
+ else if(!strcmp(current->name, "channels"))
+ channels = atoi(current->value);
+ else if(!strcmp(current->name, "device"))
+ device = current->value;
+ else if(!strcmp(current->name, "samples"))
+ samples = atoi(current->value);
+ else if(!strcmp(current->name, "periods"))
+ s->periods = atoi(current->value);
+ else if(!strcmp(current->name, "buffer-time"))
+ s->buffer_time = atoi(current->value);
+ else if(!strcmp(current->name, "metadatafilename"))
+ mod->metadata_filename = current->value;
+ else
+ LOG_WARN1("Unknown parameter %s for alsa module", current->name);
- thread_mutex_create(&s->metadatalock);
+ current = current->next;
+ }
+ s->rate = rate;
+ s->channels = channels;
+ s->samples = samples;
+ s->device = device;
- current = params;
+ s->read_buffer_len = s->samples*2*s->channels;
+ s->read_buffer = malloc (s->read_buffer_len);
- while(current)
- {
- if(!strcmp(current->name, "rate"))
- s->rate = atoi(current->value);
- else if(!strcmp(current->name, "channels"))
- s->channels = atoi(current->value);
- else if(!strcmp(current->name, "device"))
- device = current->value;
- else if(!strcmp(current->name, "metadata"))
- use_metadata = atoi(current->value);
- else if(!strcmp(current->name, "metadatafilename"))
- ices_config->metadata_filename = current->value;
- else if(!strcmp(current->name, "buffer-time"))
- s->buffer_time = atoi (current->value) * 1000;
- else if(!strcmp(current->name, "periods"))
- s->periods = atoi (current->value);
- else
- LOG_WARN1("Unknown parameter %s for alsa module", current->name);
+ LOG_INFO0("ALSA driver initialised");
- current = current->next;
- }
+ return 0;
+}
- snd_pcm_hw_params_alloca(&hwparams);
+int alsa_open_module(input_module_t *mod)
+{
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
+ im_alsa_state *s = mod->internal;
+ int err;
+ unsigned int exact_rate;
+ int dir = 1;
- if ((err = snd_pcm_open(&s->fd, device, stream, 0)) < 0)
- {
- LOG_ERROR2("Failed to open audio device %s: %s", device, snd_strerror(err));
- goto fail;
- }
+ snd_pcm_hw_params_alloca(&hwparams);
- if ((err = snd_pcm_hw_params_any(s->fd, hwparams)) < 0)
- {
- LOG_ERROR1("Failed to initialize hwparams: %s", snd_strerror(err));
- goto fail;
- }
- if ((err = snd_pcm_hw_params_set_access(s->fd, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
- {
- LOG_ERROR1("Error setting access: %s", snd_strerror(err));
- goto fail;
- }
- if ((err = snd_pcm_hw_params_set_format(s->fd, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
- {
- LOG_ERROR1("Couldn't set sample format to SND_PCM_FORMAT_S16_LE: %s", snd_strerror(err));
- goto fail;
- }
+ if ((err = snd_pcm_open(&s->fd, s->device, stream, 0)) < 0)
+ {
+ LOG_ERROR2("Failed to open audio device %s: %s", s->device, snd_strerror(err));
+ goto fail;
+ }
+
+ if ((err = snd_pcm_hw_params_any(s->fd, hwparams)) < 0)
+ {
+ LOG_ERROR1("Failed to initialize hwparams: %s", snd_strerror(err));
+ goto fail;
+ }
+ if ((err = snd_pcm_hw_params_set_access(s->fd, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+ {
+ LOG_ERROR1("Error setting access: %s", snd_strerror(err));
+ goto fail;
+ }
+ if ((err = snd_pcm_hw_params_set_format(s->fd, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
+ {
+ LOG_ERROR1("Couldn't set sample format to SND_PCM_FORMAT_S16_LE: %s", snd_strerror(err));
+ goto fail;
+ }
+ if ((err = snd_pcm_hw_params_set_channels(s->fd, hwparams, s->channels)) < 0)
+ {
+ LOG_ERROR1("Error setting channels: %s", snd_strerror(err));
+ goto fail;
+ }
+
exact_rate = s->rate;
err = snd_pcm_hw_params_set_rate_near(s->fd, hwparams, &exact_rate, 0);
if (err < 0)
@@ -244,11 +285,7 @@
"%d instead", s->rate, exact_rate);
goto fail;
}
- if ((err = snd_pcm_hw_params_set_channels(s->fd, hwparams, s->channels)) < 0)
- {
- LOG_ERROR1("Error setting channels: %s", snd_strerror(err));
- goto fail;
- }
+
if ((err = snd_pcm_hw_params_set_buffer_time_near(s->fd, hwparams, &s->buffer_time, &dir)) < 0)
{
LOG_ERROR2("Error setting buffer time %u: %s", s->buffer_time, snd_strerror(err));
@@ -259,31 +296,24 @@
LOG_ERROR2("Error setting %u periods: %s", s->periods, snd_strerror(err));
goto fail;
}
- if ((err = snd_pcm_hw_params(s->fd, hwparams)) < 0)
- {
- LOG_ERROR1("Error setting HW params: %s", snd_strerror(err));
- goto fail;
- }
- /* We're done, and we didn't fail! */
- LOG_INFO1 ("Opened audio device %s", device);
- LOG_INFO4 ("using %d channel(s), %d Hz, buffer %u ms (%u periods)",
- s->channels, s->rate, s->buffer_time/1000, s->periods);
+ if ((err = snd_pcm_hw_params(s->fd, hwparams)) < 0)
+ {
+ LOG_ERROR1("Error setting HW params: %s", snd_strerror(err));
+ goto fail;
+ }
+ s->newtrack = 1;
- if(use_metadata)
- {
- LOG_INFO0("Starting metadata update thread");
- if(ices_config->metadata_filename)
- thread_create("im_alsa-metadata", metadata_thread_signal, mod, 1);
- else
- thread_create("im_alsa-metadata", metadata_thread_stdin, mod, 1);
- }
+ /* We're done, and we didn't fail! */
+ LOG_INFO1("Opened audio device %s", s->device);
+ LOG_INFO4("with %d channel(s), %d Hz, buffer %u ms (%u periods)",
+ s->channels, s->rate, s->buffer_time/1000, s->periods);
- return mod;
+ return 0;
fail:
- close_module(mod); /* safe, this checks for valid contents */
- return NULL;
+ alsa_shutdown_module(mod); /* safe, this checks for valid contents */
+ return -1;
}
Modified: icecast/branches/kh/ices/src/im_alsa.h
===================================================================
--- icecast/trunk/ices/src/im_alsa.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_alsa.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_alsa.h
* - read pcm data from oss devices
*
- * $Id: im_alsa.h,v 1.5 2004/03/01 20:58:02 karl Exp $
+ * $Id: im_alsa.h,v 1.1 2002/12/29 10:28:30 msmith Exp $
*
* by Jason Chu <jchu at uvic.ca>, based
* on im_oss.c which is...
@@ -16,24 +16,42 @@
#ifndef __IM_ALSA_H__
#define __IM_ALSA_H__
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <ogg/ogg.h>
#include <alsa/asoundlib.h>
-#include <thread/thread.h>
-#include <ogg/ogg.h>
#include "inputmodule.h"
typedef struct
{
- unsigned int rate;
- int channels;
+ int rate;
+ int channels;
unsigned buffer_time;
unsigned periods;
- snd_pcm_t *fd;
- char **metadata;
- int newtrack;
- mutex_t metadatalock;
+ snd_pcm_t *fd;
+ const char *device;
+ int newtrack;
+ int user_terminated;
+ int samples;
+ void *read_buffer;
+ unsigned read_buffer_len;
} im_alsa_state;
-input_module_t *alsa_open_module(module_param_t *params);
-#endif /* __IM_ALSA */
+int alsa_init_module(input_module_t *mod);
+int alsa_open_module (input_module_t *mod);
+void alsa_shutdown_module (input_module_t *mod);
+void alsa_close_module (input_module_t *mod);
+
+
+#endif /* __IM_ALSA_H__ */
Added: icecast/branches/kh/ices/src/im_jack.c
===================================================================
--- icecast/trunk/ices/src/im_jack.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_jack.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,362 @@
+/* im_jack.c
+ * - input from JACK applications
+ *
+ * $Id: im_jack.c,v 0.7.0 2004/03/06 18:30:30 j Exp $
+ *
+ *
+ * (c) 2004 jan gerber <j at thing.net>,
+ * based on im_alsa.c which is...
+ * by Jason Chu <jchu at uvic.ca>, based
+ * on im_oss.c which is...
+ * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ogg/ogg.h>
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signals.h>
+
+#include "cfgparse.h"
+#include "metadata.h"
+
+#include "im_jack.h"
+
+#define MODULE "input-jack/"
+#include "logging.h"
+
+#define DEFAULT_RB_SIZE 32*1024
+
+int jack_callback_process (jack_nframes_t nframes, void *arg)
+{
+ int chn;
+ input_module_t *mod=arg;
+ im_jack_state *s = mod->internal;
+ unsigned to_write = sizeof (jack_default_audio_sample_t) * nframes;
+ int problem = 0;
+
+ /* Do nothing until we're ready to begin. Stop doing on exit. */
+ if ((!s->can_process) || s->user_terminated)
+ return 0;
+
+ /* copy data to ringbuffer; one per channel */
+ for (chn = 0; chn < (s->channels); chn++)
+ {
+ int len;
+ len = jack_ringbuffer_write (s->rb[chn],
+ jack_port_get_buffer (s->jack_ports[chn], nframes), to_write);
+ if (len < to_write)
+ {
+ problem = 1;
+ break;
+ }
+ }
+ if (problem)
+ {
+ s->jack_shutdown = 1;
+ LOG_ERROR0 ("ringbuffer full");
+ }
+ /* input_adv_sleep ((uint64_t)nframes * 1000000 / s->rate); */
+ return 0;
+}
+
+static int jack_initialise_buffer (input_module_t *mod, input_buffer *ib)
+{
+ im_jack_state *s = mod->internal;
+ int i;
+ float **ptr;
+
+ if ((ib->buf = calloc (s->channels, sizeof (float*))) == NULL)
+ return -1;
+ ptr = ib->buf;
+ for (i=s->channels ; i; i--)
+ {
+ if ((*ptr = calloc (sizeof(float), s->samples)) == NULL)
+ return -1;
+ ptr++;
+ }
+ ib->type = ICES_INPUT_PCM;
+ ib->subtype = INPUT_PCM_LE_16;
+ ib->critical = 0;
+ ib->channels = s->channels;
+ ib->samplerate = s->rate;
+ ib->mod = mod;
+ return 0;
+}
+
+
+void jack_close_module(input_module_t *mod)
+{
+ int i;
+ if (mod)
+ {
+ LOG_DEBUG0 ("closing JACK module");
+ /* input_sleep (); */
+ if (mod->internal)
+ {
+ im_jack_state *s = mod->internal;
+ if (s->client != NULL)
+ jack_client_close(s->client);
+ if(s->jack_ports)
+ free(s->jack_ports);
+ if(s->rb){
+ for(i=0;i<s->channels;i++){
+ jack_ringbuffer_free(s->rb[i]);
+ }
+ free(s->rb);
+ s->rb = NULL;
+ }
+ }
+ }
+}
+
+
+void jack_shutdown_module (input_module_t *mod)
+{
+ im_jack_state *s = mod->internal;
+
+ LOG_INFO0 ("Shutdown JACK module");
+ free (s);
+ mod->internal = NULL;
+}
+
+void jack_shutdown (void *arg)
+{
+ LOG_ERROR0("killed by JACK server");
+ input_module_t *mod=arg;
+ im_jack_state *s = mod->internal;
+ s->jack_shutdown = 1;
+}
+
+
+/*
+ do the expensive stuff here so that
+ jack_callback_process is not stopped by jack
+ */
+static int jack_read(input_module_t *mod)
+{
+ im_jack_state *s = mod->internal;
+
+ size_t i;
+ int chn;
+ input_buffer *ib;
+ jack_default_audio_sample_t **pcms;
+ jack_nframes_t nframes=s->samples;
+ size_t framebuf_size = sizeof (jack_default_audio_sample_t) * nframes;
+
+ while (1)
+ {
+ /* read and process from ringbuffer has to go here. */
+ if (jack_ringbuffer_read_space (s->rb[0]) > framebuf_size)
+ {
+ if ((ib = input_alloc_buffer (mod)) == NULL)
+ {
+ LOG_ERROR0 ("Failed buffer allocation");
+ return 0;
+ }
+ pcms = ib->buf;
+
+ for (chn = 0; chn < (s->channels); chn++)
+ {
+ size_t len;
+ len = jack_ringbuffer_read (s->rb[chn], (char*)pcms[chn], framebuf_size);
+ if (len < framebuf_size)
+ framebuf_size = len;
+ }
+
+ ib->samples = framebuf_size/sizeof(jack_default_audio_sample_t);
+ ib->samplerate = s->rate;
+ ib->channels = s->channels;
+
+ if (s->newtrack)
+ {
+ LOG_DEBUG0 ("metadata updates flagged");
+ metadata_thread_signal (mod, ib);
+ ib->critical = 1;
+ s->newtrack = 0;
+ }
+
+ if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+ {
+ LOG_DEBUG0("marking buffer eos");
+ ib->eos = 1;
+ }
+ input_adv_sleep ((uint64_t)ib->samples * 1000000 / s->rate);
+ send_for_processing (mod, ib);
+ }
+ else
+ {
+ thread_sleep (s->sleep);
+ }
+ if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+ {
+ s->newtrack = 1;
+ if (move_to_next_input || ices_config->shutdown)
+ s->user_terminated = 1;
+ }
+
+ if (s->jack_shutdown)
+ {
+ s->jack_shutdown = 0;
+ break;
+ }
+ if (s->user_terminated)
+ {
+ s->user_terminated = 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+static void jack_return_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
+{
+ ib->critical = 0;
+ ib->eos = 0;
+ return;
+}
+
+int jack_init_module(input_module_t *mod)
+{
+ im_jack_state *s;
+ module_param_t *current;
+ char *clientname = "ices"; /* default clientname */
+ int channels, rate;
+ unsigned int samples;
+ unsigned sleep_time;
+
+ mod->name = "JACK";
+ mod->type = ICES_INPUT_PCM;
+ mod->subtype = INPUT_PCM_LE_16;
+ mod->getdata = jack_read;
+ mod->release_input_buffer = jack_return_buffer;
+ mod->initialise_buffer = jack_initialise_buffer;
+
+ mod->internal = calloc(1, sizeof(im_jack_state));
+ if (mod->internal == NULL)
+ return -1;
+ s = mod->internal;
+
+ s->client = NULL; /* Set it to something invalid, for now */
+
+ /* Defaults */
+ rate = 48000;
+ channels = 2;
+ samples = 4096;
+ sleep_time = 10000;
+
+ current = mod->module_params;
+
+ while(current)
+ {
+ if(!strcmp(current->name, "channels"))
+ channels = atoi(current->value);
+ else if(!strcmp(current->name, "clientname"))
+ clientname = current->value;
+ else if(!strcmp(current->name, "metadatafilename"))
+ mod->metadata_filename = current->value;
+ else if(!strcmp(current->name, "sleep"))
+ sleep_time = atoi (current->value);
+ else
+ LOG_WARN1("Unknown parameter %s for jack module", current->name);
+
+ current = current->next;
+ }
+ s->channels = channels;
+ s->clientname = clientname;
+
+ s->rate = rate;
+ s->samples = samples;
+ if (sleep_time > 0)
+ s->sleep = sleep_time;
+
+ /* allocate a few, so that mallocs don't kick in */
+ mod->prealloc_count = 20 + (ices_config->runner_count * 5);
+ mod->buffer_count = mod->prealloc_count;
+
+ s->can_process=0;
+
+ LOG_INFO0("JACK driver initialised");
+
+ return 0;
+
+}
+
+int jack_open_module(input_module_t *mod)
+{
+ im_jack_state *s = mod->internal;
+ int i,j;
+ char port_name[32];
+ size_t rb_size;
+
+ if ((s->client = jack_client_new(s->clientname)) == 0)
+ {
+ LOG_ERROR0("jack server not running");
+ goto fail;
+ }
+ LOG_INFO2("Registering as %s:in_1,%s:in_2\n", s->clientname,s->clientname);
+
+ /* do some memory management */
+ s->jack_ports =(jack_port_t **)malloc(sizeof (jack_port_t *) * (s->channels));
+
+ /* create the ringbuffers; one per channel, figures may need tweaking */
+ rb_size = (size_t)((s->sleep / 2000.0) *
+ jack_get_buffer_size(s->client) * sizeof(jack_default_audio_sample_t));
+ LOG_DEBUG2("creating %d ringbuffers, one per channel, of "
+ "%d bytes each", s->channels, rb_size);
+ s->rb =(jack_ringbuffer_t **)malloc(sizeof (jack_ringbuffer_t *) * (s->channels));
+ for(i=0;i<s->channels;i++){
+ s->rb[i]=jack_ringbuffer_create(DEFAULT_RB_SIZE);
+ }
+
+ /* start the callback process */
+ s->can_process=0; /* let the callback wait until all is running */
+ jack_set_process_callback(s->client, jack_callback_process, mod);
+
+ jack_on_shutdown (s->client, jack_shutdown, mod);
+
+ /* set samplerate and samples */
+ if(jack_get_sample_rate(s->client)>0)
+ {
+ s->rate = jack_get_sample_rate(s->client);
+ }
+ if (jack_activate(s->client))
+ {
+ LOG_ERROR0("cannot activate client");
+ goto fail;
+ }
+
+ /* Create and connect the jack ports */
+ for (i = 0; i < s->channels; i++)
+ {
+ sprintf(port_name, "in_%d", i+1);
+ s->jack_ports[i] = jack_port_register(s->client, port_name,
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput,0);
+ }
+
+ s->newtrack = 1;
+
+ LOG_INFO2("Channels %d / Samplerate %d", s->channels, s->rate);
+ s->can_process=1;
+ return 0;
+
+fail:
+ jack_shutdown_module(mod); /* safe, this checks for valid contents */
+ return -1;
+}
Added: icecast/branches/kh/ices/src/im_jack.h
===================================================================
--- icecast/trunk/ices/src/im_jack.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_jack.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,62 @@
+/* im_jack.h
+ * - input from JACK applications
+ *
+ * $Id: im_jack.h,v 0.5 2004/01/22 22:53:30 j Exp $
+ *
+ *
+ * (c) 2004 jan gerber <j at thing.net>,
+ * based on im_alsa.c which is...
+ * by Jason Chu <jchu at uvic.ca>, based
+ * on im_oss.c which is...
+ * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifndef __IM_JACK_H__
+#define __IM_JACK_H__
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <ogg/ogg.h>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include "inputmodule.h"
+
+typedef struct
+{
+ int rate;
+ int channels;
+ int samples;
+
+ int newtrack;
+ int user_terminated;
+ unsigned sleep;
+
+ int jack_shutdown;
+ const char *clientname;
+ jack_client_t *client;
+ jack_port_t **jack_ports;
+ jack_ringbuffer_t **rb;
+ volatile int can_process;
+
+} im_jack_state;
+
+int jack_init_module(input_module_t *mod);
+int jack_open_module (input_module_t *mod);
+void jack_shutdown_module (input_module_t *mod);
+void jack_close_module (input_module_t *mod);
+
+#endif /* __IM_JACK_H__ */
Modified: icecast/branches/kh/ices/src/im_oss.c
===================================================================
--- icecast/trunk/ices/src/im_oss.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_oss.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_oss.c
* - Raw PCM input from OSS devices
*
- * $Id: im_oss.c,v 1.14 2004/01/17 04:24:10 karl Exp $
+ * $Id: im_oss.c,v 1.7 2002/08/09 13:52:56 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -9,10 +9,14 @@
* Public License, version 2. You may use, modify, and redistribute
* it under the terms of this license. A copy should be included
* with this source.
+
+
+ * Modified to work with non-blocking ices.
+
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
@@ -26,248 +30,313 @@
#include <sys/types.h>
#include <fcntl.h>
-
-#include <thread/thread.h>
+#include "thread/thread.h"
#include "cfgparse.h"
#include "stream.h"
#include "metadata.h"
#include "inputmodule.h"
#include "im_oss.h"
+#include "signals.h"
#define MODULE "input-oss/"
#include "logging.h"
-#define BUFSIZE 8192
+#define SAMPLES 8192
-/* Some platforms (freebsd) don't define this, so just define it to something
- * that should be treated the same
- */
-#ifndef ERESTART
-#define ERESTART EINTR
-#endif
+static void oss_return_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
+{
+ ib->critical = 0;
+ ib->eos = 0;
+ return;
+}
-static void close_module(input_module_t *mod)
+
+static void oss_free_buffer (input_module_t *mod, input_buffer *ib)
{
- if(mod)
+ im_oss_state *s = mod->internal;
+ float **ptr;
+ int i;
+
+ ptr = ib->buf;
+ for (i=s->channels; i; i--)
{
- if(mod->internal)
+ if (ptr)
{
- im_oss_state *s = mod->internal;
- if(s->fd >= 0)
- close(s->fd);
- thread_mutex_destroy(&s->metadatalock);
- free(s);
+ free (*ptr);
+ *ptr = NULL;
}
- free(mod);
+ ptr++;
}
+ free (ib->buf);
+ ib->buf = NULL;
}
-static int event_handler(input_module_t *mod, enum event_type ev, void *param)
+
+
+static int oss_initialise_buffer (input_module_t *mod, input_buffer *ib)
{
im_oss_state *s = mod->internal;
+ float **ptr;
+ int i;
- switch(ev)
+ if ((ib->buf = calloc (1, s->default_len)) == NULL)
+ return -1;
+
+ if ((ib->buf = calloc (s->channels, sizeof (float*))) == NULL)
+ return -1;
+ ptr = ib->buf;
+ for (i=s->channels ; i; i--)
{
- case EVENT_SHUTDOWN:
- close_module(mod);
- break;
- case EVENT_NEXTTRACK:
- s->newtrack = 1;
- break;
- case EVENT_METADATAUPDATE:
- thread_mutex_lock(&s->metadatalock);
- if(s->metadata)
- {
- char **md = s->metadata;
- while(*md)
- free(*md++);
- free(s->metadata);
- }
- s->metadata = (char **)param;
- s->newtrack = 1;
- thread_mutex_unlock(&s->metadatalock);
- break;
- default:
- LOG_WARN1("Unhandled event %d", ev);
+ if ((*ptr = calloc (sizeof(float), s->samples)) == NULL)
return -1;
+ ptr++;
}
+ ib->type = ICES_INPUT_PCM;
+ ib->subtype = INPUT_PCM_LE_16;
+ ib->critical = 0;
+ ib->channels = s->channels;
+ ib->samplerate = s->samplerate;
+ ib->mod = mod;
+
return 0;
}
-static void metadata_update(void *self, vorbis_comment *vc)
-{
- im_oss_state *s = self;
- char **md;
- thread_mutex_lock(&s->metadatalock);
-
- md = s->metadata;
-
- if(md)
- {
- while(*md)
- vorbis_comment_add(vc, *md++);
- }
-
- thread_mutex_unlock(&s->metadatalock);
-}
-
/* Core streaming function for this module
* This is what actually produces the data which gets streamed.
*
- * returns: >0 Number of bytes read
- * 0 Non-fatal error.
- * <0 Fatal error.
*/
-static int oss_read(void *self, ref_buffer *rb)
+static int oss_read(input_module_t *mod)
{
- int result;
- im_oss_state *s = self;
+ im_oss_state *s = mod->internal;
+ input_buffer *ib;
+ int len;
+ int dead_air;
+
+ while (1)
+ {
+ if (s->user_terminated)
+ {
+ s->user_terminated = 0;
+ return 0;
+ }
- rb->buf = malloc(BUFSIZE*2*s->channels);
- if(!rb->buf)
- return -1;
- result = read(s->fd, rb->buf, BUFSIZE*2*s->channels);
+ dead_air = 100;
+ while ((ib = input_alloc_buffer (mod)) == NULL)
+ {
+ if (dead_air == 100)
+ LOG_WARN0 ("will skip input for a short time");
+ read (s->fd, dead_audio, DEAD_AIR_BYTES);
+ if (--dead_air == 0)
+ {
+ mod->failures++;
+ return 0;
+ }
+ }
- rb->len = result;
- rb->aux_data = s->rate*s->channels*2;
+ len = read (s->fd, s->read_buffer, s->read_buffer_len);
- if(s->newtrack)
- {
- rb->critical = 1;
- s->newtrack = 0;
+ if (len == -1)
+ {
+ LOG_ERROR1("Error reading from audio device: %s", strerror(errno));
+ input_free_buffer (ib);
+ return 0;
+ }
+ if ((unsigned)len < s->read_buffer_len)
+ LOG_DEBUG1 ("Read return short length %d", len);
+
+ ib->samples = len/(ib->channels*2);
+ uninterleave_pcm_le ((signed char*)s->read_buffer, ib->channels, ib->samples, ib->buf);
+
+ if (s->newtrack)
+ {
+ LOG_DEBUG0 ("metadata updates flagged");
+ metadata_thread_signal (mod, ib);
+ ib->critical = 1;
+ s->newtrack = 0;
+ }
+
+ if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+ {
+ LOG_DEBUG0("marking buffer eos");
+ s->newtrack = 1;
+ ib->eos = 1;
+ if (move_to_next_input || ices_config->shutdown)
+ s->user_terminated = 1;
+ }
+ input_adv_sleep ((uint64_t)ib->samples * 1000000 / s->samplerate);
+ send_for_processing (mod, ib);
}
+
+ return 0;
+}
- if(result == -1 && (errno == EINTR || errno == ERESTART))
+
+
+void oss_close_module (input_module_t *mod)
+{
+ im_oss_state *s = mod->internal;
+
+ LOG_INFO0("Closing OSS module");
+ if (s->fd > -1)
{
- return 0; /* Non-fatal error */
+ close (s->fd);
+ s->fd = -1;
}
- else if(result <= 0)
- {
- if(result == 0)
- LOG_INFO0("Reached EOF, no more data available");
- else
- LOG_ERROR1("Error reading from audio device: %s", strerror(errno));
- free(rb->buf);
- return -1;
- }
+}
- return rb->len;
+void oss_shutdown_module (input_module_t *mod)
+{
+ im_oss_state *s = mod->internal;
+
+ LOG_INFO0 ("Shutdown OSS module");
+ free (s);
+ mod->internal = NULL;
}
-input_module_t *oss_open_module(module_param_t *params)
+
+
+int oss_init_module(input_module_t *mod)
{
- input_module_t *mod = calloc(1, sizeof(input_module_t));
im_oss_state *s;
module_param_t *current;
- char *device = "/dev/dsp"; /* default device */
- int format = AFMT_S16_LE;
- int channels, rate;
- int use_metadata = 1; /* Default to on */
+ mod->name = "OSS";
mod->type = ICES_INPUT_PCM;
- mod->subtype = INPUT_PCM_LE_16;
mod->getdata = oss_read;
- mod->handle_event = event_handler;
- mod->metadata_update = metadata_update;
+ mod->release_input_buffer = oss_return_buffer;
+ mod->initialise_buffer = oss_initialise_buffer;
+ mod->free_input_buffer = oss_free_buffer;
+ mod->buffer_count = ices_config->runner_count*5 + 25;
+ mod->prealloc_count = 2 + ices_config->runner_count*2;
mod->internal = calloc(1, sizeof(im_oss_state));
- s = mod->internal;
+ if (mod->internal == NULL)
+ return -1;
- s->fd = -1; /* Set it to something invalid, for now */
- s->rate = 44100; /* Defaults */
- s->channels = 2;
+ s = mod->internal;
- thread_mutex_create(&s->metadatalock);
+ s->fd = -1; /* Set it to something invalid, for now */
+ s->samplerate = 44100; /* Defaults */
+ s->channels = 2;
- current = params;
+ current = mod->module_params;
- while(current)
- {
- if(!strcmp(current->name, "rate"))
- s->rate = atoi(current->value);
- else if(!strcmp(current->name, "channels"))
- s->channels = atoi(current->value);
- else if(!strcmp(current->name, "device"))
- device = current->value;
- else if(!strcmp(current->name, "metadata"))
- use_metadata = atoi(current->value);
- else if(!strcmp(current->name, "metadatafilename"))
- ices_config->metadata_filename = current->value;
+ while(current)
+ {
+ if(!strcmp(current->name, "rate"))
+ s->samplerate = atoi(current->value);
+ else if(!strcmp(current->name, "channels"))
+ s->channels = atoi(current->value);
+ else if(!strcmp(current->name, "device"))
+ s->devicename = current->value;
+ else if(!strcmp(current->name, "samples"))
+ s->samples = atoi(current->value);
+ else if(!strcmp(current->name, "metadatafilename"))
+ mod->metadata_filename = current->value;
+ else if (!strcmp(current->name, "comment"))
+ ;
else
- LOG_WARN1("Unknown parameter %s for oss module", current->name);
+ LOG_WARN1("Unknown parameter %s for oss module", current->name);
- current = current->next;
- }
+ current = current->next;
+ }
+ if (s->samples == 0)
+ s->samples = s->samplerate/5;
+
+ s->aux_data = s->samplerate * s->channels * 2;
+ s->default_len = s->samples * s->channels * 2;
+ s->read_buffer_len = s->samples*2*s->channels;
+ s->read_buffer = malloc (s->read_buffer_len);
- /* First up, lets open the audio device */
- if((s->fd = open(device, O_RDONLY, 0)) == -1)
- {
- /* Open failed */
- LOG_ERROR2("Failed to open audio device %s: %s",
- device, strerror(errno));
- goto fail;
- }
+ LOG_INFO0 ("Module OSS initialised");
+ return 0;
+}
- /* Now, set the required parameters on that device */
- if(ioctl(s->fd, SNDCTL_DSP_SETFMT, &format) == -1)
- {
- LOG_ERROR2("Failed to set sample format on audio device %s: %s",
- device, strerror(errno));
- goto fail;
- }
- if(format != AFMT_S16_LE)
- {
- LOG_ERROR0("Couldn't set sample format to AFMT_S16_LE");
- goto fail;
- }
+int oss_open_module(input_module_t *mod)
+{
+ im_oss_state *s = mod->internal;
+ int format = AFMT_S16_LE;
+ int channels, rate;
+ int flags;
+ /* First up, lets open the audio device */
channels = s->channels;
- if(ioctl(s->fd, SNDCTL_DSP_CHANNELS, &channels) == -1)
- {
- LOG_ERROR2("Failed to set number of channels on audio device %s: %s",
- device, strerror(errno));
- goto fail;
- }
- if(channels != s->channels)
- {
- LOG_ERROR0("Couldn't set number of channels");
- goto fail;
- }
+ rate = s->samplerate;
- rate = s->rate;
- if(ioctl(s->fd, SNDCTL_DSP_SPEED, &rate) == -1)
+ if (ices_config->shutdown)
+ return -1;
+
+ if((s->fd = open(s->devicename, O_RDONLY, 0)) == -1)
+ {
+ LOG_ERROR2("Failed to open audio device %s: %s", s->devicename, strerror(errno));
+ goto fail;
+ }
+ flags = fcntl (s->fd, F_GETFL);
+ if (flags == -1)
{
- LOG_ERROR2("Failed to set sampling rate on audio device %s: %s",
- device, strerror(errno));
+ LOG_ERROR0 ("Cannot get file state");
goto fail;
}
- if(rate != s->rate)
+ flags &= ~O_NONBLOCK;
+ if (fcntl(s->fd, F_SETFL, flags) == -1)
{
- LOG_ERROR0("Couldn't set sampling rate");
+ LOG_ERROR0 ("Cannot set dsp file state");
goto fail;
}
+ /* Now, set the required parameters on that device */
- /* We're done, and we didn't fail! */
- LOG_INFO3("Opened audio device %s at %d channel(s), %d Hz",
- device, channels, rate);
+ if(ioctl(s->fd, SNDCTL_DSP_SETFMT, &format) == -1)
+ {
+ LOG_ERROR2("Failed to set sample format on audio device %s: %s",
+ s->devicename, strerror(errno));
+ goto fail;
+ }
+ if(format != AFMT_S16_LE)
+ {
+ LOG_ERROR0("Couldn't set sample format to AFMT_S16_LE");
+ goto fail;
+ }
- if(use_metadata)
- {
- LOG_INFO0("Starting metadata update thread");
- if(ices_config->metadata_filename)
- thread_create("im_oss-metadata", metadata_thread_signal, mod, 1);
- else
- thread_create("im_oss-metadata", metadata_thread_stdin, mod, 1);
- }
+ channels = s->channels;
+ if(ioctl(s->fd, SNDCTL_DSP_CHANNELS, &channels) == -1)
+ {
+ LOG_ERROR2("Failed to set number of channels on audio device %s: %s",
+ s->devicename, strerror(errno));
+ goto fail;
+ }
+ if(channels != s->channels)
+ {
+ LOG_ERROR0("Couldn't set number of channels");
+ goto fail;
+ }
- return mod;
+ rate = s->samplerate;
+ if(ioctl(s->fd, SNDCTL_DSP_SPEED, &rate) == -1)
+ {
+ LOG_ERROR2("Failed to set sampling rate on audio device %s: %s",
+ s->devicename, strerror(errno));
+ goto fail;
+ }
+ if(rate != s->samplerate)
+ {
+ LOG_ERROR0("Couldn't set sampling rate");
+ goto fail;
+ }
+ /* We're done, and we didn't fail! */
+ LOG_INFO3("Opened audio device %s at %d channel(s), %d Hz",
+ s->devicename, channels, rate);
+
+ s->newtrack = 1;
+ return 0;
fail:
- close_module(mod); /* safe, this checks for valid contents */
- return NULL;
+ oss_close_module(mod); /* safe, this checks for valid contents */
+ return -1;
}
+
+
Modified: icecast/branches/kh/ices/src/im_oss.h
===================================================================
--- icecast/trunk/ices/src/im_oss.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_oss.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_oss.h
* - read pcm data from oss devices
*
- * $Id: im_oss.h,v 1.4 2003/03/28 01:07:37 karl Exp $
+ * $Id: im_oss.h,v 1.2 2001/09/25 12:04:21 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -14,21 +14,30 @@
#ifndef __IM_OSS_H__
#define __IM_OSS_H__
-#include <thread/thread.h>
+#include "inputmodule.h"
#include <ogg/ogg.h>
-#include "inputmodule.h"
typedef struct
{
- int rate;
- int channels;
+ int channels;
+ int samplerate;
+ int aux_data;
+ int samples;
+ int default_len;
+ int user_terminated;
- int fd;
- char **metadata;
- int newtrack;
- mutex_t metadatalock;
+ int fd;
+ char *devicename;
+ int newtrack;
+ void *read_buffer;
+ unsigned read_buffer_len;
} im_oss_state;
-input_module_t *oss_open_module(module_param_t *params);
+int oss_open_module(input_module_t *);
+void oss_close_module (input_module_t *);
+int oss_init_module(input_module_t *mod);
+void oss_shutdown_module(input_module_t *mod);
+
+
#endif /* __IM_OSS_H__ */
Added: icecast/branches/kh/ices/src/im_pcm.c
===================================================================
--- icecast/trunk/ices/src/im_pcm.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_pcm.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,416 @@
+/* im_pcm.c
+ * - Raw PCM input from a pipe, based on the stdinpcm module.
+ *
+ * Copyright (c) 2002 Karl Heyes <karl at xiph.org>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ogg/ogg.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include "thread/thread.h"
+
+#include "cfgparse.h"
+#include "stream.h"
+#include "signals.h"
+
+#include "inputmodule.h"
+
+
+#include "metadata.h"
+#include "im_pcm.h"
+#include "timing/timing.h"
+
+#define MODULE "input-pcm/"
+#include "logging.h"
+
+#define SAMPLES 4096
+
+/* #define MAX_DEAD_AUDIO_BYTES 48000*2*2
+static char dead_audio[MAX_DEAD_AUDIO_BYTES]; */
+static uint64_t skip_bytes;
+
+static int run_script (char *script, im_pcm_state *s)
+{
+ int fd [2];
+
+ if (pipe (fd) < 0)
+ {
+ LOG_ERROR0 ("Pipe creation error");
+ return -1;
+ }
+ switch (s->pid = fork ())
+ {
+ case -1:
+ LOG_ERROR2 ("Unable to fork %s (%s)", script, strerror (errno));
+ return -1;
+ case 0: /* child */
+ LOG_DEBUG1 ("Starting command %s", script);
+ close (1);
+ dup2 (fd[1], 1);
+ close (fd[0]);
+ close (fd[1]);
+ close (0); /* We want to detach the keyboard from the script */
+ /* execl (script, script, NULL); */
+ execve (s->args[0], s->args, NULL);
+ LOG_ERROR2 ("Unable to run command %s (%s)", script, strerror (errno));
+ close (1);
+ _exit (-1);
+ default: /* parent */
+ s->fd = fd[0];
+ close (fd[1]);
+ break;
+ }
+ return 0;
+}
+
+static void kill_script (im_pcm_state *s)
+{
+ LOG_DEBUG1 ("Sending SIGTERM to pid %d", s->pid);
+ kill (s->pid, SIGTERM);
+ close (s->fd);
+ waitpid (s->pid, NULL, 0);
+ s->fd = -1;
+}
+
+
+static int wait_for_pcm (im_pcm_state *s)
+{
+ fd_set fds;
+ struct timeval tv;
+ int ret;
+
+ FD_ZERO (&fds);
+ FD_SET (s->fd, &fds);
+ tv.tv_sec = s->timeout;
+ tv.tv_usec = 0;
+ ret = select (s->fd+1, &fds, NULL, NULL, &tv);
+ if (ret == 0)
+ {
+ LOG_WARN0 ("Timeout reading from input");
+ s->no_stream = 1;
+ return -1;
+ }
+ if (ret == -1)
+ {
+ LOG_ERROR1("select failed error %s", strerror (errno));
+ s->no_stream = 1;
+ return -1;
+ }
+ /* LOG_DEBUG1 ("data availble %d", sleep_to); */
+ return 0;
+}
+
+
+static int pcm_read(input_module_t *mod)
+{
+ im_pcm_state *s = mod->internal;
+ input_buffer *ib = NULL;
+ unsigned char *buf;
+ unsigned remaining, len;
+ int dead_air = 100;
+
+ while (1)
+ {
+ if (s->terminate)
+ {
+ s->terminate = 0;
+ return 0;
+ }
+ if (s->error)
+ {
+ LOG_INFO0("Error reading from stream, switching to next input");
+ mod->failures++;
+ return 0;
+ }
+ if (s->no_stream)
+ {
+ LOG_INFO0("End of input has been reached, switching to next input");
+ return 0;
+ }
+ input_sleep();
+
+ while ((ib = input_alloc_buffer (mod)) == NULL)
+ {
+ if (dead_air == 100)
+ LOG_WARN0 ("will skip input for a short time");
+ read (s->fd, dead_audio, DEAD_AIR_BYTES);
+ if (--dead_air == 0)
+ return 0;
+ }
+
+ buf = s->read_buffer;
+ remaining = s->read_buffer_len;
+ len = 0;
+ if(s->newtrack)
+ {
+ LOG_DEBUG0 ("metadata updates flagged");
+ metadata_thread_signal (mod, ib);
+ ib->critical = 1;
+ s->newtrack = 0;
+ }
+ if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+ {
+ LOG_DEBUG0("signal or shutdown received");
+ s->newtrack = 1;
+ ib->eos = 1;
+ if (ices_config->shutdown || move_to_next_input)
+ s->terminate = 1;
+ }
+
+ while (remaining)
+ {
+ int sent;
+
+ sent = read (s->fd, buf, remaining); /* non blocking */
+ if (sent < 0)
+ {
+ if (errno == EAGAIN && wait_for_pcm (s) == 0)
+ continue;
+ s->error = 1;
+ break;
+ }
+ if (sent == 0)
+ {
+ s->no_stream = 1;
+ break;
+ }
+ remaining -= sent;
+ buf += sent;
+ }
+ len = s->read_buffer_len - remaining;
+ if (remaining)
+ {
+ LOG_INFO0("No more data available");
+ input_free_buffer (ib);
+ break;
+ }
+
+ ib->samples = len/(ib->channels*2);
+
+ uninterleave_pcm_le ((signed char*)s->read_buffer, ib->channels, ib->samples, ib->buf);
+
+ input_adv_sleep ((uint64_t)ib->samples * 1000000 / ib->samplerate);
+ send_for_processing (mod, ib);
+ }
+
+ return 0;
+}
+
+
+void pcm_close_module(input_module_t *mod)
+{
+ LOG_INFO0 ("Closing PCM input module");
+ if(mod && mod->internal)
+ {
+ im_pcm_state *s = mod->internal;
+ if (s->command && s->fd != -1)
+ {
+ kill_script (s);
+ }
+ }
+}
+
+
+void pcm_shutdown_module(input_module_t *mod)
+{
+ LOG_INFO0 ("shutdown PCM input module");
+ if (mod && mod->internal)
+ {
+ input_buffer *ib;
+
+ while (1)
+ {
+ ib = mod->free_list;
+ if (ib == NULL)
+ break;
+ mod->free_list = ib->next;
+ free (ib->buf);
+ free (ib);
+ }
+ free (mod->internal);
+ mod->internal = NULL;
+ }
+}
+
+
+static int pcm_initialise_buffer (input_module_t *mod, input_buffer *ib)
+{
+ im_pcm_state *s = mod->internal;
+ float **ptr;
+ int i;
+
+ /* allocate memory for vorbis data */
+ if ((ib->buf = calloc (s->channels, sizeof (float*))) == NULL)
+ return -1;
+ ptr = ib->buf;
+ for (i=s->channels ; i; i--)
+ {
+ if ((*ptr = calloc (sizeof(float), s->samples)) == NULL)
+ return -1;
+ ptr++;
+ }
+
+ ib->type = ICES_INPUT_PCM;
+ ib->subtype = INPUT_PCM_LE_16;
+ ib->critical = 0;
+ ib->channels = s->channels;
+ ib->samplerate = s->samplerate;
+ ib->mod = mod;
+
+ return 0;
+}
+
+
+int pcm_initialise_module(input_module_t *mod)
+{
+ im_pcm_state *s;
+ module_param_t *current;
+
+ LOG_INFO0 ("Initialising PCM input module");
+
+ mod->name = "pcm";
+ mod->type = ICES_INPUT_PCM;
+ mod->getdata = pcm_read;
+ mod->initialise_buffer = pcm_initialise_buffer;
+ mod->buffer_count = ices_config->runner_count*15 + 5;
+ mod->prealloc_count = ices_config->runner_count * 4;
+
+ mod->internal = calloc(1, sizeof(im_pcm_state));
+ if (mod->internal == NULL)
+ return -1;
+
+ s = mod->internal;
+
+ /* Defaults */
+ s->channels = 2;
+ s->samplerate = 44100;
+ s->samples = SAMPLES;
+ s->timeout = 5;
+ s->arg_count = 1;
+
+ current = mod->module_params;
+
+ while(current)
+ {
+ if(!strcmp(current->name, "rate"))
+ s->samplerate = atoi(current->value);
+ else if(!strcmp(current->name, "channels"))
+ s->channels = atoi(current->value);
+ else if(!strcmp(current->name, "metadatafilename"))
+ mod->metadata_filename = current->value;
+ else if (!strcmp(current->name, "source") || !strcmp(current->name, "command"))
+ {
+ s->command = current->value;
+ s->args[0] = s->command;
+ }
+ else if (!strcmp(current->name, "arg"))
+ if (s->arg_count < MAX_ARGS)
+ s->args[s->arg_count++] = current->value;
+ else
+ {
+ LOG_WARN0("too many args, disabling module");
+ return -1;
+ }
+ else if (!strcmp(current->name, "comment"))
+ s->command = current->value;
+ else if (!strcmp(current->name, "timeout"))
+ s->timeout = atoi (current->value);
+ else if (!strcmp(current->name, "samples"))
+ s->samples = atoi (current->value);
+ /* else if (!strcmp(current->name, "lossy"))
+ s->sleep = _sleep_add_silence; */
+ else if (!strcmp(current->name, "skip"))
+ s->skip = atoi (current->value);
+ else
+ LOG_WARN1("Unknown parameter %s for pcm module", current->name);
+
+ current = current->next;
+ }
+ s->args[s->arg_count] = NULL;
+ s->aux_data = s->samplerate * s->channels * 2;
+ s->default_duration = (s->default_len*1000)/s->aux_data;
+ s->read_buffer_len = s->samples*2*s->channels;
+ s->read_buffer = malloc (s->read_buffer_len);
+ LOG_DEBUG1 ("Module PCM using buffers of %d samples", s->samples);
+
+ return 0;
+}
+
+
+
+int pcm_open_module(input_module_t *mod)
+{
+ im_pcm_state *s = mod->internal;
+ const char *label = "standard input";
+
+ s->fd = 0;
+ if (s->command)
+ {
+ if (run_script (s->command, s) < 0)
+ {
+ s->fd = -1;
+ LOG_ERROR1 ("Failed to start script %s", s->command);
+ return -1;
+ }
+ label = s->command;
+ }
+ s->newtrack = 1;
+ s->silence_start = 0;
+ s->error = 0;
+ s->no_stream = 0;
+ fcntl (s->fd, F_SETFL, O_NONBLOCK);
+ skip_bytes = s->skip;
+ LOG_INFO0("Retrieving PCM to skip");
+ while (skip_bytes)
+ {
+ unsigned len;
+ int ret;
+
+ if (wait_for_pcm (s) < 0)
+ {
+ s->terminate = 1;
+ break;
+ }
+ if (skip_bytes > DEAD_AIR_BYTES)
+ len = DEAD_AIR_BYTES;
+ else
+ len = skip_bytes;
+ ret = read (s->fd, dead_audio, len);
+ if (ret ==0)
+ break;
+ if (ret > 0)
+ skip_bytes -= ret;
+ else
+ {
+ s->terminate = 1;
+ break;
+ }
+ }
+ LOG_INFO1("Retrieving PCM from %s", label);
+
+ return 0;
+}
+
+
Added: icecast/branches/kh/ices/src/im_pcm.h
===================================================================
--- icecast/trunk/ices/src/im_pcm.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_pcm.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,58 @@
+/* im_stdinpcm.h
+ * - stdin reading
+ *
+ * $Id: im_stdinpcm.h,v 1.2 2001/09/25 12:04:21 msmith Exp $
+ *
+ * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifndef __IM_PCM_H__
+#define __IM_PCM_H__
+
+#include "inputmodule.h"
+#include <ogg/ogg.h>
+
+
+#define MAX_ARGS 12
+
+typedef struct _im_pcm_state im_pcm_state;
+
+struct _im_pcm_state
+{
+ int (*sleep) (im_pcm_state *s, void *buf, unsigned remaining, timing_control *control);
+ int samplerate;
+ int channels;
+ int newtrack;
+ int timeout;
+ char *command;
+ FILE *file;
+ pid_t pid;
+ int fd;
+ int max_buffers;
+ unsigned samples;
+ unsigned default_len;
+ float default_duration;
+ size_t aux_data;
+ time_t silence_start;
+ int error;
+ int terminate;
+ int no_stream;
+ uint64_t skip;
+ int arg_count;
+ char *args[MAX_ARGS+1]; /* allow for NULL */
+ void *read_buffer;
+ unsigned read_buffer_len;
+};
+
+int pcm_initialise_module(input_module_t *);
+int pcm_open_module(input_module_t *);
+void pcm_close_module(input_module_t *mod);
+void pcm_shutdown_module(input_module_t *mod);
+
+
+#endif /* __IM_PCM_H__ */
Modified: icecast/branches/kh/ices/src/im_playlist.c
===================================================================
--- icecast/trunk/ices/src/im_playlist.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_playlist.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,10 @@
/* playlist.c
* - Basic playlist functionality
*
- * $Id: im_playlist.c,v 1.15 2004/01/12 23:39:39 karl Exp $
+ * $Id: im_playlist.c,v 1.6 2002/08/03 15:05:38 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2003 Karl Heyes <karl at xiph.org>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -12,7 +13,7 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
@@ -25,266 +26,441 @@
#include "cfgparse.h"
#include "stream.h"
-#include "event.h"
#include "inputmodule.h"
-#include "input.h"
#include "im_playlist.h"
+#include "timing/timing.h"
#include "playlist_basic.h"
+#include "metadata.h"
+#include "signals.h"
#define MODULE "playlist-builtin/"
#include "logging.h"
-#define BUFSIZE 4096
+#define OGG_BUFSIZE 4096
+#define ALLOCATION_DELAY 500*1000 /* uS */
-typedef struct _module
+typedef struct _playlist_module
{
char *name;
- int (*init)(module_param_t *, playlist_state_t *);
-} module;
+ int (*init)(module_param_t *, struct playlist_state *);
+} playlist_module_t;
-static module modules[] = {
+static playlist_module_t pmodules[] = {
{ "basic", playlist_basic_initialise},
{ "script", playlist_script_initialise},
{NULL,NULL}
};
-static void close_module(input_module_t *mod)
+static void playlist_clear_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
{
- if (mod == NULL) return;
-
- if (mod->internal)
+ if (ib)
{
- playlist_state_t *pl = (playlist_state_t *)mod->internal;
- pl->clear(pl->data);
- ogg_sync_clear(&pl->oy);
- free(pl);
+ ib->critical = 0;
+ if (ib->buf)
+ {
+ ogg_packet *op = ib->buf;
+ free (op->packet);
+ free (op);
+ ib->buf = NULL;
+ }
}
- free(mod);
}
-static int event_handler(input_module_t *mod, enum event_type ev, void *param)
+int playlist_open_module(input_module_t *mod)
{
- switch(ev)
+ module_param_t *current;
+
+ LOG_INFO0("open playlist module");
+
+ current = mod->module_params;
+ while (current)
{
- case EVENT_SHUTDOWN:
- close_module(mod);
+ if (!strcmp(current->name, "type"))
+ {
+ int current_module = 0;
+
+ while(pmodules[current_module].init)
+ {
+ if(!strcmp(current->value, pmodules[current_module].name))
+ {
+ struct playlist_state *pl = (struct playlist_state *)mod->internal;
+
+ if (pmodules[current_module].init (mod->module_params, pl))
+ {
+ LOG_ERROR0("Playlist initialisation failed");
+ return -1;
+ }
+ pl->filename = NULL;
+ pl->next_track = 1;
+ pl->terminate = 0;
+ pl->prev_op = NULL;
+ LOG_DEBUG0 ("initialised module");
+ return 0;
+ }
+ current_module++;
+ }
break;
- case EVENT_NEXTTRACK:
- LOG_INFO0("Moving to next file in playlist.");
- ((playlist_state_t *)mod->internal)->nexttrack = 1;
- break;
- default:
- LOG_WARN1("Unhandled event %d", ev);
- return -1;
+ }
+ current = current->next;
}
+ return -1;
+}
- return 0;
+
+static void close_file (struct playlist_state *pl)
+{
+ if (pl->current_file)
+ {
+ fclose (pl->current_file);
+ pl->current_file = NULL;
+ /* Reinit sync, so that dead data from previous file is discarded */
+ ogg_stream_clear(&pl->os);
+ ogg_sync_clear(&pl->oy);
+ }
}
-/* Core streaming function for this module
- * This is what actually produces the data which gets streamed.
- *
- * returns: >0 Number of bytes read
- * 0 Non-fatal error.
- * <0 Fatal error.
- */
-static int playlist_read(void *self, ref_buffer *rb)
+
+void playlist_close_module(input_module_t *mod)
{
- playlist_state_t *pl = (playlist_state_t *)self;
- int bytes;
- unsigned char *buf;
- char *newfn;
- int result;
- ogg_page og;
+ LOG_INFO0 ("Close playlist module");
+ if (mod->internal)
+ {
+ struct playlist_state *pl = (struct playlist_state *)mod->internal;
+ pl->free_filename (pl->data, pl->filename);
+ pl->clear (pl->data);
+ pl->data = NULL;
+ close_file (pl);
+ }
+}
- if (pl->errors > 5)
+void playlist_shutdown_module(input_module_t *mod)
+{
+ input_buffer *ib;
+
+ LOG_INFO0 ("Shutdown playlist module");
+
+ if (mod->internal)
{
- LOG_WARN0("Too many consecutive errors - exiting");
- return -1;
+ struct playlist_state *pl = (struct playlist_state *)mod->internal;
+ if (pl->data) pl->clear(pl->data);
+ ogg_sync_clear(&pl->oy);
+ free(pl);
}
+ while (1)
+ {
+ ib = mod->free_list;
+ if (ib == NULL)
+ break;
+ mod->free_list = ib->next;
+ free (ib);
+ }
+}
- if (!pl->current_file || pl->nexttrack)
+
+static int find_next_entry (struct playlist_state *pl)
+{
+ char *newfn;
+
+ pl->next_track = 0;
+
+ if (ices_config->shutdown || pl->terminate)
+ return 0;
+
+ while (1)
{
- pl->nexttrack = 0;
-
- if (pl->current_file && strcmp (pl->filename, "-"))
+ if (pl->errors > 5)
{
- fclose(pl->current_file);
- pl->current_file = NULL;
+ LOG_WARN0("Too many consecutive errors - exiting");
+ return 0;
}
newfn = pl->get_filename(pl->data);
- if (!newfn)
+ if (newfn == NULL)
{
- LOG_INFO0("No more filenames available, end of playlist");
- return -1; /* No more files available */
+ LOG_DEBUG0 ("no filename returned");
+ return 0; /* No more files available */
}
if (strcmp (newfn, "-"))
{
- if (!pl->allow_repeat && pl->filename && !strcmp(pl->filename, newfn))
+ pl->current_file = fopen(newfn, "rb");
+ if (pl->current_file == NULL)
{
- LOG_ERROR0("Cannot play same file twice in a row, skipping");
- pl->errors++;
+ LOG_WARN2("Error opening file \"%s\": %s", newfn, strerror(errno));
pl->free_filename (pl->data, newfn);
- return 0;
- }
- pl->free_filename(pl->data, pl->filename);
- pl->filename = newfn;
-
- pl->current_file = fopen(pl->filename, "rb");
- if (!pl->current_file)
- {
- LOG_WARN2("Error opening file \"%s\": %s",pl->filename, strerror(errno));
pl->errors++;
- return 0;
+ continue;
}
- LOG_INFO1("Currently playing \"%s\"", pl->filename);
}
else
{
- LOG_INFO0("Currently playing from stdin");
pl->current_file = stdin;
- pl->free_filename(pl->data, pl->filename);
- pl->filename = newfn;
}
+ pl->errors = 0;
- /* Reinit sync, so that dead data from previous file is discarded */
- ogg_sync_clear(&pl->oy);
- ogg_sync_init(&pl->oy);
+ break;
}
- input_sleep ();
+ pl->free_filename(pl->data, pl->filename);
+ pl->filename = newfn;
- while(1)
+ LOG_INFO1("Currently playing \"%s\"", newfn);
+
+ ogg_sync_init(&pl->oy);
+ return 1;
+}
+
+
+static ogg_packet *copy_ogg_packet (ogg_packet *packet)
+{
+ ogg_packet *next;
+ do
{
- result = ogg_sync_pageout(&pl->oy, &og);
- if(result < 0)
- LOG_WARN1("Corrupt or missing data in file (%s)", pl->filename);
- else if(result > 0)
- {
- if (ogg_page_bos (&og))
- {
- if (ogg_page_serialno (&og) == pl->current_serial)
- {
- LOG_WARN1 ("Skipping \"%s\" as the serial number is the same as previous", pl->filename);
- pl->nexttrack = 1;
- pl->errors++;
- return 0;
- }
- pl->current_serial = ogg_page_serialno (&og);
- }
- if (input_calculate_ogg_sleep (&og) < 0)
- {
- pl->nexttrack = 1;
- return 0;
- }
- rb->len = og.header_len + og.body_len;
- rb->buf = malloc(rb->len);
- rb->aux_data = og.header_len;
-
- memcpy(rb->buf, og.header, og.header_len);
- memcpy(rb->buf+og.header_len, og.body, og.body_len);
- if(ogg_page_granulepos(&og)==0)
- rb->critical = 1;
+ next = malloc (sizeof (ogg_packet));
+ if (next == NULL)
break;
- }
+ memcpy (next, packet, sizeof (ogg_packet));
+ next->packet = malloc (next->bytes);
+ if (next->packet == NULL)
+ break;
+ memcpy (next->packet, packet->packet, next->bytes);
+ return next;
+ } while (0);
- /* If we got to here, we didn't have enough data. */
- buf = ogg_sync_buffer(&pl->oy, BUFSIZE);
- bytes = fread(buf,1, BUFSIZE, pl->current_file);
- if (bytes <= 0)
+ if (next)
+ free (next);
+ return NULL;
+}
+
+
+static void *alloc_ogg_buffer (input_module_t *mod, unsigned len)
+{
+ struct playlist_state *pl = (struct playlist_state *)mod->internal;
+ return ogg_sync_buffer (&pl->oy, len);
+}
+
+
+static void prepare_buffer (input_buffer *ib, struct playlist_state *pl, int force_eos)
+{
+ ogg_packet *op = pl->prev_packet;
+
+ ib->buf = op;
+ if (op->b_o_s)
+ {
+ ib->critical = 1;
+ vorbis_info_init (&pl->vi);
+ vorbis_comment_init (&pl->vc);
+ pl->more_headers = 3;
+ }
+ if (pl->more_headers)
+ {
+ if (vorbis_synthesis_headerin (&pl->vi, &pl->vc, op) < 0)
{
- if (feof(pl->current_file))
- {
- pl->nexttrack = 1;
- return playlist_read(pl,rb);
- }
- else
- {
- LOG_ERROR2("Read error from \"%s\": %s", pl->filename, strerror(errno));
- fclose(pl->current_file);
- pl->current_file=NULL;
- pl->errors++;
- return 0;
- }
+ LOG_ERROR1("Problem with vorbis header %d", 4-pl->more_headers);
+ metadata_update_signalled = 1;
+ pl->prev_packet = NULL;
}
+ pl->more_headers--;
+ pl->prev_window = 0;
+ ib->samples = 0;
+ }
+ else
+ {
+ int window = vorbis_packet_blocksize (&pl->vi, op) / 4;
+ if (pl->prev_window)
+ {
+ pl->granulepos += pl->prev_window + window;
+ ib->samples = pl->prev_window + window;
+ }
else
- ogg_sync_wrote(&pl->oy, bytes);
+ {
+ pl->granulepos = 0;
+ ib->samples = 0;
+ }
+ pl->prev_window = window;
+ op->granulepos = pl->granulepos;
+ ib->samplerate = pl->vi.rate;
+ ib->channels = pl->vi.channels;
}
+ ib->type = ICES_INPUT_VORBIS_PACKET;
+ if (force_eos || op->e_o_s)
+ {
+ ib->eos = 1;
+ op->e_o_s = 1;
+ vorbis_comment_clear (&pl->vc);
+ vorbis_info_clear (&pl->vi);
+ }
+ /* printf ("packet has %ld granulepos\n", op->granulepos); */
+ input_adv_sleep ((unsigned long)(ib->samples * 1000000.0 / ib->samplerate));
+}
- pl->errors=0;
- return rb->len;
-}
-
-input_module_t *playlist_open_module(module_param_t *params)
+static void flush_file (input_module_t *mod)
{
- input_module_t *mod = calloc(1, sizeof(input_module_t));
- playlist_state_t *pl;
- module_param_t *current;
- int (*init)(module_param_t *, playlist_state_t *)=NULL;
+ struct playlist_state *pl = (struct playlist_state *)mod->internal;
- mod->type = ICES_INPUT_VORBIS;
- mod->getdata = playlist_read;
- mod->handle_event = event_handler;
- mod->metadata_update = NULL; /* Not used for playlists */
+ /* flush final EOS packet */
+ if (pl->prev_packet)
+ {
+ input_buffer *ib = input_alloc_buffer (mod);
+ if (ib)
+ {
+ LOG_DEBUG0("Flushing EOS packet");
+ prepare_buffer (ib, pl, 1);
+ send_for_processing (mod, ib);
+ pl->prev_packet = NULL;
+ }
+ }
+ close_file(pl);
+}
- mod->internal = calloc(1, sizeof(playlist_state_t));
- pl = (playlist_state_t *)mod->internal;
- current = params;
- while(current)
+static int write_ogg_data (input_module_t *mod, void *buffer, unsigned len)
+{
+ struct playlist_state *pl = (struct playlist_state *)mod->internal;
+ ogg_page page;
+
+ ogg_sync_wrote (&pl->oy, len);
+ /* data now in the stream, lets see about getting packets */
+ while (1)
{
- if (!strcmp(current->name, "type"))
+ ogg_packet packet;
+ int result;
+
+ while (ogg_stream_packetpeek (&pl->os, &packet) > 0)
{
- int current_module = 0;
+ /* we cache one packet so that EOS can be marked even if it's missing */
+ if (pl->prev_packet)
+ {
+ input_buffer *ib = NULL;
+ int buffers_ok = 1;
- while(modules[current_module].init)
- {
- if(!strcmp(current->value, modules[current_module].name))
+ while (1)
{
- init = modules[current_module].init;
- break;
+ if (metadata_update_signalled)
+ {
+ LOG_INFO0("switching to next entry in playlist");
+ metadata_update_signalled = 0;
+ return 0;
+ }
+ if (ices_config->shutdown || move_to_next_input)
+ {
+ LOG_INFO0("module shutdown requested");
+ pl->terminate = 1;
+ return 0;
+ }
+ ib = input_alloc_buffer (mod);
+ if (ib)
+ break;
+
+ if (pl->sleep && buffers_ok)
+ {
+ LOG_ERROR0 ("failed buffer allocation");
+ buffers_ok = 0;
+ }
+
+ input_adv_sleep (ALLOCATION_DELAY);
+ input_sleep ();
}
- current_module++;
+ /* pass BOS just in case EOS is missing in the input stream */
+ prepare_buffer (ib, pl, packet.b_o_s);
+ send_for_processing (mod, ib);
+ if (metadata_update_signalled)
+ return 0;
}
-
- if(!init)
- {
- LOG_ERROR1("Unknown playlist type \"%s\"", current->value);
- goto fail;
- }
+ /* copy the packet */
+ pl->prev_packet = copy_ogg_packet (&packet);
+ ogg_stream_packetout (&pl->os, NULL);
+ if (pl->sleep)
+ input_sleep();
}
- current = current->next;
- }
-
- if(init)
- {
- if(init(params, pl))
+ result = ogg_sync_pageout (&pl->oy, &page);
+ if (result < 0)
{
- LOG_ERROR0("Playlist initialisation failed");
- goto fail;
+ LOG_WARN1("Corrupt or missing data in stream from %s", pl->filename);
+ break;
}
- else
+ if (result == 0)
+ break;
+ /* ok, we have a page, now do we initialise the stream */
+ if (ogg_page_bos (&page))
{
- ogg_sync_init(&pl->oy);
- return mod; /* Success. Finished initialising */
+ if (pl->os_init)
+ ogg_stream_clear (&pl->os);
+ ogg_stream_init (&pl->os, ogg_page_serialno (&page));
+ pl->os_init = 1;
}
+ ogg_stream_pagein (&pl->os, &page);
}
- else
- LOG_ERROR0("No playlist type given, cannot initialise playlist module");
+ return len;
+}
-fail:
- if (mod)
+
+/* Core streaming function for this module
+ * This is what actually produces the data which gets streamed.
+ *
+ */
+
+static int playlist_read(input_module_t *mod)
+{
+ struct playlist_state *pl = (struct playlist_state *)mod->internal;
+
+ /* setup callbacks */
+ pl->write_func = write_ogg_data;
+ pl->alloc_buffer = alloc_ogg_buffer;
+ pl->flush_func = flush_file;
+ mod->release_input_buffer = playlist_clear_buffer;
+
+ /* start processing */
+ while (find_next_entry(pl))
{
- if (mod->internal)
- free(mod->internal);
- free(mod);
+ while (1)
+ {
+ void *buffer;
+ int len;
+
+ buffer = pl->alloc_buffer (mod, OGG_BUFSIZE);
+ len = fread (buffer, 1, OGG_BUFSIZE, pl->current_file);
+ if (len == 0)
+ break;
+
+ if (pl->write_func (mod , buffer, len) < len)
+ break;
+ }
+ LOG_DEBUG0 ("End of file");
+ /* callback for flush */
+ pl->flush_func (mod);
}
+ return 0;
+}
- return NULL;
+
+static int playlist_init_buffer (input_module_t *mod, input_buffer *ib)
+{
+ ib->type = ICES_INPUT_VORBIS;
+ ib->subtype = INPUT_SUBTYPE_INVALID;
+ ib->critical = 0;
+ ib->mod = mod;
+ return 0;
}
+int playlist_init_module (input_module_t *mod)
+{
+ LOG_INFO0 ("Initialise playlist module");
+ mod->type = ICES_INPUT_VORBIS;
+ mod->name = "playlist";
+ mod->getdata = playlist_read;
+ mod->initialise_buffer = playlist_init_buffer;
+ mod->buffer_count = ices_config->runner_count*100 + 200;
+ mod->prealloc_count = ices_config->runner_count * 30 + 40;
+
+ mod->internal = calloc(1, sizeof(struct playlist_state));
+ if (mod->internal == NULL)
+ return -1;
+
+ return 0;
+}
+
+
Modified: icecast/branches/kh/ices/src/im_playlist.h
===================================================================
--- icecast/trunk/ices/src/im_playlist.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_playlist.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_playlist.h
* - Basic playlist functionality
*
- * $Id: im_playlist.h,v 1.6 2004/01/12 23:39:39 karl Exp $
+ * $Id: im_playlist.h,v 1.3 2002/07/07 11:07:55 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -17,25 +17,40 @@
#include "inputmodule.h"
#include <ogg/ogg.h>
-typedef struct _playlist_state_tag
+struct playlist_state
{
- FILE *current_file;
- char *filename; /* Currently streaming file */
- int errors; /* Consecutive errors */
- int current_serial;
- int nexttrack;
- int allow_repeat;
- ogg_sync_state oy;
-
- char *(*get_filename)(void *data); /* returns the next desired filename */
+ FILE *current_file;
+ char *filename; /* Currently streaming file */
+ int errors; /* Consecutive errors */
+ int next_track;
+ int terminate;
+ int sleep;
+ int os_init;
+ int more_headers;
+ int prev_window;
+ ogg_int64_t granulepos;
+ ogg_sync_state oy;
+ ogg_stream_state os;
+ vorbis_info vi;
+ vorbis_comment vc;
+ ogg_packet *prev_packet;
+ ogg_packet *prev_op;
+
+ char *(*get_filename)(void *data); /* returns the next desired filename */
void (*free_filename)(void *data, char *fn); /* Called when im_playlist is
done with this filename */
- void (*clear)(void *data); /* module clears self here */
+ void (*clear) (void *data); /* module clears self here */
+ int (*write_func) (input_module_t *mod, void *buffer, unsigned len);
+ void *(*alloc_buffer) (input_module_t *mod, unsigned len);
+ void (*flush_func) (input_module_t *mod);
- void *data; /* Internal data for this particular playlist module */
+ void *data; /* Internal data for this particular playlist module */
+};
-} playlist_state_t;
+int playlist_open_module (input_module_t *);
+void playlist_close_module (input_module_t *mod);
+int playlist_init_module (input_module_t *);
+void playlist_shutdown_module (input_module_t *mod);
-input_module_t *playlist_open_module(module_param_t *params);
#endif /* __IM_PLAYLIST_H__ */
Modified: icecast/branches/kh/ices/src/im_sun.c
===================================================================
--- icecast/trunk/ices/src/im_sun.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_sun.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,11 +1,12 @@
/* im_sun.c
* - Raw PCM input from Solaris audio devices
*
- * $Id: im_sun.c,v 1.13 2004/01/17 04:24:10 karl Exp $
+ * $Id: im_sun.c,v 1.7 2002/08/03 15:05:39 msmith Exp $
*
* by Ciaran Anscomb <ciarana at rd.bbc.co.uk>, based
* on im_oss.c which is...
- * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2001 Michael Smith <msmith at xiph.org>
+ * Copyright (c) 2003 karl Heyes <karl at xiph.org>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -13,8 +14,11 @@
* with this source.
*/
+#define SAMPLES 8192
+
+
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include <config.h>
#endif
#include <stdio.h>
@@ -31,8 +35,10 @@
#endif
#include <fcntl.h>
-#include "cfgparse.h"
+
+#include "thread/thread.h"
#include "stream.h"
+#include "signals.h"
#include "inputmodule.h"
#include "metadata.h"
@@ -43,224 +49,301 @@
#define BUFSIZE 8192
-static void close_module(input_module_t *mod)
+#define MAX_DEAD_AUDIO_BYTES 48000*2*2
+
+
+static void sun_return_buffer (input_module_t *mod __attribute__((unused)), input_buffer *ib)
{
- if(mod)
+ ib->critical = 0;
+ ib->eos = 0;
+ return;
+}
+
+
+static void sun_free_buffer (input_module_t *mod, input_buffer *ib)
+{
+ im_sun_state *s = mod->internal;
+ float **ptr;
+ int i;
+
+ ptr = ib->buf;
+ for (i=s->device_info.record.channels; i; i--)
{
- if(mod->internal)
+ if (ptr)
{
- im_sun_state *s = mod->internal;
- if(s->fd >= 0)
- close(s->fd);
- thread_mutex_destroy(&s->metadatalock);
- free(s);
+ free (*ptr);
+ *ptr = NULL;
}
- free(mod);
+ ptr++;
}
+ free (ib->buf);
+ ib->buf = NULL;
}
-static int event_handler(input_module_t *mod, enum event_type ev, void *param)
+
+static int sun_initialise_buffer (input_module_t *mod, input_buffer *ib)
{
im_sun_state *s = mod->internal;
-
- switch(ev)
+ float **ptr;
+ int i;
+
+ if ((ib->buf = calloc (1, s->default_len)) == NULL)
+ return -1;
+
+ if ((ib->buf = calloc (s->device_info.record.channels, sizeof (float*))) == NULL)
+ return -1;
+ ptr = ib->buf;
+ for (i=s->device_info.record.channels ; i; i--)
{
- case EVENT_SHUTDOWN:
- close_module(mod);
- break;
- case EVENT_NEXTTRACK:
- s->newtrack = 1;
- break;
- case EVENT_METADATAUPDATE:
- thread_mutex_lock(&s->metadatalock);
- if(s->metadata)
- {
- char **md = s->metadata;
- while(*md)
- free(*md++);
- free(s->metadata);
- }
- s->metadata = (char **)param;
- s->newtrack = 1;
- thread_mutex_unlock(&s->metadatalock);
- break;
- default:
- LOG_WARN1("Unhandled event %d", ev);
+ if ((*ptr = calloc (sizeof(float), s->samples)) == NULL)
return -1;
+ ptr++;
}
-
+
+ ib->type = ICES_INPUT_PCM;
+#ifdef WORDS_BIGENDIAN
+ ib->subtype = INPUT_PCM_BE_16;
+#else
+ ib->subtype = INPUT_PCM_LE_16;
+#endif
+ ib->critical = 0;
+ ib->channels = s->device_info.record.channels;
+ ib->samplerate = s->device_info.record.sample_rate;
+ ib->mod = mod;
+
return 0;
}
-static void metadata_update(void *self, vorbis_comment *vc)
-{
- im_sun_state *s = self;
- char **md;
- thread_mutex_lock(&s->metadatalock);
- md = s->metadata;
-
- if(md)
+void sun_close_module (input_module_t *mod)
+{
+ im_sun_state *s = mod->internal;
+
+ LOG_INFO0("Closing Sun audio module");
+ if (s->fd > -1)
{
- while(*md)
- vorbis_comment_add(vc, *md++);
+ close (s->fd);
+ s->fd = -1;
}
+}
- thread_mutex_unlock(&s->metadatalock);
+void sun_shutdown_module (input_module_t *mod)
+{
+ im_sun_state *s = mod->internal;
+
+ LOG_INFO0 ("Shutdown Sun audio module");
+ free (s);
+ mod->internal = NULL;
}
+
+
/* Core streaming function for this module
* This is what actually produces the data which gets streamed.
*
- * returns: >0 Number of bytes read
- * 0 Non-fatal error.
- * <0 Fatal error.
*/
-static int sun_read(void *self, ref_buffer *rb)
+static int sun_read(input_module_t *mod)
{
- int result;
- im_sun_state *s = self;
- unsigned char *i, j;
+ im_sun_state *s = mod->internal;
+ input_buffer *ib;
+ int len;
+ int dead_air;
- rb->buf = malloc(BUFSIZE*2*s->device_info.record.channels);
- if(!rb->buf)
- return -1;
- result = read(s->fd, rb->buf, BUFSIZE*2*s->device_info.record.channels);
+ while (1)
+ {
+ if (s->user_terminated)
+ {
+ s->user_terminated = 0;
+ return 0;
+ }
- rb->len = result;
- rb->aux_data = s->device_info.record.sample_rate*s->device_info.record.channels*2;
+ dead_air = 100;
+ while ((ib = input_alloc_buffer (mod)) == NULL)
+ {
+ if (dead_air == 100)
+ LOG_WARN0 ("will skip input for a short time");
+ read (s->fd, dead_audio, MAX_DEAD_AUDIO_BYTES);
+ if (--dead_air == 0)
+ {
+ mod->failures++;
+ return 0;
+ }
+ }
+
+ len = read (s->fd, s->read_buffer, s->read_buffer_len);
+
+ if (len == -1)
+ {
+ LOG_ERROR1("Error reading from audio device: %s", strerror(errno));
+ input_free_buffer (ib);
+ return 0;
+ }
- if(s->newtrack)
- {
- rb->critical = 1;
- s->newtrack = 0;
- }
+ ib->samples = len/(ib->channels*2);
- if(result == -1 && errno == EINTR)
- {
- return 0; /* Non-fatal error */
+#ifdef __sparc
+ uninterleave_pcm_be ((signed char*)s->read_buffer, ib->channels, ib->samples, ib->buf);
+#else
+ uninterleave_pcm_le ((signed char*)s->read_buffer, ib->channels, ib->samples, ib->buf);
+#endif
+
+ if (s->newtrack)
+ {
+ LOG_DEBUG0 ("metadata updates flagged");
+ metadata_thread_signal (mod, ib);
+ ib->critical = 1;
+ s->newtrack = 0;
+ }
+
+ if (metadata_update_signalled || ices_config->shutdown || move_to_next_input)
+ {
+ LOG_DEBUG0("marking buffer eos");
+ s->newtrack = 1;
+ ib->eos = 1;
+ if (move_to_next_input || ices_config->shutdown)
+ s->user_terminated = 1;
+ }
+ input_adv_sleep ((uint64_t)ib->samples * 1000000 / s->device_info.record.sample_rate);
+ send_for_processing (mod, ib);
}
- else if(result <= 0)
- {
- if(result == 0)
- LOG_INFO0("Reached EOF, no more data available");
- else
- LOG_ERROR1("Error reading from audio device: %s", strerror(errno));
- free(rb->buf);
- return -1;
- }
- return rb->len;
+ return 0;
}
-input_module_t *sun_open_module(module_param_t *params)
+
+
+int sun_init_module(input_module_t *mod)
{
- input_module_t *mod = calloc(1, sizeof(input_module_t));
im_sun_state *s;
module_param_t *current;
char *device = "/dev/audio"; /* default device */
int sample_rate = 44100;
int channels = 2;
- int use_metadata = 1; /* Default to on */
+ int samples = SAMPLES;
+ mod->name = "Sun Audio";
+
mod->type = ICES_INPUT_PCM;
-#ifdef WORDS_BIGENDIAN
- mod->subtype = INPUT_PCM_BE_16;
-#else
mod->subtype = INPUT_PCM_LE_16;
-#endif
mod->getdata = sun_read;
- mod->handle_event = event_handler;
- mod->metadata_update = metadata_update;
+ mod->initialise_buffer = sun_initialise_buffer;
+ mod->free_input_buffer = sun_free_buffer;
+ mod->buffer_count = ices_config->runner_count*10 + 5;
+ mod->prealloc_count = ices_config->runner_count * 4;
mod->internal = calloc(1, sizeof(im_sun_state));
- s = mod->internal;
+ do
+ {
+ if (mod->internal == NULL)
+ break;
+ s = mod->internal;
- s->fd = -1; /* Set it to something invalid, for now */
+ s->fd = -1; /* Set it to something invalid, for now */
- thread_mutex_create(&s->metadatalock);
+ current = mod->module_params;
- current = params;
+ while (current) {
+ if (!strcmp(current->name, "rate"))
+ sample_rate = atoi(current->value);
+ else if (!strcmp(current->name, "channels"))
+ channels = atoi(current->value);
+ else if (!strcmp(current->name, "device"))
+ device = current->value;
+ else if (!strcmp(current->name, "samples"))
+ samples = atoi (current->value);
+ else if(!strcmp(current->name, "metadatafilename"))
+ mod->metadata_filename = current->value;
+ else
+ LOG_WARN1("ignored parameter %s for sun module", current->name);
+ current = current->next;
+ }
- while (current) {
- if (!strcmp(current->name, "rate"))
- sample_rate = s->device_info.record.sample_rate = atoi(current->value);
- else if (!strcmp(current->name, "channels"))
- channels = s->device_info.record.channels = atoi(current->value);
- else if (!strcmp(current->name, "device"))
- device = current->value;
- else if (!strcmp(current->name, "metadata"))
- use_metadata = atoi(current->value);
- else if(!strcmp(current->name, "metadatafilename"))
- ices_config->metadata_filename = current->value;
- else
- LOG_WARN1("Unknown parameter %s for sun module", current->name);
- current = current->next;
+ /* Try and set up what we want */
+ AUDIO_INITINFO(&s->device_info);
+ s->device_info.record.sample_rate = sample_rate;
+ s->device_info.record.channels = channels;
+ s->device_info.record.precision = 16;
+ s->device_info.record.encoding = AUDIO_ENCODING_LINEAR;
+ s->device_info.record.port = AUDIO_LINE_IN;
+ s->device_info.record.pause = 0;
+ s->device = device;
+ s->samples = samples;
+ s->default_len = samples * 2 * channels;
+ s->read_buffer_len = s->samples*2*channels;
+ s->read_buffer = malloc (s->read_buffer_len);
+
+ return 0;
}
+ while (0);
+ /* error, need to cleanup */
+ if (mod->internal)
+ {
+ free (mod->internal);
+ mod->internal = NULL;
+ }
+ return -1;
+}
+int sun_open_module (input_module_t *mod)
+{
+ im_sun_state *s = mod->internal;
+ int sample_rate = s->device_info.record.sample_rate;
+ int channels = s->device_info.record.channels;
+
/* First up, lets open the audio device */
- if((s->fd = open(device, O_RDONLY, 0)) < 0) {
- LOG_ERROR2("Failed to open audio device %s: %s",
- device, strerror(errno));
- goto fail;
- }
+ do
+ {
+ if ((s->fd = open(s->device, O_RDONLY, 0)) < 0)
+ {
+ LOG_ERROR2("Failed to open audio device %s: %s", s->device, strerror(errno));
+ break;
+ }
- /* Try and set up what we want */
- AUDIO_INITINFO(&s->device_info);
- s->device_info.record.sample_rate = sample_rate;
- s->device_info.record.channels = channels;
- s->device_info.record.precision = 16;
- s->device_info.record.encoding = AUDIO_ENCODING_LINEAR;
- s->device_info.record.port = AUDIO_LINE_IN;
- s->device_info.record.pause = 0;
-
- if (ioctl(s->fd, AUDIO_SETINFO, &s->device_info) < 0) {
- LOG_ERROR2("Failed to configure audio device %s: %s",
- device, strerror(errno));
- goto fail;
- }
+ if (ioctl (s->fd, AUDIO_SETINFO, &s->device_info) < 0)
+ {
+ LOG_ERROR2("Failed to configure audio device %s: %s",
+ s->device, strerror(errno));
+ break;
+ }
#ifdef __sun
- ioctl (s->fd, I_FLUSH, FLUSHR);
+ ioctl(s->fd, I_FLUSH, FLUSHR);
#endif
#ifdef __OpenBSD__
- ioctl (s->fd, AUDIO_FLUSH, NULL);
+ ioctl(s->fd, AUDIO_FLUSH, NULL);
#endif
- /* Check all went according to plan */
- if (s->device_info.record.sample_rate != sample_rate) {
- LOG_ERROR0("Couldn't set sampling rate");
- goto fail;
- }
- if (s->device_info.record.channels != channels) {
- LOG_ERROR0("Couldn't set number of channels");
- goto fail;
- }
- if (s->device_info.record.precision != 16) {
- LOG_ERROR0("Couldn't set 16 bit precision");
- goto fail;
- }
- if (s->device_info.record.encoding != AUDIO_ENCODING_LINEAR) {
- LOG_ERROR0("Couldn't set linear encoding");
- goto fail;
- }
+ /* Check all went according to plan */
+ if (s->device_info.record.sample_rate != sample_rate)
+ {
+ LOG_ERROR0("Couldn't set sampling rate");
+ break;
+ }
+ if (s->device_info.record.channels != channels) {
+ LOG_ERROR0("Couldn't set number of channels");
+ break;
+ }
+ if (s->device_info.record.precision != 16)
+ {
+ LOG_ERROR0("Couldn't set 16 bit precision");
+ break;
+ }
+ if (s->device_info.record.encoding != AUDIO_ENCODING_LINEAR)
+ {
+ LOG_ERROR0("Couldn't set linear encoding");
+ break;
+ }
- /* We're done, and we didn't fail! */
- LOG_INFO3("Opened audio device %s at %d channel(s), %d Hz",
- device, channels, sample_rate);
+ /* We're done, and we didn't fail! */
+ LOG_INFO3("Opened audio device %s at %d channel(s), %d Hz",
+ s->device, channels, sample_rate);
- if(use_metadata)
- {
- LOG_INFO0("Starting metadata update thread");
- if(ices_config->metadata_filename)
- thread_create("im_sun-metadata", metadata_thread_signal, mod, 1);
- else
- thread_create("im_sun-metadata", metadata_thread_stdin, mod, 1);
+ s->newtrack = 1;
+ return 0;
}
+ while (0);
- return mod;
-
-fail:
- close_module(mod); /* safe, this checks for valid contents */
+ sun_close_module(mod); /* safe, this checks for valid contents */
return NULL;
}
+
Modified: icecast/branches/kh/ices/src/im_sun.h
===================================================================
--- icecast/trunk/ices/src/im_sun.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/im_sun.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* im_sun.h
* - read pcm data from sun devices
*
- * $Id: im_sun.h,v 1.4 2003/07/01 23:53:06 karl Exp $
+ * $Id: im_sun.h,v 1.2 2001/09/25 12:04:21 msmith Exp $
*
* by Ciaran Anscomb <ciarana at rd.bbc.co.uk>, based
* on im_oss.c which is...
@@ -18,20 +18,28 @@
#include <sys/audioio.h>
#include "inputmodule.h"
-#include "thread/thread.h"
#include <ogg/ogg.h>
typedef struct
{
- audio_info_t device_info;
- int fd;
- int fdctl;
- char **metadata;
- int newtrack;
- mutex_t metadatalock;
+ audio_info_t device_info;
+ int fd;
+ int fdctl;
+ char **metadata;
+ int newtrack;
+ int user_terminated;
+ int samples;
+ int default_len;
+ int read_buffer_len;
+ void *read_buffer;
+ const char *device;
} im_sun_state;
-input_module_t *sun_open_module(module_param_t *params);
+int sun_open_module (input_module_t *mod);
+void sun_close_module (input_module_t *mod);
+void sun_shutdown_module (input_module_t *mod);
+int sun_init_module (input_module_t *mod);
+
#endif /* __IM_SUN_H__ */
Modified: icecast/branches/kh/ices/src/input.c
===================================================================
--- icecast/trunk/ices/src/input.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/input.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,10 +1,9 @@
/* input.c
* - Main producer control loop. Fetches data from input modules, and controls
- * submission of these to the instance threads. Timing control happens here.
- *
- * $Id: input.c,v 1.32 2004/03/11 17:22:59 karl Exp $
+ * submission of these to the runner threads. Timing control happens here.
+ * originally based on the work by Michael Smith
*
- * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2001-4 Karl Heyes <karl at xiph.org>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -13,41 +12,50 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
+#include <pwd.h>
#include <sys/types.h>
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#include <ogg/ogg.h>
#include <vorbis/codec.h>
-#include <string.h>
-#include <timing/timing.h>
-#include <thread/thread.h>
+#include "runner.h"
+#include "thread/thread.h"
+#include "timing/timing.h"
#include "cfgparse.h"
-#include "stream.h"
-#include "input.h"
-#include "event.h"
+#include "metadata.h"
#include "inputmodule.h"
#include "im_playlist.h"
-#include "im_stdinpcm.h"
+#include "im_pcm.h"
+#include "signals.h"
-#ifdef HAVE_OSS
+#ifdef HAVE_OSS_AUDIO
#include "im_oss.h"
#endif
-#ifdef HAVE_SUN_AUDIO
-#include "im_sun.h"
+#ifdef HAVE_ALSA_AUDIO
+#include "im_alsa.h"
#endif
-#ifdef HAVE_ALSA
-#include "im_alsa.h"
+#ifdef HAVE_JACK
+#include "im_jack.h"
#endif
+#ifdef HAVE_SUN_AUDIO
+#include "im_sun.h"
+#endif
+
#ifdef _WIN32
typedef __int64 int64_t
typedef unsigned __int64 uint64_t
@@ -58,478 +66,378 @@
#define MAX_BUFFER_FAILURES 15
-typedef struct _timing_control_tag
-{
- uint64_t starttime;
- uint64_t senttime;
- uint64_t samples;
- uint64_t oldsamples;
- unsigned samplerate;
- long serialno;
-} timing_control;
+char dead_audio [DEAD_AIR_BYTES];
-typedef struct _module
-{
- char *name;
- input_module_t *(*open)(module_param_t *params);
-} module;
-
-static module modules[] = {
- { "playlist", playlist_open_module},
- { "stdinpcm", stdin_open_module},
-#ifdef HAVE_OSS
- { "oss", oss_open_module},
+module_t modules[] = {
+ { "playlist", playlist_init_module, playlist_open_module, playlist_close_module, playlist_shutdown_module },
+ { "pcm", pcm_initialise_module, pcm_open_module, pcm_close_module, pcm_shutdown_module },
+#ifdef HAVE_ALSA_AUDIO
+ { "alsa", alsa_init_module, alsa_open_module, alsa_close_module, alsa_shutdown_module },
#endif
-#ifdef HAVE_SUN_AUDIO
- { "sun", sun_open_module},
+#ifdef HAVE_JACK
+ { "jack", jack_init_module, jack_open_module, jack_close_module, jack_shutdown_module },
#endif
-#ifdef HAVE_ALSA
- { "alsa", alsa_open_module},
+#ifdef HAVE_OSS_AUDIO
+ { "oss", oss_init_module, oss_open_module, oss_close_module, oss_shutdown_module },
#endif
- {NULL,NULL}
+#ifdef HAVE_SUN_AUDIO
+ { "sun", sun_init_module, sun_open_module, sun_close_module, sun_shutdown_module },
+#endif
+ {NULL,NULL,NULL,NULL,NULL}
};
+
static timing_control control;
-
-/* This is identical to shout_sync(), really. */
-void input_sleep(void)
+void input_sleep (void)
{
- int64_t sleep;
+ signed long sleep;
- /* no need to sleep if we haven't sent data */
- if (control.senttime == 0) return;
+ sleep = control.senttime - ((timing_get_time() - control.starttime) *1000);
- sleep = ((double)control.senttime / 1000) -
- (timing_get_time() - control.starttime);
-
- /* trap for long sleeps, typically indicating a clock change. it's not */
- /* perfect though, as low bitrate/low samplerate vorbis can trigger this */
- if(sleep > 8000) {
- LOG_WARN1("Extended sleep requested (%ld ms), sleeping for 5 seconds",
- sleep);
- timing_sleep(5000);
+ if (sleep > 1000000)
+ {
+ LOG_WARN1 ("Sleeping for over 1 second (%ld), resetting timer", sleep);
+ control.starttime = timing_get_time();
+ control.senttime = 0;
+ sleep = 0;
}
- else if(sleep > 0)
- timing_sleep((uint64_t)sleep);
+#if 0
+ printf ("sentime is %lld\n", control.senttime/1000);
+ printf ("Sleeping for %ld\n", sleep);
+#endif
+ /* only sleep if the difference is above 5ms, might as well use the timeslice */
+ if (sleep > 5000)
+ {
+ /* printf ("Sleeping for %ld\n", sleep);
+ LOG_DEBUG1("sleep is %ld\n", sleep); */
+ thread_sleep((unsigned long)sleep);
+ }
}
-int input_calculate_pcm_sleep(unsigned bytes, unsigned bytes_per_sec)
+void input_adv_sleep (unsigned long adv)
{
- control.senttime += ((uint64_t)bytes * 1000000)/bytes_per_sec;
-
- return 0;
+ control.senttime += adv; /* in uS */
+ /* printf ("Adding %lu\n", adv); */
}
-int input_calculate_ogg_sleep(ogg_page *page)
-{
- static ogg_stream_state os;
- ogg_packet op;
- static vorbis_info vi;
- static vorbis_comment vc;
- static int need_start_pos, need_headers, state_in_use = 0;
- static int serialno = 0;
- static uint64_t offset;
- static uint64_t first_granulepos;
- if (ogg_page_granulepos(page) == -1)
+void uninterleave_pcm_le (signed char *src, unsigned channels, unsigned samples, float **dest)
+{
+ unsigned int i,j;
+ float *dst;
+ signed char *from = src;
+ for (j=0 ; j<channels ; j++)
{
- LOG_ERROR0("Timing control: corrupt timing information in vorbis file, cannot stream.");
- return -1;
+ dst = dest[j];
+ from = src+(j<<1);
+ for(i=samples; i ; i--)
+ {
+ *dst = (((*(from+1)) << 8) | (0x00ff&(int)(*from))) / 32768.f;
+ dst++;
+ from += channels*2;
+ }
}
- if (ogg_page_bos (page))
- {
- control.oldsamples = 0;
+}
- if (state_in_use)
- ogg_stream_clear (&os);
- ogg_stream_init (&os, ogg_page_serialno (page));
- serialno = ogg_page_serialno (page);
- state_in_use = 1;
- vorbis_info_init (&vi);
- vorbis_comment_init (&vc);
- need_start_pos = 1;
- need_headers = 3;
- offset = (uint64_t)0;
+
+
+void uninterleave_pcm_be (signed char *src, unsigned channels, unsigned samples, float **dest)
+{
+ unsigned int i,j;
+ float *dst;
+ signed char *from = src;
+ for (j=0 ; j<channels ; j++)
+ {
+ dst = dest[j];
+ from = src+(j<<1);
+ for(i=samples; i ; i--)
+ {
+ *dst = (((*from) << 8) | (0x00ff&(int)(*(from+1)))) / 32768.f;
+ dst++;
+ from += channels*2;
+ }
}
- if (need_start_pos)
+}
+
+static int initialise_input_modules (void)
+{
+ input_module_t *mod;
+
+ mod = ices_config->inputs;
+ while (mod)
{
- int found_first_granulepos = 0;
+ int i;
+ input_buffer *ib;
- ogg_stream_pagein (&os, page);
- while (ogg_stream_packetout (&os, &op) == 1)
+ if (mod->initialise_module (mod) < 0)
+ return -1;
+
+ mod->free_list_tail = &mod->free_list;
+
+ /* set the global minimum if need be */
+ if (mod->buffer_count < 2)
+ mod->buffer_count = 2;
+
+ if (mod->prealloc_count < 2)
+ mod->prealloc_count = 2;
+ if (mod->prealloc_count > mod->buffer_count)
+ mod->prealloc_count = mod->buffer_count;
+
+ /* create the pre-allocated buffers */
+ for (i=mod->prealloc_count; i ; i--)
{
- if (need_headers)
- {
- if (vorbis_synthesis_headerin (&vi, &vc, &op) < 0)
- {
- LOG_ERROR0("Timing control: can't determine sample rate for input, not vorbis.");
- control.samplerate = 0;
- return -1;
- }
- need_headers--;
- control.samplerate = vi.rate;
+ ib = calloc (1, sizeof (input_buffer));
+ if (ib == NULL)
+ return -1;
+ if (mod->initialise_buffer && mod->initialise_buffer (mod, ib) < 0)
+ return -1;
- if (need_headers == 0)
- {
- vorbis_comment_clear (&vc);
- first_granulepos = (uint64_t)0;
- return 0;
- }
- continue;
- }
- /* headers have been read */
- if (first_granulepos == 0 && op.granulepos > 0)
- {
- first_granulepos = op.granulepos;
- found_first_granulepos = 1;
- }
- offset += vorbis_packet_blocksize (&vi, &op) / 4;
+ *mod->free_list_tail = ib;
+ mod->free_list_tail = &ib->next;
}
- if (!found_first_granulepos)
- return 0;
+ LOG_DEBUG4 ("Module %d (%s) has pre-allocated %d buffers out of %d",
+ mod->id, mod->name, mod->prealloc_count, mod->buffer_count);
- need_start_pos = 0;
- control.oldsamples = first_granulepos - offset;
- vorbis_info_clear (&vi);
- ogg_stream_clear (&os);
- state_in_use = 0;
+ mod = mod->next;
}
- if (serialno != ogg_page_serialno (page))
- {
- LOG_ERROR0 ("Found page which does not belong to current logical stream");
- return -1;
- }
- control.samples = ogg_page_granulepos (page) - control.oldsamples;
- control.oldsamples = ogg_page_granulepos (page);
- control.senttime += ((uint64_t)control.samples * 1000000 /
- (uint64_t)control.samplerate);
-
return 0;
}
-
-void input_flush_queue(buffer_queue *queue, int keep_critical)
+void input_free_buffer(input_buffer *ib)
{
- queue_item *item, *next, *prev=NULL;
+ input_module_t *mod;
+ if (ib == NULL)
+ return;
- LOG_DEBUG0("Input queue flush requested");
+ mod = ib->mod;
- thread_mutex_lock(&queue->lock);
- if(!queue->head)
+ if (ib->serial != mod->expected)
{
- thread_mutex_unlock(&queue->lock);
- return;
+ LOG_DEBUG2("expected %lld, saw %lld", mod->expected, ib->serial);
+ mod->expected = ib->serial + 1;
}
-
- item = queue->head;
- while(item)
+ else
+ mod->expected++;
+ metadata_free (ib->metadata);
+ ib->metadata = NULL;
+ if (mod->release_input_buffer)
+ mod->release_input_buffer (mod, ib);
+ ib->next = NULL;
+ ib->critical = 0;
+ ib->eos = 0;
+ mod->released_serial++;
+#if 0
+ /* every so often, actually free a buffer, so that memory usage is reduced */
+ if (mod->allotted_serial - mod->released_serial > mod->prealloc_count)
+ /* if ((ib->serial & (uint64_t)255) == 255) */
{
- next = item->next;
-
- if(!(keep_critical && item->buf->critical))
- {
- thread_mutex_lock(&ices_config->refcount_lock);
- item->buf->count--;
- if(!item->buf->count)
- {
- free(item->buf->buf);
- free(item->buf);
- }
- thread_mutex_unlock(&ices_config->refcount_lock);
-
- if(prev)
- prev->next = next;
- else
- queue->head = next;
-
- free(item);
- item = next;
-
- queue->length--;
- }
- else
- {
- prev = item;
- item = next;
- }
+ if (mod->free_input_buffer)
+ mod->free_input_buffer (mod, ib);
+ free (ib);
+ /* LOG_DEBUG1 ("removed buffer, length now %d", mod->allotted_serial - mod->released_serial); */
}
-
- /* Now, fix up the tail pointer */
- queue->tail = NULL;
- item = queue->head;
-
- while(item)
+ else
+#endif
{
- queue->tail = item;
- item = item->next;
+ *mod->free_list_tail = ib;
+ mod->free_list_tail = &ib->next;
}
- thread_mutex_unlock(&queue->lock);
+ return;
}
-void input_loop(void)
+input_buffer *input_alloc_buffer (input_module_t *mod)
{
- input_module_t *inmod=NULL;
- instance_t *instance, *prev, *next;
- queue_item *queued;
- int shutdown = 0;
- int current_module = 0;
- int valid_stream = 1;
- int inc_count;
- int not_waiting_for_critical;
+ input_buffer *ib;
- thread_cond_create(&ices_config->queue_cond);
- thread_cond_create(&ices_config->event_pending_cond);
- thread_mutex_create(&ices_config->refcount_lock);
- thread_mutex_create(&ices_config->flush_lock);
-
- memset (&control, 0, sizeof (control));
-
- while(ices_config->playlist_module && modules[current_module].open)
+ do
{
- if(!strcmp(ices_config->playlist_module, modules[current_module].name))
+ ib = mod->free_list;
+ if (ib == NULL || ib->next == NULL)
{
- inmod = modules[current_module].open(ices_config->module_params);
- break;
+ unsigned in_use = mod->allotted_serial - mod->released_serial;
+ if (in_use > mod->buffer_count)
+ return NULL;
+ ib = calloc (1, sizeof (input_buffer));
+ if (ib == NULL)
+ return NULL;
+ if (mod->initialise_buffer && mod->initialise_buffer (mod, ib) < 0)
+ return NULL;
+ mod->buffer_alloc++;
+ mod->delay_buffer_check = 1000;
+ /* LOG_DEBUG1 ("added buffer, length now %d", mod->allotted_serial - mod->released_serial); */
}
- current_module++;
+ else
+ {
+ mod->free_list = ib->next;
+ if (mod->delay_buffer_check == 0 && mod->buffer_alloc > mod->prealloc_count)
+ {
+ /* LOG_DEBUG0("reducing free list"); */
+ if (mod->free_input_buffer)
+ mod->free_input_buffer (mod, ib);
+ mod->buffer_alloc--;
+ ib = NULL;
+ mod->delay_buffer_check = 500;
+ continue;
+ }
+ if (mod->delay_buffer_check)
+ mod->delay_buffer_check--;
+ }
}
+ while (ib == NULL);
- if(!inmod)
- {
- LOG_ERROR1("Couldn't initialise input module \"%s\"",
- ices_config->playlist_module);
- return;
- }
+ ib->next = NULL;
+ ib->serial = mod->allotted_serial++;
- ices_config->inmod = inmod;
+ return ib;
+}
- /* ok, basic config stuff done. Now, we want to start all our listening
- * threads.
- */
- instance = ices_config->instances;
+void process_input(input_module_t *mod)
+{
+ if (!mod)
+ {
+ LOG_ERROR0("NULL input module");
+ return;
+ }
- while(instance)
- {
- stream_description *arg = calloc(1, sizeof(stream_description));
- arg->stream = instance;
- arg->input = inmod;
- /*
- if(instance->savefilename != NULL)
- thread_create("savefile", savefile_stream, arg, 1);
- */
- thread_create("stream", ices_instance_stream, arg, 1);
+ move_to_next_input = 0;
+ control.samples = control.oldsamples = 0;
- instance = instance->next;
- }
- /* treat as if a signal has arrived straight away */
- signal_usr1_handler (0);
+ while (mod->getdata (mod))
+ ;
+}
- /* now we go into the main loop
- * We shut down the main thread ONLY once all the instances
- * have killed themselves.
- */
- while(!shutdown)
- {
- ref_buffer *chunk = calloc(1, sizeof(ref_buffer));
- buffer_queue *current;
- int ret;
+static input_module_t *open_next_input_module (input_module_t *mod)
+{
+ input_module_t *next_mod = mod;
- instance = ices_config->instances;
- prev = NULL;
-
- while(instance)
+ if (ices_config->shutdown || mod == NULL)
+ return NULL;
+ do
+ {
+ LOG_DEBUG1 ("checking module %d", next_mod->id);
+ if (next_mod->failures < 10)
{
- /* if an instance has died, get rid of it
- ** this should be replaced with something that tries to
- ** restart the instance, probably.
- */
- if (instance->died)
+ if (next_mod->open_module)
{
- LOG_DEBUG0("An instance died, removing it");
- next = instance->next;
+ time_t start = time (NULL);
- if (prev)
- prev->next = next;
- else
- ices_config->instances = next;
-
- /* Just in case, flush any existing buffers
- * Locks shouldn't be needed, but lets be SURE */
- thread_mutex_lock(&ices_config->flush_lock);
- input_flush_queue(instance->queue, 0);
- thread_mutex_unlock(&ices_config->flush_lock);
-
- config_free_instance(instance);
- free(instance);
-
- instance = next;
- continue;
+ if (next_mod->start+2 > start)
+ {
+ LOG_WARN0("restarted input within 2 seconds, it probably failed");
+ next_mod->failures++;
+ }
+ if (next_mod->open_module (next_mod) == 0)
+ {
+ next_mod->start = start;
+ ices_config->next_track = 0;
+ return next_mod;
+ }
}
-
- prev = instance;
- instance = instance->next;
+ else
+ next_mod->failures++;
}
+ else
+ LOG_WARN2 ("Too many failures on input module %d (%s)", next_mod->id, next_mod->name);
- instance = ices_config->instances;
+ next_mod = next_mod->next;
- if(!instance)
- {
- shutdown = 1;
- free(chunk);
- continue;
- }
+ } while (next_mod != mod && next_mod);
- if(ices_config->shutdown) /* We've been signalled to shut down, but */
- { /* the instances haven't done so yet... */
- timing_sleep(250); /* sleep for quarter of a second */
- free(chunk);
- continue;
- }
-
- /* If this is the first time through, set initial time. This should
- * be done before the call to inmod->getdata() below, in order to
- * properly keep time if this input module blocks.
- */
- if (control.starttime == 0)
- control.starttime = timing_get_time();
+ return NULL;
+}
- /* get a chunk of data from the input module */
- ret = inmod->getdata(inmod->internal, chunk);
- /* input module signalled non-fatal error. Skip this chunk */
- if(ret==0)
- {
- free(chunk);
- continue;
- }
+static void free_modules()
+{
+ input_module_t *mod = ices_config->inputs;
+ input_buffer *next, *ib;
- /* Input module signalled fatal error, shut down - nothing we can do
- * from here */
- if(ret < 0)
+ LOG_DEBUG0 ("freeing up module storage");
+ while (mod)
+ {
+ ib = mod->free_list;
+ while (ib)
{
- ices_config->shutdown = 1;
- thread_cond_broadcast(&ices_config->queue_cond);
- free(chunk);
- continue;
+ next = ib->next;
+ if (mod->free_input_buffer)
+ mod->free_input_buffer (mod, ib);
+ free (ib);
+ ib = next;
}
+ mod->free_list = NULL;
+ mod->free_list_tail = NULL;
+ mod = mod->next;
+ }
+}
- if(chunk->critical)
- valid_stream = 1;
+void send_for_processing (input_module_t *mod, input_buffer *ib)
+{
+#if 0
+ if (ib->critical) printf ("BOS seen\n");
+ if (ib->eos) printf ("EOS seen\n");
+#endif
+ send_to_runner (ices_config->runners, ib);
+}
- if(ret < 0) {
- /* Tell the input module to go to the next track, hopefully allowing
- * resync. */
- ices_config->inmod->handle_event(ices_config->inmod,
- EVENT_NEXTTRACK,NULL);
- valid_stream = 0;
- }
- inc_count = 0;
- not_waiting_for_critical = 0;
+void *input_loop(void *arg)
+{
+ input_module_t *mod;
+ struct runner *r = ices_config->runners;
- if(valid_stream)
- {
- while(instance)
- {
- if(instance->wait_for_critical && !chunk->critical)
- {
- instance = instance->next;
- continue;
+ start_runners();
+ thread_sleep (300000);
- }
+ if (initialise_input_modules () < 0)
+ {
+ printf ("Unable to initialise input modules\n");
+ return NULL;
+ }
- not_waiting_for_critical = 1;
+ /* start the clock */
+ control.starttime = timing_get_time();
- if(instance->skip)
- {
- instance = instance->next;
- continue;
- }
-
- queued = malloc(sizeof(queue_item));
-
- queued->buf = chunk;
- current = instance->queue;
-
- inc_count++;
-
- thread_mutex_lock(¤t->lock);
-
- if(current->head == NULL)
- {
- current->head = current->tail = queued;
- current->head->next = current->tail->next = NULL;
- }
- else
- {
- current->tail->next = queued;
- queued->next = NULL;
- current->tail = queued;
- }
+ mod = open_next_input_module (ices_config->inputs);
- current->length++;
- thread_mutex_unlock(¤t->lock);
+ while (ices_config->shutdown == 0)
+ {
+ if (mod == NULL)
+ break;
- instance = instance->next;
- }
- }
+ process_input (mod);
- /* If everything is waiting for a critical buffer, force one
- * early. (This will take effect on the next pass through)
- */
- if(valid_stream && !not_waiting_for_critical) {
- ices_config->inmod->handle_event(ices_config->inmod,
- EVENT_NEXTTRACK,NULL);
- instance = ices_config->instances;
- while(instance) {
- thread_mutex_lock(&ices_config->flush_lock);
- input_flush_queue(instance->queue, 0);
- instance->wait_for_critical = 0;
- thread_mutex_unlock(&ices_config->flush_lock);
- instance = instance->next;
- }
+ if (mod->close_module)
+ {
+ LOG_INFO0("Closing input module");
+ mod->close_module (mod);
}
- /* Make sure we don't end up with a 0-refcount buffer that
- * will never hit any of the free points. (this happens
- * if all threads are set to skip, for example).
- */
- thread_mutex_lock(&ices_config->refcount_lock);
- chunk->count += inc_count;
- if(!chunk->count)
+ mod = mod->next;
+ if (mod == NULL)
{
- free(chunk->buf);
- free(chunk);
+ if (ices_config->input_once_thru)
+ {
+ ices_config->shutdown = 1;
+ break;
+ }
+ else
+ mod = ices_config->inputs;
}
- thread_mutex_unlock(&ices_config->refcount_lock);
- if(valid_stream) {
- /* wake up the instances */
- thread_cond_broadcast(&ices_config->queue_cond);
-
- }
+ mod = open_next_input_module (mod);
}
- LOG_INFO0 ("All instances removed, shutting down...");
+ LOG_DEBUG0("All input stopped, shutting down.");
- ices_config->shutdown = 1;
- thread_cond_broadcast(&ices_config->event_pending_cond);
- timing_sleep(250); /* sleep for quarter of a second */
+ runner_close (r);
+ free_modules();
- thread_cond_destroy(&ices_config->queue_cond);
- thread_cond_destroy(&ices_config->event_pending_cond);
- thread_mutex_destroy(&ices_config->flush_lock);
- thread_mutex_destroy(&ices_config->refcount_lock);
-
- inmod->handle_event(inmod, EVENT_SHUTDOWN, NULL);
-
- return;
+ return NULL;
}
-
Modified: icecast/branches/kh/ices/src/inputmodule.h
===================================================================
--- icecast/trunk/ices/src/inputmodule.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/inputmodule.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* inputmodule.h
* - the interface for input modules to implement.
*
- * $Id: inputmodule.h,v 1.3 2003/03/16 14:21:48 msmith Exp $
+ * $Id: inputmodule.h,v 1.2 2001/09/25 12:04:21 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -14,32 +14,149 @@
#ifndef __INPUTMODULE_H__
#define __INPUTMODULE_H__
-#include "stream.h"
-#include "event.h"
-
#include <vorbis/codec.h>
-typedef enum _input_type {
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+typedef struct _input_module_tag input_module_t;
+typedef struct _timing_control_tag timing_control;
+typedef struct _input_buffer_tag input_buffer;
+typedef struct _module_param_tag module_param_t;
+typedef struct _module module_t;
+
+#define DEAD_AIR_BYTES 16384
+extern char dead_audio [DEAD_AIR_BYTES];
+
+typedef enum {
+ ICES_INPUT_NONE = 0,
ICES_INPUT_PCM,
ICES_INPUT_VORBIS,
+ ICES_INPUT_VORBIS_PACKET
/* Can add others here in the future, if we want */
} input_type;
typedef enum _input_subtype {
+ INPUT_SUBTYPE_INVALID,
INPUT_PCM_LE_16,
INPUT_PCM_BE_16,
+ INPUT_PCM_UNINTERLEAVE
} input_subtype;
-typedef struct _input_module_tag {
+
+#define MIN_INPUT_BUFFERS 12
+
+struct _input_buffer_tag
+{
+ uint64_t serial;
+ void *buf; /* could be ogg or pcm data */
+ /* unsigned len; */
+
+ int critical;
+ int eos;
+
+ /* long aux_data; */
input_type type;
input_subtype subtype;
- int (*getdata)(void *self, ref_buffer *rb);
- int (*handle_event)(struct _input_module_tag *self, enum event_type event,
- void *param);
- void (*metadata_update)(void *self, vorbis_comment *vc);
+ char **metadata;
+ input_module_t *mod;
+ input_buffer *next; /* for list work */
+
+ unsigned samplerate;
+ unsigned channels;
+ unsigned samples;
+};
+
+
+
+struct _timing_control_tag
+{
+ uint64_t starttime;
+ uint64_t senttime;
+ uint64_t oldsamples;
+ int samples;
+ int samplerate;
+ int channels;
+};
+
+
+
+struct _module_param_tag
+{
+ char *name;
+ char *value;
+
+ module_param_t *next;
+};
+
+
+struct _input_module_tag
+{
+ module_param_t *module_params;
+ char *module;
+
+ unsigned id;
+ const char *name;
+ input_type type;
+ input_subtype subtype;
+ unsigned failures;
+ time_t started;
+ unsigned buffer_count;
+ unsigned buffer_alloc;
+ unsigned prealloc_count;
+ unsigned delay_buffer_check;
+ time_t start;
+
+ int (*getdata)(input_module_t *self);
+ int (*initialise_module)(input_module_t *);
+ int (*open_module)(input_module_t *);
+ void (*close_module)(input_module_t *);
+ void (*shutdown_module)(input_module_t *);
+ void (*release_input_buffer)(input_module_t *, input_buffer *);
+ void (*free_input_buffer)(input_module_t *, input_buffer *);
+ int (*initialise_buffer)(input_module_t *, input_buffer *);
+
+ input_buffer *free_list, **free_list_tail;
+ uint64_t expected;
+ uint64_t allotted_serial, released_serial;
+
+ char *metadata_filename;
+
void *internal; /* For the modules internal state data */
-} input_module_t;
+ input_module_t *next;
+};
+
+
+struct _module
+{
+ char *name;
+ int (*initialise)(input_module_t *);
+ int (*open)(input_module_t *);
+ void (*close)(input_module_t *);
+ void (*shutdown)(input_module_t *);
+};
+
+
+extern module_t modules[];
+
+void input_sleep (void);
+void input_free_buffer(input_buffer *);
+input_buffer *input_alloc_buffer (input_module_t *);
+
+void uninterleave_pcm_le (signed char *src, unsigned channels, unsigned samples, float **dest);
+void uninterleave_pcm_be (signed char *src, unsigned channels, unsigned samples, float **dest);
+
+void input_adv_sleep (unsigned long adv); /* advance time in uS */
+#if 0
+void calculate_pcm_sleep(unsigned len, unsigned rate);
+int calculate_ogg_sleep(ogg_page *page);
+#endif
+
+void send_for_processing (input_module_t *mod, input_buffer *ib);
+void *input_loop(void*);
+
#endif /* __INPUTMODULE_H__ */
Modified: icecast/branches/kh/ices/src/logging.h
===================================================================
--- icecast/trunk/ices/src/logging.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/logging.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -16,38 +16,33 @@
#include "log/log.h"
-#ifndef __GNUC__
-#define __FUNCTION__ ""
-#endif
+#define LOG_ERROR0(x) log_write(ices_config->log_id,1, MODULE, __func__,x)
+#define LOG_ERROR1(x,a) log_write(ices_config->log_id,1, MODULE, __func__,x, a)
+#define LOG_ERROR2(x,a,b) log_write(ices_config->log_id,1, MODULE, __func__,x, a,b)
+#define LOG_ERROR3(x,a,b,c) log_write(ices_config->log_id,1, MODULE, __func__,x, a,b,c)
+#define LOG_ERROR4(x,a,b,c,d) log_write(ices_config->log_id,1, MODULE, __func__,x, a,b,c,d)
+#define LOG_ERROR5(x,a,b,c,d,e) log_write(ices_config->log_id,1, MODULE, __func__,x, a,b,c,d,e)
+#define LOG_WARN0(x) log_write(ices_config->log_id,2, MODULE, __func__,x)
+#define LOG_WARN1(x,a) log_write(ices_config->log_id,2, MODULE, __func__,x, a)
+#define LOG_WARN2(x,a,b) log_write(ices_config->log_id,2, MODULE, __func__,x, a,b)
+#define LOG_WARN3(x,a,b,c) log_write(ices_config->log_id,2, MODULE, __func__,x, a,b,c)
+#define LOG_WARN4(x,a,b,c,d) log_write(ices_config->log_id,2, MODULE, __func__,x, a,b,c,d)
-#define LOG_ERROR0(x) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x)
-#define LOG_ERROR1(x,a) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x, a)
-#define LOG_ERROR2(x,a,b) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x, a,b)
-#define LOG_ERROR3(x,a,b,c) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x, a,b,c)
-#define LOG_ERROR4(x,a,b,c,d) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x, a,b,c,d)
-#define LOG_ERROR5(x,a,b,c,d,e) log_write(ices_config->log_id,1, MODULE, __FUNCTION__,x, a,b,c,d,e)
+#define LOG_INFO0(x) log_write(ices_config->log_id,3, MODULE, __func__,x)
+#define LOG_INFO1(x,a) log_write(ices_config->log_id,3, MODULE, __func__,x, a)
+#define LOG_INFO2(x,a,b) log_write(ices_config->log_id,3, MODULE, __func__,x, a,b)
+#define LOG_INFO3(x,a,b,c) log_write(ices_config->log_id,3, MODULE, __func__,x, a,b,c)
+#define LOG_INFO4(x,a,b,c,d) log_write(ices_config->log_id,3, MODULE, __func__,x, a,b,c,d)
+#define LOG_INFO5(x,a,b,c,d,e) log_write(ices_config->log_id,3, MODULE, __func__,x, a,b,c,d,e)
-#define LOG_WARN0(x) log_write(ices_config->log_id,2, MODULE, __FUNCTION__,x)
-#define LOG_WARN1(x,a) log_write(ices_config->log_id,2, MODULE, __FUNCTION__,x, a)
-#define LOG_WARN2(x,a,b) log_write(ices_config->log_id,2, MODULE, __FUNCTION__,x, a,b)
-#define LOG_WARN3(x,a,b,c) log_write(ices_config->log_id,2, MODULE, __FUNCTION__,x, a,b,c)
-#define LOG_WARN4(x,a,b,c,d) log_write(ices_config->log_id,2, MODULE, __FUNCTION__,x, a,b,c,d)
+#define LOG_DEBUG0(x) log_write(ices_config->log_id,4, MODULE, __func__,x)
+#define LOG_DEBUG1(x,a) log_write(ices_config->log_id,4, MODULE, __func__,x, a)
+#define LOG_DEBUG2(x,a,b) log_write(ices_config->log_id,4, MODULE, __func__,x, a,b)
+#define LOG_DEBUG3(x,a,b,c) log_write(ices_config->log_id,4, MODULE, __func__,x, a,b,c)
+#define LOG_DEBUG4(x,a,b,c,d) log_write(ices_config->log_id,4, MODULE, __func__,x, a,b,c,d)
+#define LOG_DEBUG5(x,a,b,c,d,e) log_write(ices_config->log_id,4, MODULE, __func__,x, a,b,c,d,e)
-#define LOG_INFO0(x) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x)
-#define LOG_INFO1(x,a) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x, a)
-#define LOG_INFO2(x,a,b) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x, a,b)
-#define LOG_INFO3(x,a,b,c) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x, a,b,c)
-#define LOG_INFO4(x,a,b,c,d) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x, a,b,c,d)
-#define LOG_INFO5(x,a,b,c,d,e) log_write(ices_config->log_id,3, MODULE, __FUNCTION__,x, a,b,c,d,e)
-#define LOG_DEBUG0(x) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x)
-#define LOG_DEBUG1(x,a) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x, a)
-#define LOG_DEBUG2(x,a,b) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x, a,b)
-#define LOG_DEBUG3(x,a,b,c) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x, a,b,c)
-#define LOG_DEBUG4(x,a,b,c,d) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x, a,b,c,d)
-#define LOG_DEBUG5(x,a,b,c,d,e) log_write(ices_config->log_id,4, MODULE, __FUNCTION__,x, a,b,c,d,e)
-
-
#endif /* __LOGGING_H */
Modified: icecast/branches/kh/ices/src/metadata.c
===================================================================
--- icecast/trunk/ices/src/metadata.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/metadata.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* metadata.c
* - Metadata manipulation
*
- * $Id: metadata.c,v 1.13 2004/02/24 15:39:14 karl Exp $
+ * $Id: metadata.c,v 1.6 2002/07/20 12:52:06 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -12,7 +12,7 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
@@ -23,153 +23,98 @@
#include "cfgparse.h"
#include "inputmodule.h"
-#include "event.h"
-#include "thread/thread.h"
#define MODULE "metadata/"
#include "logging.h"
volatile int metadata_update_signalled = 0;
-void *metadata_thread_stdin(void *arg)
+
+void metadata_thread_signal(input_module_t *mod, input_buffer *buffer)
{
- char buf[1024];
- input_module_t *mod = arg;
+ static char line[1024];
+ char **md = NULL;
+ int comments = 0;
+ FILE *file;
- if (ices_config->background)
+ metadata_update_signalled = 0;
+
+ if (mod->metadata_filename == NULL)
+ return;
+ file = fopen(mod->metadata_filename, "r");
+ if (file == NULL)
{
- LOG_INFO0("Metadata thread shutting down, tried to use "
- "stdin from background");
- return NULL;
+#ifdef STRERROR_R_CHAR_P
+ char *buf = strerror_r (errno, line, sizeof (line));
+#else
+ char buf[256];
+ int i = strerror_r (errno, line, sizeof (line));
+#endif
+ LOG_WARN2("Failed to open file \"%s\" for metadata update: %s",
+ mod->metadata_filename, buf);
+ return;
}
- while(1)
- {
- char **md = NULL;
- int comments = 0;
- int wait_for_data = 1;
- /* wait for data */
- while (wait_for_data)
+ while(fgets(line, 1024, file))
+ {
+ if(line[0] == '\n')
+ break;
+ else
{
- struct timeval t;
- fd_set fds;
- FD_ZERO (&fds);
- FD_SET (0, &fds);
- t.tv_sec = 0;
- t.tv_usec = 150;
-
- switch (select (1, &fds, NULL, NULL, &t))
- {
- case 1:
- wait_for_data = 0;
- case 0:
- break;
- default:
- if (errno != EAGAIN)
- {
- LOG_INFO1 ("shutting down thread (%d)", errno);
- return NULL; /* problem get out quick */
- }
- break;
- }
+ char **old_buf;
+ unsigned len = strlen(line);
- if (ices_config->shutdown)
+ if(line[len-1] == '\n')
+ line[len-1] = '\0';
+ old_buf = md;
+ md = realloc(md, (comments+2)*sizeof(char *));
+ if (md)
{
- LOG_INFO0 ("metadata thread shutting down");
- return NULL;
- }
- }
- while(fgets(buf, 1024, stdin))
- {
- if(buf[0] == '\n')
- break;
- else
- {
- if(buf[strlen(buf)-1] == '\n')
- buf[strlen(buf)-1] = 0;
- md = realloc(md, (comments+2)*sizeof(char *));
- md[comments] = malloc(strlen(buf)+1);
-
- memcpy(md[comments], buf, strlen(buf)+1);
+ md[comments] = malloc(len+1);
+ strcpy(md[comments], line);
comments++;
}
+ else
+ md = old_buf;
}
+ }
- if(md) /* Don't update if there's nothing there */
- {
- md[comments]=0;
+ fclose(file);
- /* Now, let's actually use the new data */
- LOG_INFO0("Updating metadata");
- mod->handle_event(mod,EVENT_METADATAUPDATE,md);
- }
+ if(md) /* Don't update if there's nothing there */
+ {
+ md[comments]=NULL;
+ /* Now, let's actually use the new data */
+ LOG_INFO1("Updating metadata with %d comments", comments);
+ buffer->metadata = md;
}
+
}
-void *metadata_thread_signal(void *arg)
-{
- char buf[1024];
- input_module_t *mod = arg;
- while(1)
+void metadata_update(char **md, vorbis_comment *vc)
+{
+ if(md)
{
- char **md = NULL;
- int comments = 0;
- FILE *file;
-
- while(metadata_update_signalled == 0)
+ while(*md)
{
- thread_cond_wait(&ices_config->event_pending_cond);
- if (ices_config->shutdown)
- {
- LOG_INFO0 ("metadata thread shutting down");
- return NULL;
- }
- LOG_DEBUG0("meta thread wakeup");
+ LOG_INFO1 ("Adding comment %s", *md);
+ vorbis_comment_add(vc, *md++);
}
+ }
+}
- metadata_update_signalled = 0;
-
- file = fopen(ices_config->metadata_filename, "r");
- if(!file) {
- LOG_WARN2("Failed to open file \"%s\" for metadata update: %s",
- ices_config->metadata_filename, strerror(errno));
- continue;
- }
-
- LOG_DEBUG1("reading metadata from \"%s\"", ices_config->metadata_filename);
- while(fgets(buf, 1024, file))
+void metadata_free (char **md)
+{
+ if(md)
+ {
+ char **comment = md;
+ while(*comment)
{
- if(buf[0] == '\n')
- break;
- else
- {
- if(buf[strlen(buf)-1] == '\n')
- buf[strlen(buf)-1] = 0;
- md = realloc(md, (comments+2)*sizeof(char *));
- md[comments] = malloc(strlen(buf)+1);
-
- memcpy(md[comments], buf, strlen(buf)+1);
- comments++;
- LOG_INFO2 ("tag %d is %s", comments, buf);
- }
+ free(*comment);
+ comment++;
}
-
- fclose(file);
-
- if(md) /* Don't update if there's nothing there */
- {
- md[comments]=0;
-
- /* Now, let's actually use the new data */
- LOG_INFO0("Updating metadata");
- mod->handle_event(mod,EVENT_METADATAUPDATE,md);
- }
- else
- LOG_INFO0("No metadata has been read");
-
+ free(md);
}
}
-
-
Modified: icecast/branches/kh/ices/src/metadata.h
===================================================================
--- icecast/trunk/ices/src/metadata.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/metadata.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -14,8 +14,14 @@
#ifndef __METADATA_H__
#define __METADATA_H__
-void *metadata_thread_stdin(void *arg);
-void *metadata_thread_signal(void *arg);
+#include "inputmodule.h"
+extern int metadata_update_signalled;
+
+void *metadata_thread_signal(void *arg, input_buffer *buffer);
+void metadata_update(char **md, vorbis_comment *vc);
+void metadata_free (char **md);
+
+
#endif /* __METADATA_H__ */
Added: icecast/branches/kh/ices/src/om_file.c
===================================================================
--- icecast/trunk/ices/src/om_file.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/om_file.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,455 @@
+/* om_file.c
+ *
+ * output module for writing stream to file
+ *
+ * Copyright (c) 2003 Karl Heyes <karl at xiph.org>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>
+
+#include <ogg/ogg.h>
+
+#include <cfgparse.h>
+#define MODULE "om_file/"
+#include <logging.h>
+#include <om_file.h>
+
+static int _write_to_file (int savefile, ogg_page *og)
+{
+ struct iovec iov[2];
+ iov[0].iov_base = (void*)og->header;
+ iov[0].iov_len = og->header_len;
+ iov[1].iov_base = (void*)og->body;
+ iov[1].iov_len = og->body_len;
+ if (ogg_page_granulepos(og) == -1)
+ LOG_DEBUG1 ("granulepos is negative on page %ld", ogg_page_pageno(og));
+ if (writev (savefile, iov, 2) == -1)
+ {
+ LOG_ERROR1 ("Failed to write to save file %s", strerror (errno));
+ return -1;
+ };
+ return 0;
+}
+
+
+static void _store_last_packet (struct output_file_stream *file, ogg_packet *packet)
+{
+ /* copy the last packet on the file, so that it can be used as the first packet*/
+ /* of the next, vorbis packets are interleaved so this prevent s data loss */
+ ogg_packet *store_op = &file->last_packet;
+
+ if (store_op->packet)
+ {
+ free (store_op->packet);
+ store_op->packet = NULL;
+ }
+ memcpy (store_op, packet, sizeof (file->last_packet));
+ if ((store_op->packet = malloc (store_op->bytes)))
+ {
+ memcpy (store_op->packet, packet->packet, packet->bytes);
+ }
+ if (store_op->granulepos < 0)
+ store_op->granulepos = 0;
+
+ file->last_packet_exists = 1;
+}
+
+
+static void _flush_ogg_file (struct output_module *mod, ogg_packet *op)
+{
+ /* struct output_stream_state *out = &state->file; */
+ struct output_file_stream *out = mod->specific;
+ ogg_page page;
+ int write_it = 1;
+
+ long eos = op->e_o_s;
+
+ op->e_o_s = 1;
+#ifdef DEBUG
+ LOG_DEBUG0("marked packet for EOS");
+#endif
+ ogg_stream_packetin (&mod->os, op);
+
+ while (ogg_stream_flush (&mod->os, &page) > 0)
+ {
+#ifdef DEBUG
+ LOG_DEBUG1("flushing page (eos %d)", (ogg_page_eos (&page)?1:0));
+#endif
+ if (write_it && _write_to_file (out->fd, &page) < 0)
+ write_it = 0;
+ }
+ ogg_stream_clear (&mod->os);
+ /* reset to what it was */
+ op->e_o_s = eos;
+}
+
+
+static int _output_file_create(struct output_module *mod, char *filename)
+{
+ char *start = filename;
+ struct output_file_stream *stream = mod->specific;
+
+ if (filename == NULL || filename[0] == '\0')
+ return -1;
+ if (filename[0] == '/')
+ start++;
+
+ while (1)
+ {
+ char *ptr;
+ struct stat st;
+
+ ptr = strchr (start, '/');
+ if (ptr == NULL)
+ break;
+ *ptr = '\0';
+
+#ifdef DEBUG
+ LOG_DEBUG2 ("checking path %s (%s)", filename, start);
+#endif
+ if (stat (filename, &st) < 0)
+ {
+ char cwd[PATH_MAX];
+ char *tmp_ptr;
+ int ret = 0;
+
+ if (errno != ENOENT)
+ {
+ LOG_ERROR2 ("unable to stat path %s (%s)", start, strerror (errno));
+ return -1;
+ }
+ if (getcwd (cwd, sizeof (cwd)) == NULL)
+ {
+ LOG_ERROR0 ("failed to get cwd");
+ return -1;
+ }
+ /* ok now create the directory, but we need to strip off the
+ * new part first */
+ if ((tmp_ptr = strrchr (filename, '/')))
+ *tmp_ptr = '\0';
+ if (tmp_ptr && filename[0] && chdir (filename) < 0)
+ {
+ LOG_ERROR1 ("chdir failed on %s", filename);
+ if (tmp_ptr)
+ *tmp_ptr = '/';
+ ret = -1;
+ }
+ else
+ {
+ if (mkdir (start, stream->dmask))
+ {
+ LOG_ERROR2("Failed to mkdir %s in %s", start, filename);
+ if (tmp_ptr)
+ *tmp_ptr = '/';
+ ret = -1;
+ }
+ else
+ {
+ if (tmp_ptr)
+ *tmp_ptr = '/';
+ LOG_INFO1 ("created directory %s", filename);
+ }
+ }
+ if (chdir (cwd) < 0)
+ {
+ LOG_ERROR0 ("failed to reset cwd");
+ ret = -1;
+ }
+ if (ret == -1)
+ return -1;
+ }
+ start = ptr+1;
+ *ptr = '/';
+ }
+ stream->fd = open (filename, O_CREAT|O_EXCL|O_WRONLY, stream->fmask);
+ if (stream->fd == -1)
+ {
+ LOG_WARN2 ("Failed to open file %s (%s)", filename, strerror (errno));
+ return -1;
+ }
+ LOG_INFO1 ("Saving to file %s", filename);
+ return 0;
+}
+
+
+static int file_audio_pageout (struct output_module *mod, ogg_page *page)
+{
+ int ret;
+
+ if (mod->initial_packets)
+ {
+ mod->initial_packets--;
+ if (mod->initial_packets == 0)
+ ret = ogg_stream_flush (&mod->os, page);
+ else
+ ret = 0;
+ }
+ else
+ ret = ogg_stream_pageout (&mod->os, page);
+
+ return ret;
+}
+
+
+/* return non-zero is packet flushed to file */
+static int _output_file_timeout (struct output_module *mod, time_t time_val, ogg_packet *op)
+{
+ int ret = 0, close_it = 0;
+ long packetno = op->packetno;
+ struct output_state *state = mod->parent;
+ struct output_file_stream *stream = mod->specific;
+
+ if (stream->fd < 0)
+ return 0;
+ if (stream->close_on_new_headers && state->new_headers)
+ {
+ close_it = 1;
+ }
+ else
+ {
+ if (stream->saveduration)
+ {
+ if (stream->save_start + (time_t)stream->saveduration <= time_val)
+ {
+ ogg_int64_t granule = op->granulepos;
+ op->packetno = mod->packetno++;
+ op->granulepos -= mod->start_pos;
+ _flush_ogg_file (mod, op);
+ op->granulepos = granule;
+ close_it = 1;
+ LOG_DEBUG0("file duration expired");
+ }
+ }
+ }
+ if (close_it)
+ {
+ LOG_DEBUG0 ("Closing save file");
+ close (stream->fd);
+ stream->fd = -1;
+ _store_last_packet (stream, op);
+ mod->packetno = 3;
+ op->packetno = packetno;
+ ret = 1;
+ }
+ return ret;
+}
+
+
+int output_ogg_file (struct output_module *mod, ogg_packet *op, unsigned samples)
+{
+ int output_needs_headers = 0;
+ struct output_state *state = mod->parent;
+ struct output_file_stream *file = mod->specific;
+ ogg_page page;
+ long packetno = op->packetno;
+ ogg_int64_t granule = op->granulepos;
+ time_t time_val;
+
+ /* does the file need closing */
+ time_val = time(NULL); /* called far too much */
+ if (_output_file_timeout (mod, time_val, op))
+ {
+ return 0;
+ }
+#ifdef DEBUG
+ if (op->e_o_s)
+ LOG_DEBUG0("packet seen with eos set");
+#endif
+ /* if no file open then open it */
+ if (file->fd == -1)
+ {
+ struct tm tm;
+ char filename [PATH_MAX];
+ int ret;
+
+ localtime_r (&time_val, &tm);
+ if (file->savefilename)
+ {
+ ret = strftime (filename, sizeof (filename), file->savefilename, &tm);
+ for (; ret && filename [ret-1]=='\\'; ret--)
+ filename [ret-1] = '\0';
+ if (ret == 0)
+ {
+ LOG_WARN1 ("Unable to generate a filename (%d)", ret);
+ return -1;
+ }
+ LOG_DEBUG2 ("filename expansion %s -> %s", file->savefilename, filename);
+ }
+ else
+ snprintf (filename, sizeof (filename), "save-%lu.ogg", file->count++);
+ if (_output_file_create (mod, filename) < 0)
+ return -1;
+
+ file->save_start = time_val;
+ output_needs_headers = 1;
+ }
+ if (state->new_headers || output_needs_headers)
+ {
+ ogg_page page;
+ long packetno;
+ int i;
+
+ if (mod->in_use)
+ {
+ LOG_DEBUG0 ("clearing ogg file stream");
+ ogg_stream_clear (&mod->os);
+ }
+
+ LOG_DEBUG0 ("initialising output stream");
+ if (mod->reset)
+ mod->start_pos = 0;
+ mod->initial_packets = 2;
+ mod->start_pos = 0;
+ mod->in_use = 1;
+ ogg_stream_init (&mod->os, ++mod->serial);
+ mod->granule = (uint64_t)0;
+ mod->packetno = 0; /* need to verify is start from 0 or 3 */
+ for (i=0; i< 3 ; i++)
+ {
+ packetno = state->packets[i] . packetno;
+ state->packets[i] . packetno = mod->packetno++;;
+ /* LOG_DEBUG1 ("Added header %lld", state->packets[i] . packetno); */
+ ogg_stream_packetin (&mod->os, &state->packets[i]);
+ }
+
+ while (ogg_stream_flush (&mod->os, &page) > 0)
+ {
+ /* LOG_DEBUG2 ("header: granulepos is %lld on page %ld\n", ogg_page_granulepos (&page), ogg_page_pageno(&page)); */
+ if (file->fd > -1 && _write_to_file (file->fd, &page) < 0)
+ {
+ close (file->fd);
+ file->fd = -1;
+ }
+ }
+ output_needs_headers = 0;
+
+ if (file->last_packet_exists)
+ {
+ file->last_packet.e_o_s = 0;
+ if (mod->reset)
+ mod->start_pos = file->last_packet.granulepos;
+ file->last_packet.granulepos = 0;
+ file->last_packet.packetno = mod->packetno++;
+ /* LOG_DEBUG2 ("inital packet: granulepos is %lld on packet %ld", mod->last_packet.granulepos, mod->last_packet.packetno); */
+ ogg_stream_packetin (&mod->os, &file->last_packet);
+ }
+ if (op->e_o_s)
+ mod->initial_packets = 1;
+ }
+
+ op -> packetno = mod->packetno++;
+ granule = op->granulepos;
+ op->granulepos -= mod->start_pos;
+ /* LOG_DEBUG2 ("main %lld granulepos is %lld", op->packetno, op->granulepos); */
+ ogg_stream_packetin (&mod->os, op);
+
+ while (file_audio_pageout (mod, &page) > 0)
+ {
+ if (file->fd > -1 && _write_to_file (file->fd, &page) < 0)
+ {
+ close (file->fd);
+ file->fd = -1;
+ }
+ }
+ /* reset packet to what it was */
+ op->packetno = packetno;
+ op->granulepos = granule;
+ return 0;
+}
+
+
+static void output_filestream_clear (struct output_module *mod)
+{
+ if (mod)
+ {
+ struct output_file_stream *stream = mod->specific;
+ LOG_DEBUG0("clearing structure");
+ if (stream)
+ {
+ ogg_stream_clear (&mod->os);
+ if (stream->fd != -1)
+ close (stream->fd);
+ if (stream->savefilename)
+ xmlFree (stream->savefilename);
+ if (stream->last_packet.packet)
+ free (stream->last_packet.packet);
+ free (stream);
+ mod->specific = NULL;
+ }
+ }
+}
+
+
+
+int parse_savefile (xmlNodePtr node, void *arg)
+{
+ struct output_state *state = arg;
+ char *filename = NULL;
+ int duration = 60*60; /* default to 1 hour */
+ int single = 0; /* enable to have a single logical stream per file */
+ mode_t fmask = 0600; /* file umask */
+ mode_t dmask = 0700; /* directory umask */
+ int reset = 1;
+ struct output_module *mod;
+
+ mod = calloc (1, sizeof (struct output_module));
+ while (mod)
+ {
+ struct cfg_tag savefile_tags[] =
+ {
+ { "filename", get_xml_string, &filename },
+ { "duration", get_xml_int, &duration },
+ { "on-metadata", get_xml_bool, &single },
+ { "fmask", get_xml_int, &fmask },
+ { "dmask", get_xml_int, &dmask },
+ { "reset-time", get_xml_bool, &reset },
+ { NULL, NULL, NULL }
+ };
+ struct output_file_stream *stream;
+
+ if (parse_xml_tags ("savefile", node->xmlChildrenNode, savefile_tags))
+ break;
+
+ mod->parent = state;
+ mod->output_send = output_ogg_file;
+ mod->output_clear = output_filestream_clear;
+ stream = calloc (1, sizeof (struct output_file_stream));
+ if (stream == NULL)
+ break;
+
+ mod->reset = reset;
+ mod->specific = stream;
+ stream->fmask = fmask;
+ stream->dmask = dmask;
+ stream->close_on_new_headers = single;
+ stream->savefilename = filename;
+ stream->saveduration = duration;
+ stream->fd = -1;
+
+ mod->next = state->head;
+ state->head = mod;
+#ifdef DEBUG
+ printf("Added file output stream\n");
+#endif
+ return 0;
+ }
+ if (mod) free (mod);
+ if (filename) xmlFree (filename);
+
+ return -1;
+}
+
Added: icecast/branches/kh/ices/src/om_file.h
===================================================================
--- icecast/trunk/ices/src/om_file.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/om_file.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,21 @@
+#ifndef __OM_FILE_H
+#define __OM_FILE_H
+
+struct output_file_stream
+{
+ int fd;
+ char *savefilename;
+ mode_t fmask;
+ mode_t dmask;
+ unsigned long count;
+ unsigned saveduration;
+ time_t save_start;
+ /* int reset; */
+ int close_on_new_headers;
+ int last_packet_exists;
+ ogg_packet last_packet;
+};
+
+int parse_savefile (xmlNodePtr node, void *arg);
+
+#endif /* __OM_FILE_H */
Added: icecast/branches/kh/ices/src/om_shout.c
===================================================================
--- icecast/trunk/ices/src/om_shout.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/om_shout.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,354 @@
+/* om_shout.c
+ *
+ * - Output module for sending to shout streams
+ *
+ * Copyright (c) 2002 Karl Heyes <karl at xiph.org>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source. */
+
+#include <config.h>
+#include <string.h>
+#include <ogg/ogg.h>
+
+#include <cfgparse.h>
+
+#include <om_shout.h>
+
+#define MODULE "om_shout/"
+#include <logging.h>
+
+
+#define SHOUT_TIMEOUT 10
+
+#define DEFAULT_HOSTNAME "localhost"
+#define DEFAULT_PORT 8000
+#define DEFAULT_PASSWORD "password"
+#define DEFAULT_USERNAME "source"
+#define DEFAULT_MOUNT "/stream.ogg"
+#define DEFAULT_RECONN_DELAY 30
+#define DEFAULT_RECONN_ATTEMPTS -1
+
+
+static void _output_connection_close (struct output_module *mod)
+{
+ struct output_shout_state *stream = mod->specific;
+ LOG_DEBUG0("closed shout connection");
+ shout_close (stream->shout);
+ shout_free (stream->shout);
+ stream->shout = NULL;
+ if (stream->connected)
+ {
+ if (mod->in_use)
+ ogg_stream_clear (&mod->os);
+
+ stream->connected = 0;
+ }
+ stream->restart_time = time(NULL) + stream->reconnect_delay;
+ stream->reconnect_count++;
+}
+
+
+void check_shout_connected (struct output_module *mod)
+{
+ struct output_shout_state *stream = mod->specific;
+ if (shout_connection_ready (stream->shout))
+ return;
+ if (stream->reconnect_attempts == -1 ||
+ stream->reconnect_attempts > stream->reconnect_count)
+ {
+ time_t now = time (NULL);
+
+ if (stream->restart_time <= now)
+ {
+ int shouterr;
+
+ if (stream->shout == NULL || shout_get_errno (stream->shout) != SHOUTERR_PENDING)
+ {
+ char audio_info[11];
+ int failed = 1;
+ struct output_state *state = mod->parent;
+
+ LOG_DEBUG3 ("Time we started stream on %s:%d%s", stream->hostname,
+ stream->port, stream->mount);
+ /* allow for traping long icecast connects */
+ stream->restart_time = now;
+
+ do
+ {
+ if ((stream->shout = shout_new ()) == NULL)
+ break;
+ if (shout_set_host (stream->shout, stream->hostname) != SHOUTERR_SUCCESS)
+ break;
+ if (shout_set_password (stream->shout, stream->password) != SHOUTERR_SUCCESS)
+ break;
+ if (shout_set_port (stream->shout, stream->port) != SHOUTERR_SUCCESS)
+ break;
+ if (stream->user && shout_set_user (stream->shout, stream->user) != SHOUTERR_SUCCESS)
+ break;
+ if (shout_set_mount (stream->shout, stream->mount) != SHOUTERR_SUCCESS)
+ break;
+ if (shout_set_agent (stream->shout, PACKAGE_STRING) != SHOUTERR_SUCCESS)
+ break;
+ if (shout_set_name (stream->shout, state->name) != SHOUTERR_SUCCESS)
+ break;
+ if (shout_set_genre (stream->shout, state->genre) != SHOUTERR_SUCCESS)
+ break;
+ if (state->url && shout_set_url (stream->shout, state->url) != SHOUTERR_SUCCESS)
+ break;
+ if (shout_set_description (stream->shout, state->description) != SHOUTERR_SUCCESS)
+ break;
+ if (stream->yp && shout_set_public (stream->shout, 1) != SHOUTERR_SUCCESS)
+ break;
+
+ shout_set_nonblocking (stream->shout, 1);
+ shout_set_format (stream->shout, SHOUT_FORMAT_VORBISPAGE);
+ shout_set_protocol (stream->shout, SHOUT_PROTOCOL_HTTP);
+
+ snprintf(audio_info, sizeof(audio_info), "%ld", state->vi.bitrate_nominal/1000);
+ shout_set_audio_info (stream->shout, SHOUT_AI_BITRATE, audio_info);
+
+ snprintf(audio_info, sizeof(audio_info), "%ld", state->vi.rate);
+ shout_set_audio_info (stream->shout, SHOUT_AI_SAMPLERATE, audio_info);
+
+ snprintf(audio_info, sizeof(audio_info), "%d", state->vi.channels);
+ shout_set_audio_info (stream->shout, SHOUT_AI_CHANNELS, audio_info);
+ failed = 0;
+ }
+ while (0);
+ if (failed)
+ {
+ _output_connection_close (mod);
+ return;
+ }
+ }
+ if ((shouterr = shout_open(stream->shout)) == SHOUTERR_SUCCESS)
+ {
+ LOG_INFO3("Connected to server: %s:%d%s",
+ shout_get_host(stream->shout), shout_get_port(stream->shout), shout_get_mount(stream->shout));
+ stream->connected = 1;
+ stream->reconnect_count = 0;
+ mod->need_headers = 1;
+ return;
+ }
+ if (shouterr == SHOUTERR_PENDING)
+ {
+ if (now - stream->restart_time > SHOUT_TIMEOUT)
+ {
+ LOG_ERROR3("Terminating connection to %s:%d%s",
+ shout_get_host(stream->shout), shout_get_port(stream->shout),
+ shout_get_mount(stream->shout));
+ LOG_ERROR1("no reply came in %d seconds", SHOUT_TIMEOUT);
+ _output_connection_close (mod);
+ }
+ /* ok, we just need to come back to this connection later */
+ }
+ else
+ {
+ LOG_ERROR4("Failed to connect to %s:%d%s (%s)",
+ shout_get_host(stream->shout), shout_get_port(stream->shout),
+ shout_get_mount(stream->shout), shout_get_error(stream->shout));
+ _output_connection_close (mod);
+ }
+ }
+ return;
+ }
+ LOG_INFO1 ("%d reconnect attempts, will keep re-trying", stream->reconnect_count);
+}
+
+
+static int shout_audio_pageout (struct output_module *mod, ogg_page *page)
+{
+ struct output_shout_state *stream = mod->specific;
+ int ret;
+ uint64_t samples;
+
+ if (mod->initial_packets)
+ {
+ mod->initial_packets--;
+ if (mod->initial_packets == 0)
+ ret = ogg_stream_flush (&mod->os, page);
+ else
+ ret = 0;
+ }
+ else if (stream->page_samples > (uint64_t)mod->parent->vi.rate)
+ ret = ogg_stream_flush (&mod->os, page);
+ else
+ ret = ogg_stream_pageout (&mod->os, page);
+
+ if (ret > 0)
+ {
+ samples = ogg_page_granulepos (page) - stream->prev_page_granulepos;
+ stream->page_samples -= samples;
+ stream->prev_page_granulepos = ogg_page_granulepos (page);
+ }
+
+ return ret;
+}
+
+
+int output_ogg_shout (struct output_module *mod, ogg_packet *op, unsigned samples)
+{
+ struct output_shout_state *stream = mod->specific;
+ check_shout_connected (mod);
+ if (stream->connected)
+ {
+ int send_it = 1;
+ ogg_page page;
+ long packetno = op->packetno;
+ ogg_int64_t granule = op->granulepos;
+ struct output_state *state = mod->parent;
+
+ op -> packetno = mod->packetno++;
+
+ if (state->new_headers || mod->need_headers)
+ {
+ int val = mod->serial;
+ /* start of new logical stream */
+ LOG_DEBUG0 ("initialising output stream");
+ if (mod->in_use)
+ {
+ ogg_stream_clear (&mod->os);
+ }
+ while (val == mod->serial)
+ val = rand();
+ mod->serial = val;
+ ogg_stream_init (&mod->os, val);
+ mod->in_use = 1;
+ ogg_stream_packetin (&mod->os, &state->packets[0]);
+ ogg_stream_packetin (&mod->os, &state->packets[1]);
+ ogg_stream_packetin (&mod->os, &state->packets[2]);
+ mod->need_headers = 0;
+ mod->start_pos = granule;
+ stream->prev_page_granulepos = 0;
+ stream->prev_packet_granulepos = 0;
+
+ while (ogg_stream_flush (&mod->os, &page) > 0)
+ {
+ if (send_it && shout_send (stream->shout, &page, 1) != SHOUTERR_SUCCESS)
+ {
+ send_it = 0;
+ LOG_ERROR4("Failed to write headers to %s:%d%s (%s)",
+ shout_get_host (stream->shout), shout_get_port (stream->shout),
+ shout_get_mount (stream->shout), shout_get_error (stream->shout));
+ _output_connection_close (mod);
+ return 0;
+ }
+ }
+ mod->packetno = 3;
+ mod->initial_packets = 2; /* flush 2 packets into a single page */
+ }
+
+ op->granulepos -= mod->start_pos;
+ ogg_stream_packetin (&mod->os, op);
+
+ stream->page_samples += (op->granulepos - stream->prev_packet_granulepos);
+ stream->prev_packet_granulepos = op->granulepos;
+
+ while (shout_audio_pageout (mod, &page) > 0)
+ {
+ if (send_it && shout_send (stream->shout, &page, 1) != SHOUTERR_SUCCESS)
+ {
+ send_it = 0;
+ LOG_ERROR4("Failed to write to %s:%d%s (%s)",
+ shout_get_host (stream->shout), shout_get_port (stream->shout),
+ shout_get_mount (stream->shout), shout_get_error (stream->shout));
+ _output_connection_close (mod);
+ return 0;
+ }
+ }
+ /* reset to what it was */
+ op->packetno = packetno;
+ op->granulepos = granule;
+ }
+ return 0;
+}
+
+
+
+static void output_shout_clear (struct output_module *mod)
+{
+ if (mod)
+ {
+ struct output_shout_state *stream = mod->specific;
+ _output_connection_close (mod);
+ shout_free (stream->shout);
+ xmlFree (stream->hostname);
+ xmlFree (stream->user);
+ xmlFree (stream->password);
+ xmlFree (stream->mount);
+ free (stream);
+ }
+}
+
+
+
+int parse_shout (xmlNodePtr node, void *arg)
+{
+ struct output_state *state = arg;
+ char *hostname = NULL,
+ *user = xmlStrdup (DEFAULT_USERNAME),
+ *password = xmlStrdup (DEFAULT_PASSWORD),
+ *mount = xmlStrdup (DEFAULT_MOUNT);
+ int yp = 0,
+ reconnect_delay = DEFAULT_RECONN_DELAY,
+ reconnect_attempts = DEFAULT_RECONN_ATTEMPTS,
+ port = 8000;
+
+ struct cfg_tag shout_tags[] =
+ {
+ { "hostname", get_xml_string, &hostname },
+ { "port", get_xml_int, &port },
+ { "password", get_xml_string, &password },
+ { "username", get_xml_string, &user },
+ { "mount", get_xml_string, &mount },
+ { "yp", get_xml_bool, &yp },
+ { "reconnectdelay", get_xml_int, &reconnect_delay },
+ { "reconnectattempts", get_xml_int, &reconnect_attempts },
+ { NULL, NULL, NULL }
+ };
+
+ if (parse_xml_tags ("shout", node->xmlChildrenNode, shout_tags))
+ return 1;
+
+ if (hostname == NULL)
+ return 0;
+
+ while (1)
+ {
+ struct output_shout_state *stream = NULL;
+ struct output_module *mod = calloc (1, sizeof (struct output_module));
+ if (mod == NULL)
+ break;
+ mod->parent = state;
+ mod->output_send = output_ogg_shout;
+ mod->output_clear = output_shout_clear;
+ stream = calloc (1, sizeof (struct output_shout_state));
+ if (stream == NULL)
+ break;
+ mod->specific = stream;
+ stream->hostname = hostname;
+ stream->user = user;
+ stream->password = password;
+ stream->mount = mount;
+ stream->port = port;
+ stream->yp = yp;
+
+ stream->reconnect_delay = reconnect_delay;
+ stream->reconnect_attempts = reconnect_attempts;
+
+ /* add populated struct to list of outputs */
+ mod->next = state->head;
+ state->head = mod;
+ return 0;
+ }
+ fprintf (stderr, "shout output failed\n");
+ if (hostname) xmlFree (hostname);
+ if (user) xmlFree (user);
+ if (password) xmlFree (password);
+ if (mount) xmlFree (mount);
+ return -1;
+}
+
Added: icecast/branches/kh/ices/src/om_shout.h
===================================================================
--- icecast/trunk/ices/src/om_shout.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/om_shout.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,41 @@
+/* om_shout.h
+ *
+ * output module header for rebuild stream fro sending though libshout
+ *
+ * Copyright (c) 2002 Karl Heyes <karl at pts.tele2.co.uk>
+ *
+ * his program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifndef __OM_SHOUT_H
+#define __OM_SHOUT_H
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+struct output_shout_state
+{
+ shout_t *shout;
+ char *hostname;
+ char *user;
+ char *password;
+ char *mount;
+ int port;
+ int yp;
+ int connected;
+ int reconnect_delay;
+ int reconnect_count;
+ int reconnect_attempts;
+ time_t restart_time;
+ uint64_t page_samples;
+ uint64_t prev_page_granulepos;
+ uint64_t prev_packet_granulepos;
+};
+
+int parse_shout (xmlNodePtr node, void *arg);
+
+
+#endif /* __OM_SHOUT_H */
Modified: icecast/branches/kh/ices/src/playlist_basic.c
===================================================================
--- icecast/trunk/ices/src/playlist_basic.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/playlist_basic.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,10 @@
/* playlist_basic.c
* - Simple built-in unscripted playlist
*
- * $Id: playlist_basic.c,v 1.13 2003/08/13 00:58:02 karl Exp $
+ * $Id: playlist_basic.c,v 1.7 2002/08/10 04:26:52 msmith Exp $
*
- * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2001-2 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2003 Karl Heyes <k.heyes at blueyonder.co.uk>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -12,7 +13,7 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
@@ -33,14 +34,13 @@
static void shuffle(char **buf, int len)
{
- /* From ices1 src/playlist_basic/rand.c */
int n,d;
char *temp;
n = len;
while(n > 1)
{
- d = (int) ((double)len * rand()/(RAND_MAX+1.0));
+ d = (int) ((double)n * rand()/(RAND_MAX+1.0));
temp = buf[d];
buf[d] = buf[n-1];
buf[n-1] = temp;
@@ -55,7 +55,7 @@
char buf[1024];
int buflen;
- file = fopen(data->file, "rb");
+ file = fopen(data->file, "r");
if (file == NULL)
{
@@ -93,8 +93,12 @@
if(buflen < data->len+1)
{
+ char **tmp;
buflen += 100;
- data->pl = realloc(data->pl, buflen*sizeof(char *));
+ tmp = realloc(data->pl, buflen*sizeof(char *));
+ if (tmp == NULL)
+ break;
+ data->pl = tmp;
}
data->pl[data->len++] = strdup(buf);
@@ -103,10 +107,12 @@
if(data->random)
shuffle(data->pl, data->len);
+ fclose (file);
+
return 0;
}
-void playlist_basic_clear(void *data)
+static void playlist_basic_clear(void *data)
{
basic_playlist *pl = data;
if(pl)
@@ -116,24 +122,24 @@
int i;
for(i=0; i < pl->len; i++)
free(pl->pl[i]);
- free(pl->pl);
+ free(pl->pl);
}
- free(pl);
- }
+ free(pl);
+ }
}
-char *playlist_basic_get_next_filename(void *data)
+static char *playlist_basic_get_next_filename(void *data)
{
- basic_playlist *pl = (basic_playlist *)data;
+ basic_playlist *pl = (basic_playlist *)data;
char *ptr = NULL, *dest = NULL;
int reload_playlist = 0;
- struct stat st;
+ struct stat st;
- if (stat(pl->file, &st))
- {
- LOG_ERROR2("Couldn't stat file \"%s\": %s", pl->file, strerror(errno));
- return NULL;
- }
+ if (stat(pl->file, &st))
+ {
+ LOG_ERROR2("Couldn't stat file \"%s\": %s", pl->file, strerror(errno));
+ return NULL;
+ }
if (pl->pl)
{
@@ -157,17 +163,17 @@
return NULL;
if (pl->restartafterreread)
pl->pos = 0;
- }
+ }
- if (pl->pos >= pl->len) /* reached the end of the potentially updated list */
- {
- if (pl->once)
+ if (pl->pos >= pl->len) /* reached the end of the potentially updated list */
+ {
+ if (pl->once)
return NULL;
pl->pos = 0;
if (pl->random)
shuffle(pl->pl, pl->len);
- }
+ }
ptr = pl->pl [pl->pos++];
@@ -177,53 +183,54 @@
return dest;
}
-void playlist_basic_free_filename(void *data, char *fn)
+static void playlist_basic_free_filename(void *data __attribute__((unused)), char *fn)
{
- free (fn);
+ if (fn) free (fn);
}
-int playlist_basic_initialise(module_param_t *params, playlist_state_t *pl)
+int playlist_basic_initialise(module_param_t *params, struct playlist_state *pl)
{
- basic_playlist *data;
+ basic_playlist *data;
- pl->get_filename = playlist_basic_get_next_filename;
- pl->clear = playlist_basic_clear;
+ pl->get_filename = playlist_basic_get_next_filename;
+ pl->clear = playlist_basic_clear;
pl->free_filename = playlist_basic_free_filename;
+ pl->sleep = 1;
- pl->data = calloc(1, sizeof(basic_playlist));
- data = (basic_playlist *)pl->data;
+ pl->data = calloc(1, sizeof(basic_playlist));
+ data = (basic_playlist *)pl->data;
- while (params != NULL) {
- if (!strcmp(params->name, "file"))
- {
- if (data->file) free(data->file);
- data->file = params->value;
- }
- else if (!strcmp(params->name, "random"))
- data->random = atoi(params->value);
- else if(!strcmp(params->name, "once"))
- data->once = atoi(params->value);
- else if(!strcmp(params->name, "allow-repeats"))
- pl->allow_repeat = atoi(params->value);
- else if(!strcmp(params->name, "restart-after-reread"))
- data->restartafterreread = atoi(params->value);
- else if(!strcmp(params->name, "type"))
- ; /* We recognise this, but don't want to do anything with it */
- else
- {
- LOG_WARN1("Unknown parameter to playlist input module: %s",
- params->name);
- }
- params = params->next;
- }
+ while (params != NULL) {
+ if (!strcmp(params->name, "file"))
+ {
+ if (data->file) free(data->file);
+ data->file = params->value;
+ }
+ else if (!strcmp(params->name, "random"))
+ data->random = atoi(params->value);
+ else if(!strcmp(params->name, "once"))
+ data->once = atoi(params->value);
+ else if(!strcmp(params->name, "restart-after-reread"))
+ data->restartafterreread = atoi(params->value);
+ else if(!strcmp(params->name, "sleep"))
+ pl->sleep = atoi(params->value);
+ else if(!strcmp(params->name, "type"))
+ ; /* We recognise this, but don't want to do anything with it */
+ else
+ {
+ LOG_WARN1("Unknown parameter to playlist input module: %s",
+ params->name);
+ }
+ params = params->next;
+ }
- if (!data->file)
- {
- LOG_ERROR0("No filename specified for playlist module");
- free(data);
- return -1;
- }
+ if (!data->file)
+ {
+ LOG_ERROR0("No filename specified for playlist module");
+ free(data);
+ return -1;
+ }
- return 0;
+ return 0;
}
Modified: icecast/branches/kh/ices/src/playlist_basic.h
===================================================================
--- icecast/trunk/ices/src/playlist_basic.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/playlist_basic.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* playlist_basic.h
* - Simple unscripted playlist
*
- * $Id: playlist_basic.h,v 1.5 2003/03/16 14:21:49 msmith Exp $
+ * $Id: playlist_basic.h,v 1.4 2002/06/29 15:19:18 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -16,21 +16,19 @@
typedef struct
{
- char **pl;
- int len;
- int pos;
- char *file; /* Playlist file */
- time_t mtime;
- int random;
- int once;
+ char **pl;
+ int len;
+ int pos;
+ char *file; /* Playlist file */
+ time_t mtime;
+ int random;
+ int once;
int restartafterreread;
} basic_playlist;
-void playlist_basic_clear(void *data);
-char *playlist_basic_get_next_filename(void *data);
-int playlist_basic_initialise(module_param_t *params, playlist_state_t *pl);
-int playlist_script_initialise(module_param_t *params, playlist_state_t *pl);
+int playlist_basic_initialise (module_param_t *params, struct playlist_state *);
+int playlist_script_initialise (module_param_t *params, struct playlist_state *);
#endif /* __PLAYLIST_BASIC_H__ */
Modified: icecast/branches/kh/ices/src/playlist_script.c
===================================================================
--- icecast/trunk/ices/src/playlist_script.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/playlist_script.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -2,7 +2,7 @@
* - Gets a filename to play back based on output from a program/shell script
* run each time.
*
- * $Id: playlist_script.c,v 1.7 2003/08/13 00:58:02 karl Exp $
+ * $Id: playlist_script.c,v 1.4 2002/08/09 13:59:02 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -13,7 +13,7 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
@@ -32,12 +32,12 @@
char *program;
} script_playlist;
-void playlist_script_clear(void *data) {
+static void playlist_script_clear(void *data) {
if(data)
free(data);
}
-char *playlist_script_get_filename(void *data) {
+static char *playlist_script_get_filename(void *data) {
script_playlist *pl = data;
char *prog = pl->program;
FILE *pipe;
@@ -82,18 +82,19 @@
return buf;
}
-void playlist_script_free_filename(void *data, char *fn)
+static void playlist_script_free_filename(void *data __attribute__((unused)), char *fn)
{
free(fn);
}
-int playlist_script_initialise(module_param_t *params, playlist_state_t *pl)
+int playlist_script_initialise(module_param_t *params, struct playlist_state *pl)
{
script_playlist *data;
pl->get_filename = playlist_script_get_filename;
pl->clear = playlist_script_clear;
pl->free_filename = playlist_script_free_filename;
+ pl->sleep = 1;
pl->data = calloc(1, sizeof(script_playlist));
if(!pl->data)
@@ -106,11 +107,11 @@
if(data->program) free(data->program);
data->program = params->value;
}
- else if(!strcmp(params->name, "allow-repeats"))
- pl->allow_repeat = atoi(params->value);
else if(!strcmp(params->name, "type")) {
/* We ignore this one */
}
+ else if(!strcmp(params->name, "sleep"))
+ pl->sleep = atoi(params->value);
else
LOG_WARN1("Unknown parameter to playlist script module: %s",
params->name);
Modified: icecast/branches/kh/ices/src/reencode.c
===================================================================
--- icecast/trunk/ices/src/reencode.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/reencode.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,9 +1,10 @@
/* reencode.c
* - runtime reencoding of vorbis audio (usually to lower bitrates).
*
- * $Id: reencode.c,v 1.10 2003/12/22 14:01:09 karl Exp $
+ * $Id: reencode.c,v 1.6 2002/08/03 14:41:10 msmith Exp $
*
- * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2002-4 Karl Heyes <karl at xiph.org>
*
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
@@ -12,7 +13,7 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
@@ -22,8 +23,8 @@
#include <ogg/ogg.h>
#include <vorbis/codec.h>
-#include "reencode.h"
#include "cfgparse.h"
+#include "reencode.h"
#include "stream.h"
#include "encode.h"
#include "audio.h"
@@ -31,220 +32,160 @@
#define MODULE "reencode/"
#include "logging.h"
-reencode_state *reencode_init(instance_t *stream)
+extern ogg_packet *copy_ogg_packet (ogg_packet *packet);
+
+struct reencode *reencode_new_init (struct encoder_settings *settings)
{
- reencode_state *new = calloc(1, sizeof(reencode_state));
+ struct reencode *reenc = calloc (1, sizeof (struct reencode));
+ if (reenc)
+ {
+ reenc->encoder = encode_create();
+ if (reenc->encoder == NULL)
+ {
+ free (reenc);
+ return NULL;
+ }
- new->out_min_br = stream->min_br;
- new->out_nom_br = stream->nom_br;
- new->out_max_br = stream->max_br;
- new->quality = stream->quality;
- new->managed = stream->managed;
+ vorbis_info_init (&reenc->vi);
+ vorbis_comment_init (&reenc->vc);
+ reenc->settings = settings;
+ reenc->need_headers = 3;
+ LOG_DEBUG0("Reencoder setup complete");
+ }
+ return reenc;
+}
- new->out_samplerate = stream->samplerate;
- new->out_channels = stream->channels;
- new->current_serial = -1; /* FIXME: that's a valid serial */
- new->need_headers = 0;
- return new;
-}
-void reencode_clear(reencode_state *s)
+void reencode_free(struct reencode *s)
{
- if(s)
+ if (s)
{
- LOG_DEBUG0("Clearing reencoder");
- ogg_stream_clear(&s->os);
- vorbis_block_clear(&s->vb);
- vorbis_dsp_clear(&s->vd);
- vorbis_comment_clear(&s->vc);
- vorbis_info_clear(&s->vi);
-
- free(s);
+ if (s->need_headers == 0)
+ {
+ vorbis_block_clear (&s->vb);
+ vorbis_dsp_clear (&s->vd);
+ }
+ vorbis_comment_clear (&s->vc);
+ vorbis_info_clear (&s->vi);
+ downmix_clear (s->downmix);
+ resample_clear (s->resamp);
+ encode_free (s->encoder);
+ free (s);
}
}
-/* Returns: -1 fatal death failure, argh!
- * 0 haven't produced any output yet
- * >0 success
- */
-int reencode_page(reencode_state *s, ref_buffer *buf,
- unsigned char **outbuf, int *outlen)
-{
- ogg_page og, encog;
- ogg_packet op;
- int retbuflen=0, old;
- unsigned char *retbuf=NULL;
-
- og.header_len = buf->aux_data;
- og.body_len = buf->len - buf->aux_data;
- og.header = buf->buf;
- og.body = buf->buf + og.header_len;
- if(s->current_serial != ogg_page_serialno(&og))
+static int reencode_vorbis_header (struct reencode *s, ogg_packet *op)
+{
+ if (s->need_headers)
{
- s->current_serial = ogg_page_serialno(&og);
-
- if(s->encoder)
+ /* LOG_DEBUG0("processing vorbis header"); */
+ if (vorbis_synthesis_headerin (&s->vi, &s->vc, op) < 0)
{
- if(s->resamp) {
- resample_finish(s->resamp);
- encode_data_float(s->encoder, s->resamp->buffers,
- s->resamp->buffill);
+ LOG_ERROR1 ("Failed to process Vorbis headers (%d)", 4-s->need_headers);
+ return -1;
+ }
+ s->need_headers--;
+ if (s->need_headers == 0)
+ {
+ char **comment;
+ int channels = s->vi.channels;
+
+ vorbis_synthesis_init (&s->vd, &s->vi);
+ vorbis_block_init (&s->vd, &s->vb);
+
+ s->settings->encode_channels = s->vi.channels;
+ if (s->settings->channels && s->settings->channels != s->vi.channels)
+ {
+ s->downmix = downmix_initialise();
+ s->settings->encode_channels = 1;
}
- encode_finish(s->encoder);
- while(encode_flush(s->encoder, &encog) != 0)
+
+ s->settings->encode_rate = s->vi.rate;
+ if (s->settings->samplerate && s->settings->samplerate != s->vi.rate)
{
- old = retbuflen;
- retbuflen += encog.header_len + encog.body_len;
- retbuf = realloc(retbuf, retbuflen);
- memcpy(retbuf+old, encog.header, encog.header_len);
- memcpy(retbuf+old+encog.header_len, encog.body,
- encog.body_len);
+ s->settings->encode_rate = s->settings->samplerate;
+ s->resamp = resample_initialise (s->settings->encode_channels, s->vi.rate, s->settings->samplerate);
}
+
+ comment=s->vc.user_comments;
+ while (*comment)
+ {
+ encode_comment (s->encoder, *comment);
+ ++comment;
+ }
+
+ if (encode_setup (s->encoder, s->settings) < 0)
+ return -1;
}
- encode_clear(s->encoder);
- s->encoder = NULL;
- resample_clear(s->resamp);
- s->resamp = NULL;
- downmix_clear(s->downmix);
- s->downmix = NULL;
- ogg_stream_clear(&s->os);
- ogg_stream_init(&s->os, s->current_serial);
- ogg_stream_pagein(&s->os, &og);
+ return 0;
+ }
+ LOG_WARN0("function called when not expecting header");
+ return -1;
+}
- vorbis_block_clear(&s->vb);
- vorbis_dsp_clear(&s->vd);
- vorbis_comment_clear(&s->vc);
- vorbis_info_clear(&s->vi);
- vorbis_info_init(&s->vi);
- vorbis_comment_init(&s->vc);
+int reencode_packetin (struct reencode *s, ogg_packet *packet)
+{
+ int ret = 0;
+ float **pcm;
+ int samples;
- if(ogg_stream_packetout(&s->os, &op) != 1)
+ if (s->need_headers == 0)
+ {
+ if (vorbis_synthesis (&s->vb, packet) == 0)
{
- LOG_ERROR0("Invalid primary header in stream");
- return -1;
+ vorbis_synthesis_blockin (&s->vd, &s->vb);
}
- if(vorbis_synthesis_headerin(&s->vi, &s->vc, &op) < 0)
+ /* NOTE: we could expose pcm float buffer from encoder so that copies can be reduced further */
+ while ((samples = vorbis_synthesis_pcmout (&s->vd, &pcm)) > 0)
{
- LOG_ERROR0("Input stream not vorbis, can't reencode");
- return -1;
- }
-
- s->need_headers = 2; /* We still need two more header packets */
- LOG_DEBUG0("Reinitialising reencoder for new logical stream");
- }
- else
- {
- ogg_stream_pagein(&s->os, &og);
- while(ogg_stream_packetout(&s->os, &op) > 0)
- {
- if(s->need_headers)
+ if (s->downmix)
{
- vorbis_synthesis_headerin(&s->vi, &s->vc, &op);
- /* If this was the last header, init the rest */
- if(!--s->need_headers)
+ downmix_buffer_float(s->downmix, pcm, samples, s->vi.channels);
+ if(s->resamp)
{
- vorbis_block_init(&s->vd, &s->vb);
- vorbis_synthesis_init(&s->vd, &s->vi);
-
- s->encoder = encode_initialise(s->out_channels,
- s->out_samplerate, s->managed,
- s->out_min_br, s->out_nom_br, s->out_max_br,
- s->quality, &s->vc);
-
- if(!s->encoder) {
- LOG_ERROR0("Failed to configure encoder for reencoding");
- return -1;
- }
- if(s->vi.rate != s->out_samplerate) {
- s->resamp = resample_initialise(s->out_channels,
- s->vi.rate, s->out_samplerate);
- }
- else
- s->resamp = NULL;
-
- if(s->vi.channels != s->out_channels) {
- if(s->vi.channels == 2 && s->out_channels == 1)
- s->downmix = downmix_initialise();
- else {
- LOG_ERROR2("Converting from %d to %d channels is not"
- " currently supported", s->vi.channels,
- s->out_channels);
- return -1;
- }
- }
- else
- s->downmix = NULL;
+ resample_buffer_float(s->resamp, &s->downmix->buffer, samples);
+ encode_data_float(s->encoder, s->resamp->buffers, s->resamp->buffill);
}
+ else
+ encode_data_float(s->encoder, &s->downmix->buffer, samples);
}
+ else if (s->resamp)
+ {
+ resample_buffer_float(s->resamp, pcm, samples);
+ encode_data_float(s->encoder, s->resamp->buffers,
+ s->resamp->buffill);
+ }
else
{
- float **pcm;
- int samples;
-
-
- if(vorbis_synthesis(&s->vb, &op)==0)
- {
- vorbis_synthesis_blockin(&s->vd, &s->vb);
- }
-
- while((samples = vorbis_synthesis_pcmout(&s->vd, &pcm))>0)
- {
- if(s->downmix) {
- downmix_buffer_float(s->downmix, pcm, samples);
- if(s->resamp) {
- resample_buffer_float(s->resamp, &s->downmix->buffer,
- samples);
- encode_data_float(s->encoder, s->resamp->buffers,
- s->resamp->buffill);
- }
- else
- encode_data_float(s->encoder, &s->downmix->buffer,
- samples);
- }
- else if(s->resamp) {
- resample_buffer_float(s->resamp, pcm, samples);
- encode_data_float(s->encoder, s->resamp->buffers,
- s->resamp->buffill);
- }
- else
- encode_data_float(s->encoder, pcm, samples);
- vorbis_synthesis_read(&s->vd, samples);
- }
-
- while(encode_dataout(s->encoder, &encog) != 0)
- {
-
- old = retbuflen;
- retbuflen += encog.header_len + encog.body_len;
- retbuf = realloc(retbuf, retbuflen);
- memcpy(retbuf+old, encog.header, encog.header_len);
- memcpy(retbuf+old+encog.header_len, encog.body,
- encog.body_len);
- }
+ encode_data_float(s->encoder, pcm, samples);
}
+ vorbis_synthesis_read(&s->vd, samples);
+ ret = 1;
}
+ if (packet->e_o_s)
+ encode_endstream (s->encoder);
+ return ret;
}
+ return reencode_vorbis_header (s, packet);
+}
- /* We've completed every packet from this page, so
- * now we can return what we wanted, depending on whether
- * we actually got data out or not
- */
- if(retbuflen > 0)
+
+
+
+int reencode_packetout(struct reencode *s, ogg_packet *op)
+{
+ if (s)
{
- *outbuf = retbuf;
- *outlen = retbuflen;
- return retbuflen;
+ if (s->need_headers)
+ return 0;
+ return encode_packetout (s->encoder, op);
}
- else
- {
- return 0;
- }
+ return -1;
}
-
Modified: icecast/branches/kh/ices/src/reencode.h
===================================================================
--- icecast/trunk/ices/src/reencode.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/reencode.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* reencode.h
* - reencoding functions
*
- * $Id: reencode.h,v 1.7 2004/01/13 16:35:27 karl Exp $
+ * $Id: reencode.h,v 1.4 2002/08/03 14:41:10 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -17,45 +17,55 @@
#include <ogg/ogg.h>
#include <vorbis/codec.h>
-#include "cfgparse.h"
-#include "stream.h"
+typedef struct _reencode_tag reencode;
+
#include "encode.h"
+#include "inputmodule.h"
#include "audio.h"
-typedef struct {
- int out_min_br;
- int out_nom_br;
- int out_max_br;
- float quality;
- int managed;
+struct instance;
+struct reencode
+{
+ struct encoder *encoder;
+ struct encoder_settings *settings;
+
int out_samplerate;
int out_channels;
int in_samplerate;
int in_channels;
- int current_serial;
+ int in_use;
+
int need_headers;
- ogg_stream_state os;
vorbis_info vi;
vorbis_comment vc;
vorbis_dsp_state vd;
vorbis_block vb;
- encoder_state *encoder;
- downmix_state *downmix;
- resample_state *resamp;
+ struct downmix *downmix;
+ struct resample *resamp;
-} reencode_state;
+};
-reencode_state *reencode_init(instance_t *stream);
-int reencode_page(reencode_state *s, ref_buffer *buf,
- unsigned char **outbuf, int *outlen);
-void reencode_clear(reencode_state *s);
+struct reencode *reencode_new_init ();
+int reencode_init (struct instance *);
+void reencode_free (struct reencode *s);
+int reencode_pagein (struct reencode *s, ogg_page *og);
+int reencode_pageout (struct reencode *s, ogg_page *og);
+int reencode_packetout (struct reencode *s, ogg_packet *op);
+int reencode_packetin (struct reencode *s, ogg_packet *packet);
+void reencode_clear(struct reencode *s);
+int reencode_send (struct instance *stream);
+int reencode_flush (struct reencode *s, ogg_page *og);
+void reencode_setup (struct reencode *s, long serial);
+int reencode_ogg_header (struct reencode *s, ogg_page *og, unsigned samplerate, unsigned channels);
+
+
#endif /* __REENCODE_H */
Modified: icecast/branches/kh/ices/src/resample.c
===================================================================
--- icecast/trunk/ices/src/resample.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/resample.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,6 @@
/* resample.c: see resample.h for interesting stuff */
-
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include <config.h>
#endif
#include <math.h>
@@ -129,7 +128,7 @@
}
-int resampler_init(resampler_state *state, int channels, int outfreq, int infreq, resampler_parameter op1, ...)
+int resampler_init(struct resampler *state, int channels, int outfreq, int infreq, resampler_parameter op1, ...)
{
double beta = 16.0,
cutoff = 0.80,
@@ -240,7 +239,7 @@
}
-static int push(resampler_state const * const state, SAMPLE *pool, int * const poolfill, int * const offset, SAMPLE *dest, int dststep, SAMPLE const *source, int srcstep, size_t srclen)
+static int push(struct resampler const * const state, SAMPLE *pool, int * const poolfill, int * const offset, SAMPLE *dest, int dststep, SAMPLE const *source, int srcstep, size_t srclen)
{
SAMPLE * const destbase = dest,
*poolhead = pool + *poolfill,
@@ -319,13 +318,13 @@
}
-int resampler_push_max_input(resampler_state const * const state, size_t maxoutput)
+int resampler_push_max_input(struct resampler const * const state, size_t maxoutput)
{
return maxoutput * state->infreq / state->outfreq;
}
-int resampler_push_check(resampler_state const * const state, size_t srclen)
+int resampler_push_check(struct resampler const * const state, size_t srclen)
{
if (state->poolfill < state->taps)
srclen -= state->taps - state->poolfill;
@@ -334,7 +333,7 @@
}
-int resampler_push(resampler_state *state, SAMPLE **dstlist, SAMPLE const **srclist, size_t srclen)
+int resampler_push(struct resampler *state, SAMPLE **dstlist, SAMPLE const **srclist, size_t srclen)
{
int result = -1, poolfill = -1, offset = -1, i;
@@ -356,7 +355,7 @@
}
-int resampler_push_interleaved(resampler_state *state, SAMPLE *dest, SAMPLE const *source, size_t srclen)
+int resampler_push_interleaved(struct resampler *state, SAMPLE *dest, SAMPLE const *source, size_t srclen)
{
int result = -1, poolfill = -1, offset = -1, i;
@@ -378,7 +377,7 @@
}
-int resampler_drain(resampler_state *state, SAMPLE **dstlist)
+int resampler_drain(struct resampler *state, SAMPLE **dstlist)
{
SAMPLE *tail;
int result = -1, poolfill = -1, offset = -1, i;
@@ -405,7 +404,7 @@
}
-int resampler_drain_interleaved(resampler_state *state, SAMPLE *dest)
+int resampler_drain_interleaved(struct resampler *state, SAMPLE *dest)
{
SAMPLE *tail;
int result = -1, poolfill = -1, offset = -1, i;
@@ -432,7 +431,7 @@
}
-void resampler_clear(resampler_state *state)
+void resampler_clear(struct resampler *state)
{
assert(state);
assert(state->table);
@@ -443,3 +442,20 @@
memset(state, 0, sizeof(*state));
}
+
+int parse_resample(xmlNodePtr node, void *resample_ptr)
+{
+ struct cfg_tag resample_old_tags[] =
+ {
+ { "out-rate", get_xml_int, resample_ptr },
+ { NULL, NULL, NULL }
+ };
+ /* for backward compatability */
+ if (parse_xml_tags ("resample", node->xmlChildrenNode, resample_old_tags))
+ return -1;
+ if (*(unsigned*)resample_ptr == 0)
+ if (get_xml_int (node, resample_ptr))
+ return -1;
+ return 0;
+}
+
Modified: icecast/branches/kh/ices/src/resample.h
===================================================================
--- icecast/trunk/ices/src/resample.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/resample.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -20,9 +20,11 @@
#ifndef _RESAMPLE_H_INCLUDED
#define _RESAMPLE_H_INCLUDED
+#include <cfgparse.h>
+
typedef float SAMPLE;
-typedef struct
+struct resampler
{
unsigned int channels, infreq, outfreq, taps;
float *table;
@@ -31,7 +33,7 @@
/* dynamic bits */
int poolfill;
int offset;
-} resampler_state;
+};
typedef enum
{
@@ -42,7 +44,7 @@
RES_BETA /* (double)16.0 */
} resampler_parameter;
-int resampler_init(resampler_state *state, int channels, int outfreq, int infreq, resampler_parameter op1, ...);
+int resampler_init(struct resampler *state, int channels, int outfreq, int infreq, resampler_parameter op1, ...);
/*
* Configure *state to manage a data stream with the specified parameters. The
* string 'params' is currently unspecified, but will configure the parameters
@@ -58,7 +60,7 @@
*/
-int resampler_push_max_input(resampler_state const *state, size_t maxoutput);
+int resampler_push_max_input(struct resampler const *state, size_t maxoutput);
/*
* Returns the maximum number of input elements that may be provided without
* risk of flooding an output buffer of size maxoutput. maxoutput is
@@ -66,15 +68,15 @@
*/
-int resampler_push_check(resampler_state const *state, size_t srclen);
+int resampler_push_check(struct resampler const *state, size_t srclen);
/*
* Returns the number of elements that will be returned if the given srclen
* is used in the next call to resampler_push().
*/
-int resampler_push(resampler_state *state, SAMPLE **dstlist, SAMPLE const **srclist, size_t srclen);
-int resampler_push_interleaved(resampler_state *state, SAMPLE *dest, SAMPLE const *source, size_t srclen);
+int resampler_push(struct resampler *state, SAMPLE **dstlist, SAMPLE const **srclist, size_t srclen);
+int resampler_push_interleaved(struct resampler *state, SAMPLE *dest, SAMPLE const *source, size_t srclen);
/*
* Pushes srclen samples into the front end of the filter, and returns the
* number of resulting samples.
@@ -87,8 +89,8 @@
*/
-int resampler_drain(resampler_state *state, SAMPLE **dstlist);
-int resampler_drain_interleaved(resampler_state *state, SAMPLE *dest);
+int resampler_drain(struct resampler *state, SAMPLE **dstlist);
+int resampler_drain_interleaved(struct resampler *state, SAMPLE *dest);
/*
* Recover the remaining elements by flushing the internal pool with 0 values,
* and storing the resulting samples.
@@ -98,10 +100,13 @@
*/
-void resampler_clear(resampler_state *state);
+void resampler_clear(struct resampler *state);
/*
* Free allocated buffers, etc.
*/
+
+int parse_resample (xmlNodePtr node, void *resample_ptr);
+
#endif
Added: icecast/branches/kh/ices/src/runner.c
===================================================================
--- icecast/trunk/ices/src/runner.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/runner.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,435 @@
+/* runner.c
+ * runner processing. The process involved in by each runner
+ *
+ * Copyright (c) 2002-3 Karl Heyes <karl at xiph.org>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+#include <shout/shout.h>
+
+#include "cfgparse.h"
+#include "runner.h"
+#include "stream.h"
+#include "net/resolver.h"
+#include "signals.h"
+#include "thread/thread.h"
+#include "reencode.h"
+#include "encode.h"
+#include "audio.h"
+#include "inputmodule.h"
+
+#include <om_file.h>
+#include <om_shout.h>
+
+#define DEFAULT_STREAM_NAME "unnamed ices stream"
+#define DEFAULT_STREAM_GENRE "ices unset"
+#define DEFAULT_STREAM_DESCRIPTION "no description set"
+#define DEFAULT_QUALITY 3
+#define DEFAULT_DOWNMIX 0
+#define DEFAULT_RESAMPLE 0
+
+
+#define MODULE "stream/"
+#include "logging.h"
+
+#define MAX_ERRORS 10
+
+/*
+ * Close channel to next runner
+ */
+
+void runner_close (struct runner *run)
+{
+ if (run)
+ {
+ LOG_DEBUG1 ("Runner thread %d shutting down", run->id);
+ close (run->fd[1]);
+ run->fd[1] = -1;
+ thread_join (run->thread);
+ }
+}
+
+/*
+ * send the input buffer to the next runner or if none
+ * free the buffer.
+ *
+ * The queue does not do locking, the assumption is that
+ * there is always one on the queue, so the tail will never
+ * be referring to the head.
+ *
+ */
+
+int send_to_runner (struct runner *run, input_buffer *buffer)
+{
+ if (run)
+ {
+ buffer->next = NULL;
+
+#ifdef USE_PIPES
+ if (write (run->fd[1], &buffer, sizeof (buffer)) < (ssize_t)sizeof (buffer))
+ {
+ LOG_WARN0 ("unable to write to runner");
+ return -1;
+ }
+#else
+ *run->pending_tail = buffer;
+ run->pending_tail = &buffer->next;
+ thread_cond_signal (&run->data_available);
+#endif
+
+ return 0;
+ }
+ input_free_buffer (buffer);
+ return 0;
+}
+
+
+/* retreive input buffer from runner queue. Companion
+ * to previous function to remove from the queue, again
+ * make sure there is at least one on the queue.
+ */
+
+input_buffer *runner_wait_for_data(struct runner *run)
+{
+ input_buffer *ib;
+
+#ifdef USE_PIPES
+ if (read (run->fd[0], &ib, sizeof (ib)) < (ssize_t)sizeof (ib))
+ return NULL;
+#else
+ while (run->pending == NULL || (run->pending && run->pending->next == NULL))
+ {
+ thread_cond_wait (&run->data_available);
+ if (ices_config->shutdown)
+ return NULL;
+ }
+
+ ib = run->pending;
+ run->pending = ib->next;
+ ib->next = NULL;
+#endif
+
+ return ib;
+}
+
+
+void stream_cleanup (struct instance *stream)
+{
+
+ if (stream->ops && stream->ops->flush_data)
+ {
+ LOG_DEBUG1 ("Cleanup of stream %d required", stream->id);
+ stream->ops->flush_data (stream);
+ }
+ output_clear (&stream->output);
+}
+
+
+
+/* process a block of data. This may be skipped, or may even kick off
+ * a new connection.
+ *
+ */
+static void add_to_stream (struct instance *stream, input_buffer *ib)
+{
+ if (ib->critical)
+ process_critical (stream, ib);
+
+ /* LOG_DEBUG1 ("ops is %p", stream->ops); */
+ if (stream->ops && stream->ops->process_buffer(stream, ib) < 0)
+ stream_cleanup (stream);
+
+ if (ib->type == ICES_INPUT_NONE)
+ return;
+ /* the normal end of stream flush */
+ if (ib->eos && stream->ops)
+ {
+ if (stream->ops->flush_data)
+ {
+ LOG_DEBUG1("stream flushed due to EOS [%d]", stream->id);
+ stream->ops->flush_data (stream);
+ }
+ /* EOS seen and handled so disable further processing until
+ * another start of stream is sent. */
+ stream->ops = NULL;
+ }
+
+ return;
+}
+
+static struct instance *_allocate_instance (void)
+{
+ struct instance *instance = (struct instance *)calloc(1, sizeof(struct instance));
+ static int id = 1;
+
+ if (instance == NULL)
+ return NULL;
+
+ instance->resampleoutrate = DEFAULT_RESAMPLE;
+ instance->passthru = 0;
+
+ instance->encode_settings.quality = DEFAULT_QUALITY;
+ instance->downmix = DEFAULT_DOWNMIX;
+
+ instance->id = id++;
+ instance->next = NULL;
+
+ return instance;
+}
+
+
+
+int parse_instance (xmlNodePtr node, void *arg)
+{
+ struct runner *run = arg;
+ struct instance *instance = _allocate_instance();
+
+ while (instance)
+ {
+ struct cfg_tag instance_tags[] =
+ {
+ { "name", get_xml_string, &instance->output.name },
+ { "genre", get_xml_string, &instance->output.genre },
+ { "description", get_xml_string, &instance->output.description },
+ { "url", get_xml_string, &instance->output.url },
+ { "downmix", get_xml_bool, &instance->downmix },
+ { "passthru", get_xml_bool, &instance->passthru},
+ { "passthrough", get_xml_bool, &instance->passthru},
+ { "resample", parse_resample, &instance->resampleoutrate },
+ { "encode", parse_encode, &instance->encode_settings },
+ { "savestream", parse_savefile, &instance->output },
+ { "savefile", parse_savefile, &instance->output },
+ { "shout", parse_shout, &instance->output },
+ { NULL, NULL, NULL }
+ };
+
+ /* config should be derived from runner */
+ xmlMemoryDump();
+ if (ices_config->stream_name)
+ instance->output.name = xmlStrdup (ices_config->stream_name);
+ if (ices_config->stream_genre)
+ instance->output.genre = xmlStrdup (ices_config->stream_genre);
+ if (ices_config->stream_description)
+ instance->output.description = xmlStrdup (ices_config->stream_description);
+ if (ices_config->stream_url)
+ instance->output.url = xmlStrdup (ices_config->stream_url);
+
+ if (parse_xml_tags ("instance", node->xmlChildrenNode, instance_tags))
+ break;
+
+ if (run->instances == NULL)
+ run->instances = instance;
+ else
+ {
+ struct instance *i = run->instances;
+ while (i->next != NULL) i = i->next;
+ i->next = instance;
+
+ }
+ return 0;
+ }
+ free (instance);
+
+ return -1;
+}
+
+
+
+void *ices_runner (void *arg)
+{
+ struct runner *run = arg;
+ struct instance *current;
+
+#ifdef HAVE_SCHED_GET_PRIORITY_MAX
+ int policy;
+ struct sched_param param;
+
+ pthread_getschedparam (pthread_self(), &policy, ¶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/kh/ices/src/runner.h
===================================================================
--- icecast/trunk/ices/src/runner.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/runner.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -0,0 +1,77 @@
+/* stream.h
+ * - Core streaming functions/main loop.
+ *
+ * $Id: stream.h,v 1.2 2001/09/25 12:04:22 msmith Exp $
+ *
+ * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ *
+ * This program is distributed under the terms of the GNU General
+ * Public License, version 2. You may use, modify, and redistribute
+ * it under the terms of this license. A copy should be included
+ * with this source.
+ */
+
+
+#ifndef __RUNNER_H
+#define __RUNNER_H
+
+#include "thread/thread.h"
+#include "inputmodule.h"
+#include "cfgparse.h"
+#include "encode.h"
+
+#define MAX_QUEUE_NODE_BUFFER 16*1024
+typedef struct _runner_tag runner;
+typedef struct _instance_tag instance_t;
+
+struct instance
+{
+ input_type type;
+ int id;
+
+ struct encoder_settings encode_settings;
+
+ struct codec_ops *ops;
+
+ int downmix;
+ int passthru;
+
+ struct output_state output;
+
+ float quality;
+ int channels;
+
+ int resampleoutrate;
+
+ struct instance *next;
+};
+
+
+struct runner
+{
+#ifdef USE_PIPES
+ int fd [2];
+#else
+ input_buffer *pending, **pending_tail;
+ cond_t data_available;
+#endif
+ int id;
+ struct instance *instances;
+ thread_type *thread;
+ int not_running;
+
+ struct runner *next;
+};
+
+
+void *ices_runner (void *arg);
+int send_to_runner (struct runner *run, input_buffer *buffer);
+void runner_close (struct runner *run);
+void stream_cleanup(struct instance *stream);
+int create_runner_thread (struct runner *run);
+int parse_runner (xmlNodePtr node, void *arg);
+struct runner *config_free_runner(struct runner *run);
+void start_runners();
+
+#endif
+
Modified: icecast/branches/kh/ices/src/signals.c
===================================================================
--- icecast/trunk/ices/src/signals.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/signals.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* signals.c
* - signal handling/setup
*
- * $Id: signals.c,v 1.8 2003/03/28 01:07:37 karl Exp $
+ * $Id: signals.c,v 1.4 2001/09/25 12:04:22 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -12,67 +12,66 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
-#include <thread/thread.h>
+#include "thread/thread.h"
#include "cfgparse.h"
#include "stream.h"
-#include "input.h"
#include "inputmodule.h"
-#include "event.h"
#define MODULE "signals/"
#include "logging.h"
extern volatile int metadata_update_signalled;
+int move_to_next_input;
-void signal_usr1_handler(int signum)
+void signal_usr1_handler(int signum __attribute__((unused)))
{
- LOG_INFO0("Metadata update requested");
+ /* LOG_INFO0("Metadata update requested"); */
metadata_update_signalled = 1;
- thread_cond_broadcast(&ices_config->event_pending_cond);
signal(SIGUSR1, signal_usr1_handler);
}
-void signal_hup_handler(int signum)
+void signal_usr2_handler(int signum __attribute__((unused)))
{
+ /* LOG_INFO0("Switch to next input stream requested"); */
+
+ move_to_next_input = 1;
+
+ signal(SIGUSR2, signal_usr2_handler);
+}
+
+void signal_hup_handler(int signum __attribute__((unused)))
+{
LOG_INFO0("Flushing logs");
log_flush(ices_config->log_id);
- /* Now, let's tell it to move to the next track */
- ices_config->inmod->handle_event(ices_config->inmod,EVENT_NEXTTRACK,NULL);
-
+ ices_config->next_track = 1;
signal(SIGHUP, signal_hup_handler);
}
-void signal_int_handler(int signum)
+void signal_int_handler(int signum __attribute__((unused)))
{
- /* Is a mutex needed here? Probably */
- if (!ices_config->shutdown)
- {
- LOG_INFO0("Shutdown requested...");
- ices_config->shutdown = 1;
- thread_cond_broadcast(&ices_config->queue_cond);
-
- /* If user gives a second sigint, just die. */
- signal(SIGINT, SIG_DFL);
- }
+ /* LOG_INFO0("Shutdown requested..."); */
+ ices_config->shutdown = 1;
+ signal(SIGINT, signal_int_handler);
}
void signals_setup(void)
{
- signal(SIGHUP, signal_hup_handler);
- signal(SIGINT, signal_int_handler);
- signal(SIGUSR1, signal_usr1_handler);
- signal(SIGPIPE, SIG_IGN);
+ signal(SIGINT, signal_int_handler);
+ signal(SIGTERM, signal_int_handler);
+ signal(SIGUSR1, signal_usr1_handler);
+ signal(SIGUSR2, signal_usr2_handler);
+ signal(SIGPIPE, SIG_IGN);
}
Modified: icecast/branches/kh/ices/src/signals.h
===================================================================
--- icecast/trunk/ices/src/signals.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/signals.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -16,6 +16,9 @@
#include <signal.h>
+extern int move_to_next_input;
+
+
void signal_hup_handler(int signum);
void signal_int_handler(int signum);
Modified: icecast/branches/kh/ices/src/stream.c
===================================================================
--- icecast/trunk/ices/src/stream.c 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/stream.c 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,10 +1,9 @@
-/* stream.c
- * - Core streaming functions/main loop.
+/* stream_shared.c
+ * - Stream utility functions.
*
- * $Id: stream.c,v 1.33 2004/03/11 17:22:59 karl Exp $
+ * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
+ * Copyright (c) 2002-4 Karl Heyes <karl at xiph.org>
*
- * Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
- *
* This program is distributed under the terms of the GNU General
* Public License, version 2. You may use, modify, and redistribute
* it under the terms of this license. A copy should be included
@@ -12,348 +11,469 @@
*/
#ifdef HAVE_CONFIG_H
- #include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <errno.h>
-#include <pthread.h>
+#include <sys/uio.h>
#include <unistd.h>
+#include <string.h>
+#include <limits.h>
-#include <shout/shout.h>
+#include <thread/thread.h>
#include "cfgparse.h"
-#include "input.h"
-#include "im_playlist.h"
-#include "signals.h"
-#include "thread/thread.h"
+#include "inputmodule.h"
+#include "stream.h"
#include "reencode.h"
#include "encode.h"
#include "audio.h"
-#include "inputmodule.h"
-#include "stream_shared.h"
-#include "stream.h"
+#include "metadata.h"
-#include <ogg/ogg.h>
-#include <vorbis/codec.h>
-
#define MODULE "stream/"
#include "logging.h"
-#define MAX_ERRORS 10
-/* The main loop for each instance. Gets data passed to it from the stream
- * manager (which gets it from the input module), and streams it to the
- * specified server
- */
-void *ices_instance_stream(void *arg)
+struct pcm2vorbis_encode
{
- int ret, shouterr;
- ref_buffer *buffer;
- stream_description *sdsc = arg;
- instance_t *stream = sdsc->stream;
- input_module_t *inmod = sdsc->input;
- int reencoding = (inmod->type == ICES_INPUT_VORBIS) && stream->encode;
- int encoding = (inmod->type == ICES_INPUT_PCM) && stream->encode;
- char *stream_name = NULL, *stream_genre = NULL, *stream_description = NULL;
- char *stream_url = NULL, *user = NULL;
- char audio_info[11];
-
- vorbis_comment_init(&sdsc->vc);
+ struct encoder *enc;
+ struct resample *resamp;
+ struct downmix *downmix;
+};
- sdsc->shout = shout_new();
- /* we only support the ice protocol and vorbis streams currently */
- shout_set_format(sdsc->shout, SHOUT_FORMAT_VORBIS);
- shout_set_protocol(sdsc->shout, SHOUT_PROTOCOL_HTTP);
+void output_clear(struct output_state *state)
+{
+ if (state == NULL)
+ return;
+ /* clear comment and info */
+ if (state->info_in_use)
+ {
+ struct output_module *mod, *next;
+ LOG_DEBUG0("Clearing up output state");
+ vorbis_comment_clear (&state->vc);
+ vorbis_info_clear (&state->vi);
- signal(SIGPIPE, signal_hup_handler);
+ /* clear stored headers */
+ free (state->packets[0].packet);
+ /* ogg_packet_clear (&state->packets[1]); */
+ free (state->packets[1].packet);
+ free (state->packets[2].packet);
- if (!(shout_set_host(sdsc->shout, stream->hostname)) == SHOUTERR_SUCCESS) {
- LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
- stream->died = 1;
- return NULL;
+ /* should clear individual outputs */
+ mod = state->head;
+ while (mod)
+ {
+ next = mod->next;
+ /* clear each output module */
+ mod->output_clear (mod);
+ free (mod);
+ mod = next;
+ }
+ state->head = NULL;
+ if (state->url)
+ {
+ xmlFree (state->url);
+ state->url = NULL;
+ }
+ if (state->name)
+ {
+ xmlFree (state->name);
+ state->name = NULL;
+ }
+ if (state->description)
+ {
+ xmlFree (state->description);
+ state->description = NULL;
+ }
+ if (state->genre)
+ {
+ xmlFree (state->genre);
+ state->genre = NULL;
+ }
+ state->info_in_use = 0;
}
+}
- shout_set_port(sdsc->shout, stream->port);
- if (!(shout_set_password(sdsc->shout, stream->password)) == SHOUTERR_SUCCESS) {
- LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
- stream->died = 1;
- return NULL;
- }
- if (stream->user)
- user = stream->user;
- else
- user = "source";
- if(shout_set_user(sdsc->shout, user) != SHOUTERR_SUCCESS) {
- LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
- stream->died = 1;
- return NULL;
+static int _output_oggpacket (struct instance *stream, ogg_packet *op, unsigned samples)
+{
+ struct output_state *state = &stream->output;
+ struct output_module *mod;
+
+ if (op->b_o_s)
+ {
+ LOG_DEBUG0 ("seen new stream, better get headers");
+ state->headers = 3;
}
+ if (op->e_o_s) LOG_DEBUG0 ("packet marked with EOS seen");
- if (!(shout_set_agent(sdsc->shout, PACKAGE_STRING)) == SHOUTERR_SUCCESS) {
- LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
- stream->died = 1;
- return NULL;
+ /* need to store vorbis headers */
+ if (state->headers)
+ {
+ ogg_packet *header;
+ void *ptr;
+
+#ifdef DEBUG
+ LOG_DEBUG1 ("header packet has packetno of %lld", op->packetno);
+#endif
+ if (state->headers == 3)
+ {
+ /* LOG_DEBUG1 ("intial header packet %d", stream->id); */
+ if (state->info_in_use)
+ {
+ LOG_DEBUG0 ("Clearing output info/comment settings");
+ vorbis_comment_clear (&state->vc);
+ vorbis_info_clear (&state->vi);
+ }
+ /* LOG_DEBUG0("state vi/vc initialised"); */
+ vorbis_info_init (&state->vi);
+ vorbis_comment_init (&state->vc);
+ state->info_in_use = 1;
+ }
+ /* LOG_DEBUG0("state vi/vc headerin"); */
+ vorbis_synthesis_headerin (&state->vi, &state->vc, op);
+
+ state->headers--;
+ header = &state->packets [2-state->headers];
+
+ switch (state->headers)
+ {
+#if 0
+ /* vorbis_commentheader_out leaks a small amount of mem in v1.0 */
+ case 1:
+ LOG_DEBUG0("processing comment header");
+ if (header->packet)
+ {
+ LOG_DEBUG0("clearing old header");
+ ogg_packet_clear (header);
+ header->packet = NULL;
+ }
+ vorbis_comment_add (&state->vc, "EncodedBy=" PACKAGE_STRING);
+ vorbis_commentheader_out (&state->vc, header);
+ break;
+#endif
+
+ case 0:
+ LOG_DEBUG2 ("samplerate is %d, channels is %d", state->vi.rate, state->vi.channels);
+ state->new_headers = 1;
+ state->start_pos = 0;
+ /* fall thru we need to store all 3 headers */
+ case 1:
+ case 2:
+ /* LOG_DEBUG1("header count is %d", state->headers); */
+ if (header->packet)
+ {
+ free (header->packet);
+ header->packet = NULL;
+ }
+ ptr = malloc (op->bytes);
+ memcpy (header, op, sizeof (*header));
+ memcpy (ptr, op->packet, op->bytes);
+ header->packet = ptr;
+ header->granulepos = 0;
+ break;
+
+ default:
+ LOG_ERROR1("headers expected value is unexpected %d", state->headers);
+ break;
+ }
+ return 0;
}
+ /* LOG_DEBUG1("granulepos is %lld", op->granulepos); */
+ /* printf("granulepos is %lld\n", op->granulepos); */
- if (!(shout_set_mount(sdsc->shout, stream->mount)) == SHOUTERR_SUCCESS) {
- LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
- stream->died = 1;
- return NULL;
- }
- if (shout_set_public (sdsc->shout, stream->public_stream & 1) != SHOUTERR_SUCCESS)
+ mod = state->head;
+ while (mod)
{
- LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
- stream->died = 1;
- return NULL;
+ mod->output_send (mod, op, samples);
+ mod = mod->next;
}
- /* set the metadata for the stream */
- if(stream->stream_name)
- stream_name = stream->stream_name;
- else if (ices_config->stream_name)
- stream_name = ices_config->stream_name;
+ state->new_headers = 0;
- if(stream->stream_description)
- stream_description = stream->stream_description;
- else if (ices_config->stream_description)
- stream_description = ices_config->stream_description;
+ return 0;
+}
- if(stream->stream_genre)
- stream_genre = stream->stream_genre;
- else if (ices_config->stream_genre)
- stream_genre = ices_config->stream_genre;
- if(stream->stream_url)
- stream_url = stream->stream_url;
- else if (ices_config->stream_url)
- stream_url = ices_config->stream_url;
+static void flush_vorbis_input (struct instance *stream)
+{
+ LOG_DEBUG1("Flushing ogg packets on %d", stream->id);
+ reencode_free (stream->ops->data);
+ free (stream->ops);
+ stream->ops = NULL;
+}
- if(stream_name)
- if (!(shout_set_name(sdsc->shout, stream_name)) == SHOUTERR_SUCCESS) {
- LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
- stream->died = 1;
- return NULL;
- }
- if (stream_genre)
- if (!(shout_set_genre(sdsc->shout, stream_genre)) == SHOUTERR_SUCCESS) {
- LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
- stream->died = 1;
- return NULL;
- }
- if (stream_description)
- if (!(shout_set_description(sdsc->shout, stream_description)) == SHOUTERR_SUCCESS) {
- LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
- stream->died = 1;
- return NULL;
- }
- if (stream_url)
- if (!(shout_set_url(sdsc->shout, stream_url) == SHOUTERR_SUCCESS)) {
- LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout));
- stream->died = 1;
- return NULL;
- }
- if(stream->downmix && encoding && stream->channels == 1) {
- stream->channels = 1;
- sdsc->downmix = downmix_initialise();
- }
- if(stream->resampleinrate && stream->resampleoutrate && encoding) {
- stream->samplerate = stream->resampleoutrate;
- sdsc->resamp = resample_initialise(stream->channels,
- stream->resampleinrate, stream->resampleoutrate);
+void flush_ogg_packets (struct instance *stream)
+{
+ int send_it = 1;
+ ogg_packet op;
+ struct pcm2vorbis_encode *s = stream->ops->data;
+
+ if (encode_endstream(s->enc))
+ {
+ LOG_DEBUG1("Flushing out encoded ogg packets stream %d", stream->id);
+ while (encode_packetout (s->enc, &op) > 0)
+ {
+ if (send_it && _output_oggpacket (stream, &op, 0) < 0)
+ send_it = 0;
+ }
}
+ encode_free (s->enc);
+ downmix_clear (s->downmix);
+ resample_clear (s->resamp);
+ free (s);
+ stream->ops->data = NULL;
+}
- /* max integer is 10 bytes + 1 for null term */
- snprintf(audio_info, sizeof(audio_info), "%d", stream->samplerate);
- shout_set_audio_info(sdsc->shout, SHOUT_AI_SAMPLERATE, audio_info);
- snprintf(audio_info, sizeof(audio_info), "%d", stream->channels);
- shout_set_audio_info(sdsc->shout, SHOUT_AI_CHANNELS, audio_info);
- if (stream->managed)
+
+static int encode_pcm (struct instance *stream, input_buffer *buffer)
+{
+ ogg_packet op;
+ int ret = 0;
+ struct pcm2vorbis_encode *s = stream->ops->data;
+
+ if (buffer->samples == 0)
+ return 0;
+ if (stream->downmix)
{
- snprintf(audio_info, sizeof(audio_info), "%d", stream->nom_br/1000);
- shout_set_audio_info(sdsc->shout, SHOUT_AI_BITRATE, audio_info);
+ downmix_buffer_float (s->downmix, buffer->buf, buffer->samples, buffer->channels);
+ if (s->resamp)
+ {
+ resample_buffer_float (s->resamp, &s->downmix->buffer, buffer->samples);
+ encode_data_float (s->enc, s->resamp->buffers, s->resamp->buffill);
+ }
+ else
+ encode_data_float (s->enc, &s->downmix->buffer, buffer->samples);
}
+ else if (s->resamp)
+ {
+ resample_buffer_float (s->resamp, buffer->buf, buffer->samples);
+ encode_data_float (s->enc, s->resamp->buffers, s->resamp->buffill);
+ }
else
{
- snprintf(audio_info, sizeof(audio_info), "%2.2f", stream->quality);
- shout_set_audio_info(sdsc->shout, SHOUT_AI_QUALITY, audio_info);
+ encode_data_float (s->enc, buffer->buf, buffer->samples);
}
-
- if(encoding)
+ while (encode_packetout (s->enc, &op) > 0)
{
- if(inmod->metadata_update)
- inmod->metadata_update(inmod->internal, &sdsc->vc);
- sdsc->enc = encode_initialise(stream->channels, stream->samplerate,
- stream->managed, stream->min_br, stream->nom_br, stream->max_br,
- stream->quality, &sdsc->vc);
- if(!sdsc->enc) {
- LOG_ERROR0("Failed to configure encoder");
- stream->died = 1;
- return NULL; /* FIXME: probably leaking some memory here */
+ if (ret == 0 && _output_oggpacket (stream, &op, 0) < 0)
+ {
+ ret = -1;
+ break;
}
}
- else if(reencoding)
- sdsc->reenc = reencode_init(stream);
+ return ret;
+}
- if(stream->savefilename != NULL)
- {
- stream->savefile = fopen(stream->savefilename, "wb");
- if(!stream->savefile)
- LOG_ERROR2("Failed to open stream save file %s: %s",
- stream->savefilename, strerror(errno));
- else
- LOG_INFO1("Saving stream to file %s", stream->savefilename);
- }
- if((shouterr = shout_open(sdsc->shout)) == SHOUTERR_SUCCESS)
+static int process_encode_init (struct instance *stream, input_buffer *buffer)
+{
+ unsigned samplerate;
+ int channels;
+ ogg_packet op;
+ struct pcm2vorbis_encode *s;
+ struct encoder *enc = encode_create();
+
+ do
{
- LOG_INFO3("Connected to server: %s:%d%s",
- shout_get_host(sdsc->shout), shout_get_port(sdsc->shout), shout_get_mount(sdsc->shout));
+ struct codec_ops *ops = stream->ops;
- while(1)
+ if (enc == NULL)
+ break;
+ s = calloc (1, sizeof (struct pcm2vorbis_encode));
+ if (s == NULL)
+ break;
+ s->enc = enc;
+ LOG_INFO1 ("Restarting encoder for PCM input on stream %d", stream->id);
+ samplerate = buffer->samplerate;
+
+ if (stream->resampleoutrate)
+ samplerate = stream->resampleoutrate;
+
+ channels = buffer->channels;
+ if (stream->downmix && channels == 2)
{
- if(stream->buffer_failures > MAX_ERRORS)
- {
- LOG_WARN0("Too many errors, shutting down");
+ s->downmix = downmix_initialise();
+ if (s->downmix == NULL)
break;
- }
+ channels = 1;
+ }
- buffer = stream_wait_for_data(stream);
-
- /* buffer being NULL means that either a fatal error occured,
- * or we've been told to shut down
- */
- if(!buffer)
+ if (buffer->samplerate != samplerate)
+ {
+ s->resamp = resample_initialise (channels, buffer->samplerate, samplerate);
+ if (s->resamp == NULL)
break;
+ }
- /* If data is NULL or length is 0, we should just skip this one.
- * Probably, we've been signalled to shut down, and that'll be
- * caught next iteration. Add to the error count just in case,
- * so that we eventually break out anyway
- */
- if(!buffer->buf || !buffer->len)
+ if (buffer->metadata)
+ {
+ char **md = buffer->metadata;
+ while(*md)
{
- LOG_WARN0("Bad buffer dequeued!");
- stream->buffer_failures++;
- continue;
+ LOG_INFO1 ("Adding comment %s", *md);
+ encode_comment (enc, *md++);
}
+ }
- if(stream->wait_for_critical)
- {
- LOG_INFO0("Trying restart on new substream");
- stream->wait_for_critical = 0;
- }
+ stream->encode_settings.encode_rate = samplerate;
+ stream->encode_settings.encode_channels = channels;
+ if (encode_setup (enc, &stream->encode_settings) < 0)
+ {
+ LOG_ERROR1("[%d] Failed to configure encoder", stream->id);
+ break;
+ }
+ while (encode_packetout (enc, &op) > 0)
+ {
+ _output_oggpacket (stream, &op, 0);
+ }
+ ops->data = s;
+ ops->flush_data = flush_ogg_packets;
+ ops->process_buffer = encode_pcm;
- ret = process_and_send_buffer(sdsc, buffer);
+ return 0;
+ } while (0);
- /* No data produced, do nothing */
- if(ret == -1)
- ;
- /* Fatal error */
- else if(ret == -2)
- {
- LOG_ERROR0("Serious error, waiting to restart on "
- "next substream. Stream temporarily suspended.");
- /* Set to wait until a critical buffer comes through (start of
- * a new substream, typically), and flush existing queue.
- */
- thread_mutex_lock(&ices_config->flush_lock);
- stream->wait_for_critical = 1;
- input_flush_queue(stream->queue, 0);
- thread_mutex_unlock(&ices_config->flush_lock);
- }
- /* Non-fatal shout error */
- else if(ret == 0)
- {
- LOG_ERROR2("Send error: %s (%s)",
- shout_get_error(sdsc->shout), strerror(errno));
- if(shout_get_errno(sdsc->shout) == SHOUTERR_SOCKET)
- {
- int i=0;
+ if (enc) encode_free (enc);
+ if (s)
+ {
+ if (s->resamp) resample_clear (s->resamp);
+ if (s->downmix) downmix_clear (s->downmix);
+ free (s);
+ }
+ LOG_ERROR0("Encoder failed");
+ return -1;
+}
- /* While we're trying to reconnect, don't receive data
- * to this instance, or we'll overflow once reconnect
- * succeeds
- */
- thread_mutex_lock(&ices_config->flush_lock);
- stream->skip = 1;
- /* Also, flush the current queue */
- input_flush_queue(stream->queue, 1);
- thread_mutex_unlock(&ices_config->flush_lock);
-
- while((i < stream->reconnect_attempts ||
- stream->reconnect_attempts==-1) &&
- !ices_config->shutdown)
- {
- i++;
- LOG_WARN0("Trying reconnect after server socket error");
- shout_close(sdsc->shout);
- if((shouterr = shout_open(sdsc->shout)) == SHOUTERR_SUCCESS)
- {
- LOG_INFO3("Connected to server: %s:%d%s",
- shout_get_host(sdsc->shout), shout_get_port(sdsc->shout),
- shout_get_mount(sdsc->shout));
- /* This stream can't restart until the next
- * logical stream comes along, since the
- * server won't have any cached headers for
- * this source/connection. So, don't continue
- * yet.
- */
- thread_mutex_lock(&ices_config->flush_lock);
- stream->wait_for_critical = 1;
- input_flush_queue(stream->queue, 0);
- thread_mutex_unlock(&ices_config->flush_lock);
- break;
- }
- else
- {
- LOG_ERROR3("Failed to reconnect to %s:%d (%s)",
- shout_get_host(sdsc->shout),shout_get_port(sdsc->shout),
- shout_get_error(sdsc->shout));
- if(i==stream->reconnect_attempts)
- {
- LOG_ERROR0("Reconnect failed too many times, "
- "giving up.");
- /* We want to die now */
- stream->buffer_failures = MAX_ERRORS+1;
- }
- else /* Don't try again too soon */
- thread_sleep (stream->reconnect_delay*1000000);
- }
- }
- stream->skip = 0;
- }
- stream->buffer_failures++;
+
+static int reencode_vorbis_packet (struct instance *stream, input_buffer *buffer)
+{
+ ogg_packet *packet = (ogg_packet *)buffer->buf;
+ int ret = 0;
+
+ if (reencode_packetin (stream->ops->data, packet) < 0)
+ {
+ LOG_ERROR1("[%d] Fatal reencoding error encountered", stream->id);
+ return -1;
+ }
+ while (1)
+ {
+ ogg_packet reencoded_packet;
+
+ if ((ret = reencode_packetout (stream->ops->data, &reencoded_packet)) > 0)
+ {
+ _output_oggpacket (stream, &reencoded_packet, 0);
+ }
+ else
+ {
+ if (ret < 0)
+ {
+ LOG_ERROR1("failed getting packet from re-encoder [%d]", stream->id);
+ return -1;
}
- stream_release_buffer(buffer);
+ else
+ break;
}
}
+ return ret;
+}
+
+
+static int process_ogg_init (struct instance *stream, input_buffer *buffer)
+{
+ if (stream->passthru)
+ stream->ops = &passthru_ptks_ops;
else
{
- LOG_ERROR4("Failed initial connect to %s:%d (%s: %s)",
- shout_get_host(sdsc->shout),shout_get_port(sdsc->shout), shout_get_error(sdsc->shout), strerror(errno));
+ struct codec_ops *ops = stream->ops;
+
+ if (stream->resampleoutrate)
+ stream->encode_settings.samplerate = stream->resampleoutrate;
+ if (stream->downmix)
+ stream->encode_settings.channels = 1;
+
+ if ((ops->data = reencode_new_init (&stream->encode_settings)) == NULL)
+ {
+ LOG_ERROR1("failed intialising re-encoder [%d]", stream->id);
+ return -1;
+ }
+ ops->flush_data = flush_vorbis_input;
+ ops->process_buffer = reencode_vorbis_packet;
}
-
- shout_close(sdsc->shout);
+ return 0;
+}
- if(stream->savefile != NULL)
- fclose(stream->savefile);
- shout_free(sdsc->shout);
- encode_clear(sdsc->enc);
- reencode_clear(sdsc->reenc);
- downmix_clear(sdsc->downmix);
- resample_clear(sdsc->resamp);
- vorbis_comment_clear(&sdsc->vc);
+static int send_vorbis_packet (struct instance *stream, input_buffer *buffer)
+{
+ ogg_packet *packet = (ogg_packet *)buffer->buf;
- stream->died = 1;
- return NULL;
+ if (_output_oggpacket (stream, packet, buffer->samples) < 0)
+ return -1;
+
+ return 0;
}
+
+/*
+ * Process a critical buffer of data
+ */
+void process_critical (struct instance *stream, input_buffer *buffer)
+{
+ struct codec_ops *ops;
+ int ret = 0;
+
+ if (stream->ops && stream->ops->flush_data)
+ {
+ LOG_DEBUG0("Stream has restarted but no EOS of previous seen");
+ stream->ops->flush_data (stream);
+ free (stream->ops);
+ stream->ops = NULL;
+ }
+
+ ops = malloc (sizeof (struct codec_ops));
+ if (ops == NULL)
+ {
+ LOG_DEBUG1("stream %d - codec ops allocation error", stream->id);
+ return;
+ }
+ stream->ops = ops;
+
+ switch (buffer->type)
+ {
+ case ICES_INPUT_VORBIS_PACKET:
+ ret = process_ogg_init (stream, buffer);
+ break;
+
+ case ICES_INPUT_PCM:
+ ret = process_encode_init (stream, buffer);
+ break;
+
+ default:
+ LOG_ERROR2 ("[%d] No known buffer type %d", stream->id, buffer->type);
+ break;
+ }
+ if (ret < 0) /* failed initialiser */
+ {
+ free (stream->ops);
+ stream->ops = NULL;
+ }
+}
+
+struct codec_ops passthru_ptks_ops =
+{
+ NULL,
+ send_vorbis_packet,
+ NULL
+};
+
Modified: icecast/branches/kh/ices/src/stream.h
===================================================================
--- icecast/trunk/ices/src/stream.h 2004-07-04 11:20:52 UTC (rev 6976)
+++ icecast/branches/kh/ices/src/stream.h 2004-07-11 18:40:08 UTC (rev 7098)
@@ -1,7 +1,7 @@
/* stream.h
* - Core streaming functions/main loop.
*
- * $Id: stream.h,v 1.4 2003/03/22 01:14:35 karl Exp $
+ * $Id: stream_shared.h,v 1.3 2001/09/25 12:04:22 msmith Exp $
*
* Copyright (c) 2001 Michael Smith <msmith at labyrinth.net.au>
*
@@ -11,36 +11,29 @@
* with this source.
*/
-
#ifndef __STREAM_H
#define __STREAM_H
-#include <shout/shout.h>
+#include "runner.h"
-#include "thread/thread.h"
-#include "cfgparse.h"
+struct codec_ops
+{
+ void *data;
+ int (*process_buffer)(struct instance *stream, input_buffer *buffer);
+ void (*flush_data)(struct instance *stream);
+};
-typedef struct {
- unsigned char *buf;
- long len;
- int count;
- int critical;
- long aux_data;
-} ref_buffer;
+extern struct codec_ops passthru_ptks_ops;
+extern struct codec_ops no_encoding_ops;
+extern struct codec_ops reencode_ops;
+extern struct codec_ops encode_ops;
+extern struct codec_ops reencode_new_ops;
+extern struct codec_ops reencode_packet_ops;
-typedef struct _queue_item {
- ref_buffer *buf;
- struct _queue_item *next;
-} queue_item;
-typedef struct buffer_queue {
- queue_item *head, *tail;
- int length;
- mutex_t lock;
-} buffer_queue;
+void process_critical (struct instance *stream, input_buffer *buffer);
+void output_clear(struct output_state *state);
-void *ices_instance_stream(void *arg);
-void *savefile_stream(void *arg);
-
#endif
+
More information about the commits
mailing list