2012-10-03 7 views
14

Quiero mover un archivo mp3 transmitido progresivamente a la tarjeta sd una vez que esté completamente cargado. ¿Hay alguna forma de lograr eso?Obtener acceso a la memoria caché del reproductor multimedia

He visto que el MediaPlayer descarga por completo todo el archivo durante la transmisión progresiva y luego podemos buscar cualquier parte del archivo. Quiero mover un archivo completamente transmitido a un almacenamiento externo para que la reproducción futura no desperdicie datos y batería.

+2

Mejor respuesta ella e, si alguien todavía busca la solución: http://stackoverflow.com/a/12044709/1548464 – Taras

Respuesta

10

El comentario en la entrada que los puntos originales en la dirección correcta, pero pensé que podría ser útil para exponer un poco ...

Lo que he hecho es crear un servidor proxy ligera usando Naga y el Bibliotecas HTTP de Apache. Debería haber muchos ejemplos para obtener los conceptos básicos de esta parte. Proporcione al MediaPlayer una URL localhost apropiada para que abra un socket a su proxy. Cuando el MediaPlayer realiza una solicitud, use el proxy para enviar una solicitud equivalente al host de medios real. Recibirá datos byte [] en el método Receptor del paquete del proxy, que utilizo para construir un HttpGet y enviarlo en camino con AndroidHttpClient.

Volverás a obtener una HttpResponse y podrás utilizar HttpEntity para acceder a los datos de bytes de transmisión. Estoy usando un ReadableByteChannel, así:

HttpEntityWrapper entity = (HttpEntityWrapper)response.getEntity(); 
ReadableByteChannel src = Channels.newChannel(entity.getContent()); 

Haz lo desea con los datos a medida que lee de nuevo (como caché en un archivo en la tarjeta SD). Para pasar el material correcto al MediaPlayer, obtenga el SocketChannel del socket del cliente, primero escriba los encabezados de respuesta directamente en ese canal, y luego proceda a escribir los datos de bytes de la entidad. Estoy usando un ByteBuffer NIO en un ciclo while (el cliente es un Socket y el buffer es un ByteBuffer).

int read, written; 
SocketChannel dst = client.getChannel(); 
while (dst.isConnected() && 
    dst.isOpen() && 
    src.isOpen() && 
    (read = src.read(buffer)) >= 0) { 
    try { 
     buffer.flip(); 
     // This is one point where you can access the stream data. 
     // Just remember to reset the buffer position before trying 
     // to write to the destination. 
     if (buffer.hasRemaining()) { 
      written = dst.write(buffer); 
      // If the player isn't reading, wait a bit. 
      if (written == 0) Thread.sleep(15); 
      buffer.compact(); 
     } 
    } 
    catch (IOException ex) { 
     // handle error 
    } 
} 

Es posible que tenga que modificar el encabezado de host en la respuesta antes de pasarlo junto al jugador de manera que parece que su representante sea el emisor, pero estoy tratando con una implementación propietaria de la MediaPlayer así el comportamiento podría ser un poco diferente. Espero que ayude.

+2

¿Puede compartir un poco más de código sobre cómo crear el proxy e interceptar los datos? – anz

+0

¿cómo podemos interceptar la solicitud de Media Player? – anz

14

La idea es crear un proxy que el reproductor multimedia pueda leer, en lugar de leer datos directamente desde la web.

He utilizado danikula/AndroidVideoCache que es muy simple de construir/usar. Lo usé para Audio, no Video, pero es lo mismo.

+1

Esta debería ser la respuesta correcta. Directo y fácil de usar. Ojalá pudiera dar 10 votos. – MetaSnarf

+0

¿Cómo puedo cifrar el archivo de caché? –

3

Es tarde pero encontré que la mayoría de la gente todavía necesita una solución. Mi solución basada en JakeWharton's DiskLruCache. Necesitamos dos cosas

  • AsyncTask para leer archivo o descargar de la red y la memoria caché se

  • de devolución de llamada para obtener InputStram/FileDescriptor de caché

Paso 1:

import android.content.Context; 
import android.os.AsyncTask; 
import org.apache.commons.io.IOUtils; 
import java.io.FileInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.net.HttpURLConnection; 
import java.net.URL; 

// you can use FileDescriptor as 
// extends AsyncTask<String, Void, FileDescriptor> 

public class AudioStreamWorkerTask extends AsyncTask<String, Void, FileInputStream> { 

    private OnCacheCallback callback = null; 
    private Context context = null; 

    public AudioStreamWorkerTask(Context context, OnCacheCallback callback) { 
     this.context = context; 
     this.callback = callback; 
    } 

