2012-02-11 18 views
5

Estoy escribiendo una aplicación para Android que toma metadatos de las transmisiones de SHOUTcast mp3. Estoy usando una clase bastante ingeniosa que encontré en línea que modifiqué ligeramente, pero todavía tengo 2 problemas.Obtener metadatos de SHOUTcast usando IcyStreamMeta

1) Tengo que hacer ping continuamente al servidor para actualizar los metadatos usando un TimerTask. No soy aficionado a este enfoque, pero fue todo lo que pude pensar.

2) Hay una tonelada métrica de recolección de basura mientras mi aplicación se está ejecutando. La eliminación de TimerTask eliminó el problema de la recolección de basura, por lo que no estoy seguro de si lo estoy haciendo mal o si esto es normal.

Aquí está la clase que estoy utilizando:

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

}

Y aquí es mi temporizador:

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

} 

Mucho aprecio para cualquier ayuda!

+0

Moví la línea 'helada = nueva IcyStreamMeta (nueva URL (stationUrl));' a mi método 'onCreate()' y eso corrigió el problema de la recolección de basura. Pero ahora mis metadatos no se están actualizando ... – Karai17

+0

De acuerdo, llamo a 'icy.refreshMeta()' antes de 'setText()' y mis metadatos se vuelven a actualizar, pero hay recolección de basura cada intervalo de actualización. ¿Esto es malo? Va a agotar la vida de la batería? – Karai17

+0

http://pastie.org/3362717 – Karai17

Respuesta

3

He reemplazado la clase IcyStreamMeta en mi programa y obtengo los metadatos del archivo 7.html que forma parte de la especificación SHOUTcast. Mucho menos uso de datos y todo eso, así que creo que es una mejor opción.

Todavía estoy usando el TimerTask, que es aceptable. Prácticamente ya no hay GC y estoy contento con el uso de 7.html y un poco de regex. :)

+1

+1 para el 7.html :) –

+2

También relevante: http://wiki.winamp.com/wiki/SHOUTcast_DNAS_Server_2_XML_Reponses –

+0

El problema con las respuestas XML es que debe estar en modo administrativo para acceder a ellos, no son públicos. Para usar esto directamente, necesitaría insertar el nombre de usuario y la contraseña en mi aplicación, que no solo es un problema de seguridad, sino que también es miope. En última instancia, lo que hice fue configurar un script php que publica los datos XML deseados como una cadena json y lo sondeo cada 15 segundos. – Karai17

Cuestiones relacionadas