Overview | Package | Class | Tree | Deprecated | Index | Help
PREV CLASS NEXT CLASS | FRAMES NO FRAMES
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD

## javazoom.spi.mpeg.sampled.file.tag
Class IcyInputStream

    java.lang.Object
      java.io.InputStream
          java.io.FilterInputStream
              java.io.BufferedInputStream
                  javazoom.spi.mpeg.sampled.file.tag.IcyInputStream

All Implemented Interfaces:
  MP3MetadataParser

public class IcyInputStream
extends java.io.BufferedInputStream
implements MP3MetadataParser

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.

Field Summary
`protected int` | `bytesUntilNextMetadata` how many bytes of real data remain before the next block of metadata.
`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)
`static boolean` | `DEBUG`
`protected static java.lang.String` | `INLINE_TAG_SEPARATORS` inline tags are delimited by ';', also filter out null bytes
`protected int` | `metaint` value of the "metaint" tag, which tells us how many bytes of real data are between the metadata tags.

Fields inherited from class java.io.BufferedInputStream
`buf, count, marklimit, markpos, pos`

Fields inherited from class java.io.FilterInputStream
`in`

Constructor Summary
`IcyInputStream(java.io.InputStream in)` Reads the initial headers of the stream and adds tags appropriatly.
`IcyInputStream(java.io.InputStream in, java.lang.String metaIntString)` IcyInputStream constructor for know meta-interval (Icecast 2)

Method Summary
`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(java.lang.String tagName)` Get the named tag from the HashMap of headers and in-line tags.
`java.util.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(java.lang.String[] 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 java.lang.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.

Methods inherited from class java.io.BufferedInputStream
`available, close, mark, markSupported, reset, skip`

Methods inherited from class java.lang.Object
`clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

Field Detail

### DEBUG

    public static boolean DEBUG

### INLINE_TAG_SEPARATORS

    protected static final java.lang.String INLINE_TAG_SEPARATORS

  inline tags are delimited by ';', also filter out null bytes

  See Also:
    Constant Field Values

### 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.

### bytesUntilNextMetadata

    protected int bytesUntilNextMetadata

  how many bytes of real data remain before the next block of metadata. Only meaningful if metaint != -1.

Constructor Detail

### IcyInputStream

    public IcyInputStream(java.io.InputStream in)
                   throws java.io.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(java.io.InputStream in,
                          java.lang.String metaIntString)
                   throws java.io.IOException

  IcyInputStream constructor for know meta-interval (Icecast 2)

Parameters:
  `in` -
Throws:
  `java.io.IOException`

Method Detail

### readInitialHeaders

    protected void readInitialHeaders()
                               throws java.io.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.

  Throws:
    `java.io.IOException`

### readCRLFLine

    protected java.lang.String readCRLFLine()
                                     throws java.io.IOException

  Read everything up to the next CRLF, return it as a String.

  Throws:
    `java.io.IOException`

### read

    public int read()
             throws java.io.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.

  Throws:
    `java.io.IOException`

### read

    public int read(byte[] buf,
                    int offset,
                    int length)
             throws java.io.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`

  Throws:
    `java.io.IOException`

### read

    public int read(byte[] buf)
             throws java.io.IOException

  trivial `return read (buf, 0, buf.length)`

  Throws:
    `java.io.IOException`

### readMetadata

    protected void readMetadata()
                         throws java.io.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

  Throws:
    `java.io.IOException`

### 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:
  1. Tags are delimited by semicolons
  2. Keys/values are delimited by equals-signs
  3. Values are wrapped in single-quotes
  4. Key names are in SentenceCase, not lowercase-dashed

### 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(java.lang.String tagName)

  Get the named tag from the HashMap of headers and in-line tags. Null if no such tag has been encountered.

### getTags

    public MP3Tag[] getTags()

  Get all tags (headers or in-stream) encountered thus far.

  Specified by:
    `getTags` in interface `MP3MetadataParser`

### getTagHash

    public java.util.HashMap getTagHash()

  Returns a HashMap of all headers and in-stream tags parsed so far.

### addTagParseListener

    public void addTagParseListener(TagParseListener tpl)

  Adds a TagParseListener to be notified when this stream parses MP3Tags.

  Specified by:
    `addTagParseListener` in interface `MP3MetadataParser`

### removeTagParseListener

    public void removeTagParseListener(TagParseListener tpl)

  Removes a TagParseListener, so it won't be notified when this stream parses MP3Tags.

  Specified by:
    `removeTagParseListener` in interface `MP3MetadataParser`

### main

    public static void main(java.lang.String[] args)

  Quickie unit-test.

Overview | Package | Class | Tree | Deprecated | Index | Help
PREV CLASS NEXT CLASS | FRAMES NO FRAMES
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD

JavaZOOM 1999-2005