2012-02-11 45 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