2012-10-03 16 views
14

Chcę przenieść stopniowo odtwarzany plik mp3 na kartę SD po jej całkowitym załadowaniu. Czy jest jakiś sposób na osiągnięcie tego.Uzyskiwanie dostępu do pamięci podręcznej odtwarzacza multimediów

Zauważyłem, że MediaPlayer całkowicie pobiera cały plik podczas progresywnego przesyłania strumieniowego, a następnie możemy przeszukać dowolną część pliku. Chcę przenieść całkowicie przesyłany plik do zewnętrznej pamięci masowej, aby przyszłe odtwarzanie nie powodowało marnowania danych i baterii.

+2

Najlepsza odpowiedź e, jeśli ktoś nadal szuka rozwiązania: http://stackoverflow.com/a/12044709/1548464 – Taras

Odpowiedz

10

Komentarz na oryginalnych punktów pocztowych w dobrym kierunku, ale pomyślałem, że może to być pomocne wyłożyć trochę ...

Co robiłem jest zbudować lekki serwer proxy za pomocą Naga i Biblioteki HTTP Apache. Powinno być mnóstwo przykładów, aby uzyskać podstawy tej części. Podaj MediaPlayer odpowiedni adres URL localhost, aby otwierał gniazdo na serwer proxy. Kiedy MediaPlayer zgłasza żądanie, użyj proxy, aby przesłać równoważne żądanie do rzeczywistego hosta mediów. Otrzymasz dane bajtowe [] w metodzie pakietowej proxy, której użyłem do zbudowania HttpGet i wysłania go po drodze z AndroidHttpClient.

Otrzymasz HttpResponse i możesz użyć HttpEntity w środku, aby uzyskać dostęp do danych o bajtach transmisji strumieniowej. Używam ReadableByteChannel, tak:

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

robić, co chcesz z danymi jak można przeczytać ją z powrotem (jak cache go w pliku na karcie SD). Aby przekazać właściwe rzeczy do MediaPlayer, pobierz SocketChannel z gniazda klienta, najpierw zapisz nagłówki odpowiedzi bezpośrednio do tego kanału, a następnie przystąp do zapisu danych bajtowych jednostki. Korzystam z NIO ByteBuffer w pętli while (klient to gniazdo, a bufor to 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 
    } 
} 

Być może trzeba zmienić nagłówek hosta w odpowiedzi przed przekazaniem go wraz z odtwarzaczem tak, że wygląda jak serwer proxy jest nadawca, ale mam do czynienia z zastrzeżonych realizacji MediaPlayer więc zachowanie może być trochę inaczej. Nadzieja, która pomaga.

+2

Czy możesz udostępnić nieco więcej kodu na temat tworzenia proxy i przechwytywania danych? – anz

+0

Jak możemy przechwycić żądanie mediaplayer? – anz

14

Chodzi o to, aby utworzyć serwer proxy, z którego może odtwarzać odtwarzacz multimedialny, zamiast odczytywać dane bezpośrednio z internetu.

Użyłem danikula/AndroidVideoCache, który jest bardzo prosty w budowie/użyciu. Użyłem go dla Audio nie wideo, ale jest tak samo.

+1

To powinna być właściwa odpowiedź. Bezpośredni i łatwy w użyciu. Chciałbym dać 10 głosów. – MetaSnarf

+0

Jak mogę zaszyfrować plik pamięci podręcznej? –

3

Jest późno, ale okazało się, że większość ludzi nadal potrzebuje rozwiązania. Moje rozwiązanie oparte na JakeWharton's DiskLruCache. Musimy dwie rzeczy

  • AsyncTask można odczytać pliku lub pobrać z sieci i zapamiętania go

  • oddzwanianie dostać InputStram/deskryptora pliku z pamięci podręcznej

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

Krok 2:

public interface OnCacheCallback { 

    void onSuccess(FileInputStream stream); 

    void onError(); 
} 

Przykład

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

Uwaga:

To jest testowany, ale szorstkie próbki do buforowania plików audio mogą być pewne problemy, jeśli okaże wszystko proszę poinformuj mnie :)

+0

co masz na myśli przez tę '// klasę aplikacji, w której otworzyłem DiskLruCache' – Naz141

+0

Zainicjuj DiskLruCache w twojej klasie aplikacji (rozciąga się z aplikacją) lub gdziekolwiek chcesz :) –

+0

Jeśli możesz pokazać mi kawałek kodu, jak masz napisałem inicjację DiskLruCache. dziękuję – Naz141

Powiązane problemy