javazoom.spi.mpeg.sampled.file.tag
Class IcyInputStream
BufferedInputStream
javazoom.spi.mpeg.sampled.file.tag.IcyInputStream
- MP3MetadataParser
public class IcyInputStream
extends BufferedInputStream
An BufferedInputStream that parses Shoutcast's "icy" metadata
from the stream. Gets headers at the beginning and if the
"icy-metaint" tag is found, it parses and strips in-stream
metadata.
The deal with metaint: Icy streams don't try to put
tags between MP3 frames the way that ID3 does. Instead, it
requires the client to strip metadata from the stream before
it hits the decoder. You get an
icy-metaint
name/val in the beginning of the
stream iff you sent "Icy-Metadata" with value "1" in the
request headers (SimpleMP3DataSource does this if the
"parseStreamMetadata" boolean is true). If this is the case
then the value of icy-metaint is the amount of real data
between metadata blocks. Each block begins with an int
indicating how much metadata there is -- the block is this
value times 16 (it can be, and often is, 0).
Originally thought that "icy" implied Icecast, but this is
completely wrong -- real Icecast servers, found through
www.icecast.net and typified by URLs with a trailing directory
(like CalArts School of Music - http://65.165.174.100:8000/som)
do not have the "ICY 200 OK" magic string or any of the
CRLF-separated headers. Apparently, "icy" means "Shoutcast".
Yep, that's weird.
static boolean | DEBUG
|
protected static String | INLINE_TAG_SEPARATORS - inline tags are delimited by ';', also filter out
null bytes
|
protected int | bytesUntilNextMetadata - how many bytes of real data remain before the next
block of metadata.
|
protected byte[] | crlfBuffer - Buffer for readCRLF line...
|
protected int | metaint - value of the "metaint" tag, which tells us how many bytes
of real data are between the metadata tags.
|
IcyInputStream(InputStream in) - Reads the initial headers of the stream and adds
tags appropriatly.
|
IcyInputStream(InputStream in, String metaIntString) - IcyInputStream constructor for know meta-interval (Icecast 2)
|
protected void | addTag(IcyTag tag) - adds the tag to the HashMap of tags we have encountered
either in-stream or as headers, replacing any previous
tag with this name.
|
void | addTagParseListener(TagParseListener tpl) - Adds a TagParseListener to be notified when this stream
parses MP3Tags.
|
MP3Tag | getTag(String tagName) - Get the named tag from the HashMap of headers and
in-line tags.
|
HashMap | getTagHash() - Returns a HashMap of all headers and in-stream tags
parsed so far.
|
MP3Tag[] | getTags() - Get all tags (headers or in-stream) encountered thus far.
|
static void | main(args[] ) - Quickie unit-test.
|
protected void | parseInlineIcyTags(byte[] tagBlock) - Parse metadata from an in-stream "block" of bytes, add
a tag for each one.
|
int | read() - Reads and returns a single byte.
|
int | read(byte[] buf) - trivial
return read (buf, 0, buf.length)
|
int | read(byte[] buf, int offset, int length) - Reads a block of bytes.
|
protected String | readCRLFLine() - Read everything up to the next CRLF, return it as
a String.
|
protected void | readInitialHeaders() - Assuming we're at the top of the stream, read lines one
by one until we hit a completely blank \r\n.
|
protected void | readMetadata() - Read the next segment of metadata.
|
void | removeTagParseListener(TagParseListener tpl) - Removes a TagParseListener, so it won't be notified when
this stream parses MP3Tags.
|
DEBUG
public static boolean DEBUG
INLINE_TAG_SEPARATORS
protected static final String INLINE_TAG_SEPARATORS
inline tags are delimited by ';', also filter out
null bytes
bytesUntilNextMetadata
protected int bytesUntilNextMetadata
how many bytes of real data remain before the next
block of metadata. Only meaningful if metaint != -1.
crlfBuffer
protected byte[] crlfBuffer
Buffer for readCRLF line... note this limits lines to
1024 chars (I've read that WinAmp barfs at 128, so
this is generous)
metaint
protected int metaint
value of the "metaint" tag, which tells us how many bytes
of real data are between the metadata tags. if -1, this stream
does not have metadata after the header.
IcyInputStream
public IcyInputStream(InputStream in)
throws IOException
Reads the initial headers of the stream and adds
tags appropriatly. Gets set up to find, read,
and strip blocks of in-line metadata if the
icy-metaint
header is found.
IcyInputStream
public IcyInputStream(InputStream in,
String metaIntString)
throws IOException
IcyInputStream constructor for know meta-interval (Icecast 2)
addTag
protected void addTag(IcyTag tag)
adds the tag to the HashMap of tags we have encountered
either in-stream or as headers, replacing any previous
tag with this name.
getTag
public MP3Tag getTag(String tagName)
Get the named tag from the HashMap of headers and
in-line tags. Null if no such tag has been encountered.
getTagHash
public HashMap getTagHash()
Returns a HashMap of all headers and in-stream tags
parsed so far.
main
public static void main(args[] )
Quickie unit-test.
parseInlineIcyTags
protected void parseInlineIcyTags(byte[] tagBlock)
Parse metadata from an in-stream "block" of bytes, add
a tag for each one.
Hilariously, the inline data format is totally different
than the top-of-stream header. For example, here's a
block I saw on "Final Fantasy Radio":
StreamTitle='Final Fantasy 8 - Nobuo Uematsu - Blue Fields';StreamUrl='';
In other words:
- Tags are delimited by semicolons
- Keys/values are delimited by equals-signs
- Values are wrapped in single-quotes
- Key names are in SentenceCase, not lowercase-dashed
read
public int read()
throws IOException
Reads and returns a single byte.
If the next byte is a metadata block, then that
block is read, stripped, and parsed before reading
and returning the first byte after the metadata block.
read
public int read(byte[] buf)
throws IOException
trivial return read (buf, 0, buf.length)
read
public int read(byte[] buf,
int offset,
int length)
throws IOException
Reads a block of bytes. If the next byte is known
to be a block of metadata, then that is read, parsed,
and stripped, and then a block of bytes is read and
returned.
Otherwise, it may read up to but
not into the next metadata block if
bytesUntilNextMetadata < length
readCRLFLine
protected String readCRLFLine()
throws IOException
Read everything up to the next CRLF, return it as
a String.
readInitialHeaders
protected void readInitialHeaders()
throws IOException
Assuming we're at the top of the stream, read lines one
by one until we hit a completely blank \r\n. Parse the
data as IcyTags.
readMetadata
protected void readMetadata()
throws IOException
Read the next segment of metadata. The stream must
be right on the segment, ie, the next byte to read is
the metadata block count. The metadata is parsed and
new tags are added with addTag(), which fires events
JavaZOOM 1999-2005