2012-02-11 160 views
5

我正在为Android编写一个应用程序,该应用程序从SHOUTcast mp3流中获取元数据。我正在使用我在网上发现的一个非常漂亮的课程,稍作修改,但我仍然遇到2个问题。使用IcyStreamMeta从SHOUTcast获取元数据

1)我必须连续ping服务器以使用TimerTask更新元数据。我不喜欢这种方法,但这是我所能想到的。

2)当我的应用程序正在运行时,有一吨垃圾收集。删除TimerTask摆脱了垃圾收集问题,所以我不确定是否我做错了或者这是正常的。

这里是我使用的类:

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; 
} 

}

这里是我的定时器:

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); 

} 

的任何援助大部分升值!

+0

我移动了'Iicy = new IcyStreamMeta(new URL(stationUrl))''我的'onCreate()'方法并修复了垃圾回收问题。但是现在我的元数据没有更新... – Karai17 2012-02-11 18:19:44

+0

Alrighty,我在'setText()'之前调用'icy.refreshMeta()'并且我的元数据再次刷新,但每个刷新间隔都有垃圾收集。这是坏事吗?它会消耗电池寿命吗? – Karai17 2012-02-11 19:34:35

+0

http://pastie.org/3362717 – Karai17 2012-02-11 20:05:09

回答

3

我已经替换了我的程序中的IcyStreamMeta类,并从7.html文件中获取了元数据,这是SHOUTcast规范的一部分。数据使用少得多,所以我觉得这是一个更好的选择。

我仍在使用TimerTask,这是可以接受的。几乎没有GC,我很高兴使用7.html和一点正则表达式。 :)

+1

+1为7.html :) – 2013-07-23 00:06:08

+2

也相关:http://wiki.winamp.com/wiki/SHOUTcast_DNAS_Server_2_XML_Reponses – 2013-07-23 00:10:55

+0

XML响应的问题是您需要在管理模式下访问他们,他们不公开。要直接使用这个,我需要将用户名和密码嵌入到我的应用程序中,这不仅是一个安全问题,而且是短视的。最终,我所做的是建立一个PHP脚本,将所需的XML数据发布为json字符串,并每隔15秒轮询一次。 – Karai17 2013-07-23 03:52:44