    @Override 
    protected FileInputStream doInBackground(String... params) { 
     String data = params[0]; 
     // Application class where i did open DiskLruCache 
     DiskLruCache cache = MyApplication.getDiskCache(context); 
     if (cache == null) 
      return null; 
     String key = hashKeyForDisk(data); 
     final int DISK_CACHE_INDEX = 0; 
     long currentMaxSize = cache.getMaxSize(); 
     float percentageSize = Math.round((cache.size() * 100.0f)/currentMaxSize); 
     if (percentageSize >= 90) // cache size reaches 90% 
      cache.setMaxSize(currentMaxSize + (10 * 1024 * 1024)); // increase size to 10MB 
     try { 
      DiskLruCache.Snapshot snapshot = cache.get(key); 
      if (snapshot == null) { 
       Log.i(getTag(), "Snapshot is not available downloading..."); 
       DiskLruCache.Editor editor = cache.edit(key); 
       if (editor != null) { 
        if (downloadUrlToStream(data, editor.newOutputStream(DISK_CACHE_INDEX))) 
         editor.commit(); 
        else 
         editor.abort(); 
       } 
       snapshot = cache.get(key); 
      } else 
       Log.i(getTag(), "Snapshot found sending"); 
      if (snapshot != null) 
       return (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     Log.i(getTag(), "File stream is null"); 
     return null; 
    } 

    @Override 
    protected void onPostExecute(FileInputStream fileInputStream) { 
     super.onPostExecute(fileInputStream); 
     if (callback != null) { 
      if (fileInputStream != null) 
       callback.onSuccess(fileInputStream); 
      else 
       callback.onError(); 
     } 
     callback = null; 
     context = null; 
    } 

    public boolean downloadUrlToStream(String urlString, OutputStream outputStream) { 
     HttpURLConnection urlConnection = null; 
     try { 
      final URL url = new URL(urlString); 
      urlConnection = (HttpURLConnection) url.openConnection(); 
      InputStream stream = urlConnection.getInputStream(); 
      // you can use BufferedInputStream and BufferOuInputStream 
      IOUtils.copy(stream, outputStream); 
      IOUtils.closeQuietly(outputStream); 
      IOUtils.closeQuietly(stream); 
      Log.i(getTag(), "Stream closed all done"); 
      return true; 
     } catch (final IOException e) { 
      e.printStackTrace(); 
     } finally { 
      if (urlConnection != null) 
       IOUtils.close(urlConnection); 
     } 
     return false; 
    } 

    private String getTag() { 
     return getClass().getSimpleName(); 
    } 

    private String hashKeyForDisk(String key) { 
     String cacheKey; 
     try { 
      final MessageDigest mDigest = MessageDigest.getInstance("MD5"); 
      mDigest.update(key.getBytes()); 
      cacheKey = bytesToHexString(mDigest.digest()); 
     } catch (NoSuchAlgorithmException e) { 
      cacheKey = String.valueOf(key.hashCode()); 
     } 
     return cacheKey; 
    } 

    private String bytesToHexString(byte[] bytes) { 
     // http://stackoverflow.com/questions/332079 
     StringBuilder sb = new StringBuilder(); 
     for (byte aByte : bytes) { 
      String hex = Integer.toHexString(0xFF & aByte); 
      if (hex.length() == 1) 
       sb.append('0'); 
      sb.append(hex); 
     } 
     return sb.toString(); 
    } 
} 

Paso 2:

public interface OnCacheCallback { 

    void onSuccess(FileInputStream stream); 

    void onError(); 
} 

Ejemplo

final String path = "http://www.example.com/test.mp3"; 
new AudioStreamWorkerTask (TestActivity.this, new OnCacheCallback() { 

@Override 
public void onSuccess(FileInputStream fileInputStream) { 
    Log.i(getClass().getSimpleName() + ".MediaPlayer", "now playing..."); 
    if (fileInputStream != null) { 
     // reset media player here if necessary 
     mediaPlayer = new MediaPlayer(); 
     try { 
      mediaPlayer.setDataSource(fileInputStream.getFD()); 
      mediaPlayer.prepare(); 
      mediaPlayer.setVolume(1f, 1f); 
      mediaPlayer.setLooping(false); 
      mediaPlayer.start(); 
      fileInputStream.close(); 
     } catch (IOException | IllegalStateException e) { 
      e.printStackTrace(); 
     } 
    } else { 
     Log.e(getClass().getSimpleName() + ".MediaPlayer", "fileDescriptor is not valid"); 
    } 
} 

@Override 
public void onError() { 
    Log.e(getClass().getSimpleName() + ".MediaPlayer", "Can't play audio file"); 
} 
}).execute(path); 

Nota:

Esto se prueba, pero la muestra en bruto para el almacenamiento en caché de archivos de audio, puede haber algunos problemas si encuentra cualquier cosa por favor, infórmeme :)

+0

qué quiere decir con esto '// Clase de aplicación donde abrí DiskLruCache' – Naz141

+0

Inicialice DiskLruCache en su clase de Aplicación (se extiende con la Aplicación), o donde quiera :) :) –

+0

Si me puede mostrar el código de cómo lo tiene escrito la inicialización de DiskLruCache. gracias – Naz141

Cuestiones relacionadas