StreamFeed
Feeds a stream source from audio.fromStream or audio.fromProcess. It opens a libsymphonia stream session, runs
the pump, a thread that reads the stream's bytes and pushes them into the session, and keeps the read-ahead: a queue
of decoded audio of up to 6 seconds, as 48 kHz stereo 32-bit float blocks before any volume, or as Opus packets as
stored. The Source that owns it takes audio from the queue, decides when the stream is starved, and surfaces its
failures.
A failure the feed meets is kept as pendingFailure, and the source raises it once the audio queued before it has
played. The pump stops at a failure, at the end of the input, when the session can take no more bytes, and at
destroy, which never waits for it.
For fromProcess, the feed spawns the program on the first poll and reads its stdout. Once stdout ends it waits
up to 5 seconds for the exit code; a non-zero code wins over every other outcome, with the last line of stderr as
its reason, and a program that does not exit is killed, with a warning. A program is killed only while its stdout is
still open, or at destroy, and a code that follows its own kill is ignored. A failure met while the pump waits on
stdout kills the program at once, so the failure does not wait for the program to write or exit. Until the exit is
known the stream has not ended.
An MP4 with its index at the end, or a CAF, cannot be read from the front: the session fails before it opens. The
feed keeps every byte until the session opens, up to 128 MiB, and on that failure, when the stream starts with an
MP4 (ftyp at byte 4) or CAF (caff at byte 0) marker, reads the rest of the stream, opens the whole file with
Symphonia.Decoder.fromBuffer and plays it through the same queue. Peak memory is up to about 4 times the file.
Summary
Properties
StreamFeed.decoder :: symphonia.Decoder?StreamFeed.pump :: thread?StreamFeed.stopping :: booleanStreamFeed.producerEnded :: booleanStreamFeed.bufferedSeconds :: numberStreamFeed.pendingFailure :: string?StreamFeed.command :: string?StreamFeed.arguments :: { string }StreamFeed.process :: Process?StreamFeed.exitCode :: number?StreamFeed.failedAtMillis :: number?StreamFeed.exitKnown :: booleanStreamFeed.killedByUs :: booleanStreamFeed.fallbackEnabled :: booleanStreamFeed.fallingBack :: booleanStreamFeed.retained :: buffer?StreamFeed.retainedBytes :: numberStreamFeed.maxRetainedBytes :: numberStreamFeed.firstBytes :: stringStreamFeed.outputClosed :: booleanStreamFeed.started :: booleanStreamFeed.pipeReading :: booleanStreamFeed.pipeDone :: booleanStreamFeed.pipeData :: buffer?StreamFeed.pipeFailure :: string?StreamFeed.pipeWaiter :: thread?StreamFeed.reader :: StreamReader?StreamFeed.logLevel :: logger.LogLevel?StreamFeed.queue :: { buffer }StreamFeed.queueOffset :: numberStreamFeed.bufferedFrames :: numberStreamFeed.streamBytes :: numberStreamFeed.logger :: logger.LoggerMethods
StreamFeed:poll()StreamFeed:isOpen() → booleanStreamFeed:isOpus() → booleanStreamFeed:outputDone() → booleanStreamFeed:openDecided() → booleanStreamFeed:exitSettled() → booleanStreamFeed:hasEnded() → booleanStreamFeed:hasQueued() → booleanStreamFeed:takeFloats(maxFrames: number) → buffer?StreamFeed:takePacket() → buffer?StreamFeed:fill()StreamFeed:refresh()StreamFeed:destroy()Properties
Section titled “Properties”decoder
Section titled “decoder”The stream session from the first poll, or after the whole-file fallback the decoder of the whole file; nil before
the first poll and when the session could not be opened.
The pump's thread, nil until the first poll opens the session.
stopping
Section titled “stopping”Whether destroy has asked the pump to stop. The pump checks it after every read, push and wait.
producerEnded
Section titled “producerEnded”Whether the stream's input has ended: its read returned nil, or the program's stdout closed.
bufferedSeconds
Section titled “bufferedSeconds”How many seconds of decoded audio the queue holds, up to about 6.
pendingFailure
Section titled “pendingFailure”The failure the feed met, kept until the source has played the audio queued before it. The first failure wins, except a program's non-zero exit code, which wins over every other.
command
Section titled “command”The program fromProcess runs; nil for fromStream.
arguments
Section titled “arguments”The program's arguments; empty for fromStream.
process
Section titled “process”The running program, from the first poll; nil for fromStream and when the program could not start.
exitCode
Section titled “exitCode”The program's exit code once it is known; nil until then, and when the code followed the feed's own kill.
failedAtMillis
Section titled “failedAtMillis”When pendingFailure was first set, in Unix milliseconds; nil until the feed fails.
exitKnown
Section titled “exitKnown”Whether the program's exit is settled: its code is known, or the feed killed it, which makes the code meaningless.
Always false for fromStream, which has no program; see exitSettled.
killedByUs
Section titled “killedByUs”Whether the feed killed the program, so its exit code is ignored.
fallbackEnabled
Section titled “fallbackEnabled”Whether the whole-file fallback can still run: true until the bytes kept before the session opened pass
maxRetainedBytes.
fallingBack
Section titled “fallingBack”Whether the session failed before it opened on an MP4 or CAF stream, so the pump reads the rest of the file to open it whole.
retained
Section titled “retained”Every byte read before the session opened, in one buffer that doubles as it grows; nil once the session opens, past the cap, and after the fallback opened the file.
retainedBytes
Section titled “retainedBytes”How many bytes of retained are used.
maxRetainedBytes
Section titled “maxRetainedBytes”The most bytes kept for the fallback, 128 MiB. Tests lower it to reach the cap.
firstBytes
Section titled “firstBytes”The stream's first 8 bytes, always kept for the MP4 and CAF marker check.
outputClosed
Section titled “outputClosed”Whether the feed has stopped reading the session's output for good: a read raised, an Opus packet was not valid, or the pump failed. No more audio will reach the queue.
started
Section titled “started”Whether the first poll has started the stream: spawned the program and opened the session, or tried to.
pipeReading
Section titled “pipeReading”Whether a read of the program's stdout is in flight.
pipeDone
Section titled “pipeDone”Whether the read in flight has finished; pipeData holds its bytes, nil at the end of stdout, and pipeFailure its
error when it raised.
pipeData
Section titled “pipeData”The bytes the finished stdout read returned.
pipeFailure
Section titled “pipeFailure”The error of the finished stdout read when it raised; the pump raises it when it takes that read.
pipeWaiter
Section titled “pipeWaiter”The pump, while it waits for the stdout read in flight.
reader
Section titled “reader”The stream's read, which returns the next chunk of the file's bytes and nil at the end; nil for fromProcess.
logLevel
Section titled “logLevel”The level of the stream session's logger, SymphoniaDecoder.
The read-ahead, oldest first: float blocks or Opus packets.
queueOffset
Section titled “queueOffset”How many bytes of the first queued float block have been taken.
bufferedFrames
Section titled “bufferedFrames”How many 48 kHz frames the queue holds; bufferedSeconds is this over 48000.
streamBytes
Section titled “streamBytes”How many bytes the stream has given so far.
logger
Section titled “logger”The owning source's logger, AudioSource, which the feed logs through.
Methods
Section titled “Methods”StreamFeed : poll ( ) -> ()
Moves the stream along. The first call spawns the program, for fromProcess, then opens the session, which loads
libsymphonia on first use and so may yield, and starts the pump; a program that cannot start or a failed open
becomes pendingFailure. Every call then reads the session's state and fills the queue. Does nothing after
destroy.
isOpen
Section titled “isOpen”StreamFeed : isOpen ( ) -> boolean
Whether the session has opened, so the queue's kind of audio is known.
Returns
| boolean |
isOpus
Section titled “isOpus”StreamFeed : isOpus ( ) -> boolean
Whether the session opened as Opus, so the queue holds Opus packets rather than float blocks.
Returns
| boolean |
outputDone
Section titled “outputDone”StreamFeed : outputDone ( ) -> boolean
Whether no more audio will reach the queue: the session has ended, or the feed stopped reading its output after a failure. What is still queued plays first.
Returns
| boolean |
openDecided
Section titled “openDecided”StreamFeed : openDecided ( ) -> boolean
Whether the session's open is decided, so that a failure met before it opened is final. It is when the session has opened or failed before it opened, when there is no session or the stream gave no bytes, and 500 ms after the failure. A stream that fails just after bytes the session has not probed yet so plays them first whenever they open, rather than depending on which of the two the source saw first.
Returns
| boolean |
exitSettled
Section titled “exitSettled”StreamFeed : exitSettled ( ) -> boolean
Whether the program's exit is settled: always for fromStream, and for fromProcess once the exit code is known or
the feed killed the program.
Returns
| boolean |
hasEnded
Section titled “hasEnded”StreamFeed : hasEnded ( ) -> boolean
Whether the stream has ended: no more audio will reach the queue, the program's exit is settled, and the open is
decided (see openDecided), so a stream that may still open is never taken for ended. This is the one test for the
end of the stream.
Returns
| boolean |
hasQueued
Section titled “hasQueued”StreamFeed : hasQueued ( ) -> boolean
Whether the queue holds any audio.
Returns
| boolean |
takeFloats
Section titled “takeFloats”StreamFeed : takeFloats ( maxFrames ) -> buffer?
Takes up to maxFrames float frames from the first queued block, filling the queue from the session first when it
is empty. Returns the floats and how many frames to use from them, or nil and 0 when nothing is ready.
takePacket
Section titled “takePacket”StreamFeed : takePacket ( ) -> buffer?
Takes the next queued Opus packet, filling the queue from the session first when it is empty. Nil when nothing is ready.
Returns
| buffer? |
StreamFeed : fill ( ) -> ()
Moves ready output from the session into the queue, up to 6 seconds. A read that raises, which is a failure after
the session opened, and an Opus packet that is not valid each become pendingFailure and close the output. Does
nothing before the session opens, after destroy or once the output is closed.
refresh
Section titled “refresh”StreamFeed : refresh ( ) -> ()
Reads the session's state. Once it has opened, the bytes kept for the fallback are dropped. A session that failed
before it opened sends an MP4 or CAF stream into the whole-file fallback, fails one that is past the cap with the
128 MiB message, and fails any other with could not open audio: <reason>.
destroy
Section titled “destroy”StreamFeed : destroy ( ) -> ()
Stops the pump at its next check, kills the program unless its exit is already settled, closes the session,
whichever decoder it is by then, and drops the queue and the kept bytes. It never waits for the pump: a pump inside
the stream's read stops once that read returns. A second call logs at debug.
Functions
Section titled “Functions”StreamFeed . new ( reader , logLevel , sourceLogger ) -> StreamFeed
Creates the feed for audio.fromStream; nothing is read, opened or started until the first poll. sourceLogger is
the owning source's logger.
Parameters
| reader: StreamReader | |
| logLevel: LogLevel? | |
| sourceLogger: Logger |
Returns
| StreamFeed |
fromProcess
Section titled “fromProcess”StreamFeed . fromProcess ( command , arguments , logLevel , sourceLogger ) -> StreamFeed
Creates the feed for audio.fromProcess; the program is not started until the first poll.
Parameters
| command: string | |
| arguments: { string } | |
| logLevel: LogLevel? | |
| sourceLogger: Logger |
Returns
| StreamFeed |