Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7531149
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 30, 20262026-05-30T05:06:03+00:00 2026-05-30T05:06:03+00:00

I am writing an app for Android that grabs meta data from SHOUTcast mp3

  • 0

I am writing an app for Android that grabs meta data from SHOUTcast mp3 streams. I am using a pretty nifty class I found online that I slightly modified, but I am still having 2 problems.

1) I have to continuously ping the server to update the metadata using a TimerTask. I am not fond of this approach but it was all I could think of.

2) There is a metric tonne of garbage collection while my app is running. Removing the TimerTask got rid of the garbage collection issue so I am not sure if I am just doing it wrong or if this is normal.

Here is the class I am using:

public class IcyStreamMeta {
    protected URL streamUrl;
    private Map<String, String> metadata;
    private boolean isError;

public IcyStreamMeta(URL streamUrl) {
    setStreamUrl(streamUrl);

    isError = false;
}

/**
 * Get artist using stream's title
 *
 * @return String
 * @throws IOException
 */
public String getArtist() throws IOException {
    Map<String, String> data = getMetadata();

    if (!data.containsKey("StreamTitle"))
        return "";

    try {
        String streamTitle = data.get("StreamTitle");
        String title = streamTitle.substring(0, streamTitle.indexOf("-"));
        return title.trim();
    }catch (StringIndexOutOfBoundsException e) {
        return "";
    }
}

/**
 * Get title using stream's title
 *
 * @return String
 * @throws IOException
 */
public String getTitle() throws IOException {
    Map<String, String> data = getMetadata();

    if (!data.containsKey("StreamTitle"))
        return "";

    try {
        String streamTitle = data.get("StreamTitle");
        String artist = streamTitle.substring(streamTitle.indexOf("-")+1);
        return artist.trim();
    } catch (StringIndexOutOfBoundsException e) {
        return "";
    }
}

public Map<String, String> getMetadata() throws IOException {
    if (metadata == null) {
        refreshMeta();
    }

    return metadata;
}

public void refreshMeta() throws IOException {
    retreiveMetadata();
}

private void retreiveMetadata() throws IOException {
    URLConnection con = streamUrl.openConnection();
    con.setRequestProperty("Icy-MetaData", "1");
    con.setRequestProperty("Connection", "close");
    //con.setRequestProperty("Accept", null);
    con.connect();

    int metaDataOffset = 0;
    Map<String, List<String>> headers = con.getHeaderFields();
    InputStream stream = con.getInputStream();

    if (headers.containsKey("icy-metaint")) {
        // Headers are sent via HTTP
        metaDataOffset = Integer.parseInt(headers.get("icy-metaint").get(0));
    } else {
        // Headers are sent within a stream
        StringBuilder strHeaders = new StringBuilder();
        char c;
        while ((c = (char)stream.read()) != -1) {
            strHeaders.append(c);
            if (strHeaders.length() > 5 && (strHeaders.substring((strHeaders.length() - 4), strHeaders.length()).equals("\r\n\r\n"))) {
                // end of headers
                break;
            }
        }

        // Match headers to get metadata offset within a stream
        Pattern p = Pattern.compile("\\r\\n(icy-metaint):\\s*(.*)\\r\\n");
        Matcher m = p.matcher(strHeaders.toString());
        if (m.find()) {
            metaDataOffset = Integer.parseInt(m.group(2));
        }
    }

    // In case no data was sent
    if (metaDataOffset == 0) {
        isError = true;
        return;
    }

    // Read metadata
    int b;
    int count = 0;
    int metaDataLength = 4080; // 4080 is the max length
    boolean inData = false;
    StringBuilder metaData = new StringBuilder();
    // Stream position should be either at the beginning or right after headers
    while ((b = stream.read()) != -1) {
        count++;

        // Length of the metadata
        if (count == metaDataOffset + 1) {
            metaDataLength = b * 16;
        }

        if (count > metaDataOffset + 1 && count < (metaDataOffset + metaDataLength)) {              
            inData = true;
        } else {                
            inData = false;             
        }               
        if (inData) {               
            if (b != 0) {                   
                metaData.append((char)b);               
            }           
        }               
        if (count > (metaDataOffset + metaDataLength)) {
            break;
        }

    }

    // Set the data
    metadata = IcyStreamMeta.parseMetadata(metaData.toString());

    // Close
    stream.close();
}

public boolean isError() {
    return isError;
}

public URL getStreamUrl() {
    return streamUrl;
}

public void setStreamUrl(URL streamUrl) {
    this.metadata = null;
    this.streamUrl = streamUrl;
    this.isError = false;
}

public static Map<String, String> parseMetadata(String metaString) {
    Map<String, String> metadata = new HashMap<String, String>();
    String[] metaParts = metaString.split(";");
    Pattern p = Pattern.compile("^([a-zA-Z]+)=\\'([^\\']*)\\'$");
    Matcher m;
    for (int i = 0; i < metaParts.length; i++) {
        m = p.matcher(metaParts[i]);
        if (m.find()) {
            metadata.put((String)m.group(1), (String)m.group(2));
        }
    }

    return metadata;
}

}

And here is my timer:

private void getMeta() {
    timer.schedule(new TimerTask() {
        public void run() {
            try {
                icy = new IcyStreamMeta(new URL(stationUrl));

                runOnUiThread(new Runnable() {
                     public void run() {
                         try {
                             artist.setText(icy.getArtist());
                             title.setText(icy.getTitle());
                         } catch (IOException e) {
                             e.printStackTrace();
                         } catch (StringIndexOutOfBoundsException e) {
                             e.printStackTrace();
                         }
                     }
                });
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }

        }
    },0,5000);

}

Much appreciation for any assistance!

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-30T05:06:04+00:00Added an answer on May 30, 2026 at 5:06 am

    I’ve replaced the IcyStreamMeta class in my program and am getting the meta data from the 7.html file that is a part of the SHOUTcast spec. Far less data usage and all that so I feel it is a better option.

    I am still using the TimerTask, which is acceptable. There is practically no GC any more and I am happy with using 7.html and a little regex. 🙂

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I'm writing an app for android that needs to parse data from an XML
I'm writing an Android app that is parsing RSS feeds from different sources. What
I'm writing an Android app that reads and parses NMEA sentences from GPS receiver
I'm writing an app that sends images and video from an Android phone to
I'm planning on writing an android app that can view and update data on
I am writing a little picture frame app for android that is using opengl
I am writing an android app that recieves data over bluetooth. The bytes comming
So I'm writing an android app, and I have an xml parser class that
I'm currently writing an app in Android that works with the GPS. At the
I am writing an android app. I want to pass some data across the

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.