2012-10-03 9 views
14

Desidero spostare un file mp3 in streaming progressivo su scheda SD una volta che è stato caricato completamente. C'è un modo per ottenerlo.Accesso alla cache del lettore multimediale

Ho visto che lo MediaPlayer scarica completamente l'intero file durante lo streaming progressivo e quindi possiamo cercare qualsiasi parte del file. Voglio spostare un file completamente in streaming su una memoria esterna in modo che la riproduzione futura non sprechi dati e batteria.

+2

La migliore risposta lei e, se qualcuno cerca ancora una soluzione: http://stackoverflow.com/a/12044709/1548464 – Taras

risposta

10

Il commento sui punti voi post originale nella direzione giusta, ma ho pensato che potrebbe essere utile per esporre un po '...

Quello che ho fatto è costruire un server proxy leggero utilizzando Naga e il Librerie HTTP Apache. Ci dovrebbero essere un sacco di esempi là fuori per ottenere le basi di questa parte. Fornire a MediaPlayer un URL localhost appropriato in modo che apra un socket al proprio proxy. Quando MediaPlayer effettua una richiesta, utilizzare il proxy per inviare una richiesta equivalente all'host multimediale effettivo. Riceverai i dati di byte [] nel metodo packetReceived del proxy, che utilizzo per creare un HttpGet e inviarlo tramite AndroidHttpClient.

Si otterrà una risposta Http ed è possibile utilizzare HttpEntity per accedere ai dati del byte di streaming. Io sto usando un ReadableByteChannel, in questo modo:

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

Do qualunque gradireste con i dati, come si legge indietro (come la cache in un file sulla scheda SD). Per trasferire gli elementi corretti a MediaPlayer, scaricare SocketChannel dal client Socket, innanzitutto scrivere gli header di risposta direttamente su quel canale e quindi procedere con la scrittura dei dati di byte dell'entità. Sto usando un ByteBuffer NIO in un ciclo while (il client è un socket e il buffer è 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 
    } 
} 

Potrebbe essere necessario modificare l'intestazione host nella risposta prima di passarlo insieme al giocatore in modo che sembra che il proxy è il mittente, ma ho a che fare con un'implementazione proprietaria del MediaPlayer così comportamento potrebbe essere un po 'diverso Spero possa aiutare.

+2

Puoi condividere qualche altro codice su come creare il proxy e intercettare i dati ?? – anz

+0

come possiamo intercettare la richiesta di mediaplayer? – anz

14

L'idea è di creare un proxy da cui il lettore multimediale può leggere, invece di leggere i dati direttamente dal web.

Ho usato danikula/AndroidVideoCache che è molto semplice da costruire/utilizzare. L'ho usato per Audio non video, ma è lo stesso.

+1

Questa dovrebbe essere la risposta giusta. Diretto e facile da usare. Vorrei poter dare 10 voti. – MetaSnarf

+0

Come posso crittografare il file di cache? –

3

È tardi ma ho scoperto che la maggior parte della gente ha ancora bisogno di una soluzione. La mia soluzione basata su JakeWharton's DiskLruCache. Abbiamo bisogno di due cose

  • AsyncTask per leggere file o scaricare dalla rete e la cache è

  • richiamata per ottenere InputStram/FileDescriptor dalla cache

Fase 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(); 
    } 
} 

Fase 2:

public interface OnCacheCallback { 

    void onSuccess(FileInputStream stream); 

    void onError(); 
} 

Esempio

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:

Questo è testato, ma ruvido per il caching di file audio, ci possono essere alcuni problemi se si trova tutto per favore mi informa :)

+0

cosa intendi con questo // classe di applicazione in cui ho aperto DiskLruCache' – Naz141

+0

Inizializza DiskLruCache nella tua classe Application (si estende con Application), o dove vuoi tu :) –

+0

Se puoi mostrarmi il pezzo di codice come hai scritto l'inizializzazione di DiskLruCache. grazie – Naz141

Problemi correlati