2013-05-06 14 views
8

Piszę aplikację do odtwarzania dźwięku ze zdalnego serwera. Próbowałem zastosować kilka sposobów przesyłania strumieniowego audio, ale wszystkie nie są dla mnie wystarczająco dobre. To co próbowałem:Najlepsze praktyki przesyłania strumieniowego audio

Naiwny korzystania z MediaPlayer

Coś jak:

MediaPlayer player = new MediaPlayer(); 
player.setDataSource(context, Uri.parse("http://whatever.com/track.mp3")); 
player.prepare(); 
player.start(); 

(lub prepareAsync, nieważne)

Ale norma MediaPlayer jest dość niestabilny podczas odtwarzania zdalnego zadowolony. Często spada lub zatrzymuje odtwarzanie i nie mogę tego przetworzyć. Z drugiej strony chcę zaimplementować buforowanie mediów. Ale nie znalazłem sposobu, aby pobrać buforowaną zawartość z MediaPlayer, aby zapisać ją gdzieś na urządzeniu.

Wdrażanie niestandardowego buforowania

Potem stał się pomysł, aby pobrać plik multimedialny przez kawałki, połączyć je w jeden lokalnego pliku i odtwarzanie tego pliku. Pobieranie całego pliku może być powolne z powodu złego połączenia, więc będzie można pobrać wystarczającą ilość początkowego fragmentu, a następnie rozpocząć odtwarzanie i kontynuować pobieranie i dołączanie lokalnego pliku. Poza tym uzyskujemy funkcję buforowania.

Brzmi jak plan, ale nie zawsze działa. Działa doskonale na telefonie HTC Sensation XE, ale nie zakończył odtwarzania 4.1 tabletu po ukończeniu tego początkowego utworu. Nie wiem, dlaczego tak jest. Pytałem o to question, ale nie otrzymałem odpowiedzi.

Używanie dwóch mediaplayers

Utworzyłem dwie instancje MediaPlayer i starał się uczynić je zmienić siebie. Logika jest następująca:

  • start pobierania wstępnego kawałek mediów
  • Po jego pobraniu rozpocząć odtwarzanie poprzez currentMediaPlayer. Reszta mediów nadal pobraniem
  • Po pobraniu kawałek jest prawie grał (1 sek przed zakończeniem), przygotować secondaryMediaPlayer z tego samego pliku źródłowego (jak to zostało dołączone podczas odtwarzania)
  • 261 ms przed wykończeniem currentMediaPlayer - wstrzymać , rozpocznij wtórne, ustaw wtórne jako aktualne, zaplanuj przygotowania kolejnego drugorzędnego gracza.

Źródło:

private static final String FILE_NAME="local.mp3"; 
private static final String URL = ...; 
private static final long FILE_SIZE = 7084032; 

private static final long PREPARE_NEXT_PLAYER_OFFSET = 1000; 
private static final int START_NEXT_OFFSET = 261; 

private static final int INIT_PERCENTAGE = 3; 

private MediaPlayer mPlayer; 
private MediaPlayer mSecondaryPlayer; 

private Handler mHandler = new Handler(); 

public void startDownload() { 
    mDownloader = new Mp3Downloader(FILE_NAME, URL, getExternalCacheDir()); 
    mDownloader.setDownloadListener(mInitDownloadListener); 
    mDownloader.startDownload(); 
} 


private Mp3Downloader.DownloadListener mInitDownloadListener = new Mp3Downloader.DownloadListener() { 
    public void onDownloaded(long bytes) { 
     int percentage = Math.round(bytes * 100f/FILE_SIZE); 

     // Start playback when appropriate piece of media downloaded 
     if (percentage >= INIT_PERCENTAGE) { 
      mPlayer = new MediaPlayer(); 
      try { 
       mPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath()); 
       mPlayer.prepare(); 
       mPlayer.start(); 

       mHandler.postDelayed(prepareSecondaryPlayerRunnable, mPlayer.getDuration() - PREPARE_NEXT_PLAYER_OFFSET); 
       mHandler.postDelayed(startNextPlayerRunnable, mPlayer.getDuration() - START_NEXT_OFFSET); 

      } catch (IOException e) { 
       Log.e(e); 
      } 

      mDownloader.setDownloadListener(null); 
     } 
    } 
}; 

// Starting to prepare secondary MediaPlayer 
private Runnable prepareSecondaryPlayerRunnable = new Runnable() { 
    public void run() { 
     mSecondaryPlayer = new MediaPlayer(); 
     try { 
      mSecondaryPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath()); 
      mSecondaryPlayer.prepare(); 
      mSecondaryPlayer.seekTo(mPlayer.getDuration() - START_NEXT_OFFSET); 

     } catch (IOException e) { 
      Log.e(e); 
     } 
    } 
}; 

// Starting secondary MediaPlayer playback, scheduling creating next MediaPlayer 
private Runnable startNextPlayerRunnable = new Runnable() { 
    public void run() { 
     mSecondaryPlayer.start(); 

     mHandler.postDelayed(prepareSecondaryPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - PREPARE_NEXT_PLAYER_OFFSET); 
     mHandler.postDelayed(startNextPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - START_NEXT_OFFSET); 

     mPlayer.pause(); 
     mPlayer.release(); 

     mPlayer = mSecondaryPlayer; 

    } 
}; 

Again - dźwięki, jak plan, ale nie działa idealnie. Momenty przełączania MediaPlayera są całkiem słyszalne. Tutaj mam przeciwną sytuację: na 4.1 tabletie jest OK, ale w HTC Sensation widać wyraźne opóźnienia.

Próbowałem również wdrożyć różne techniki pobierania. Zaimplementowałem pobieranie według porcji 10Kb i ramek MP3. Nie wiem dokładnie, ale wydaje się, że w przypadku ramek MP3 seekTo i zacząć działać lepiej. Ale to tylko uczucie, nie znam wytłumaczenia.

StreamingMediaPlayer

widziałem tego słowa kilka razy podczas googlowania i uznał ten realizacji: https://code.google.com/p/mynpr/source/browse/trunk/mynpr/src/com/webeclubbin/mynpr/StreamingMediaPlayer.java?r=18

Jest to wszyscy rozwiązanie użyciu?

Jeśli tak, to jest smutne, ponieważ nie działa dobrze dla mnie. I nie widzę żadnych świeżych pomysłów w realizacji.

Więc pytanie

Jak wy realizować strumienia audio w aplikacjach? Nie wierzę, że jestem jedyną osobą, która miała takie problemy. Powinny istnieć dobre praktyki.

+0

Bardzo ładne pytanie ... To także mnie wkurza przez ostatnie 2 tygodnie ... Ale potrzebuję tego do strumieniowego przesyłania wideo z jednego urządzenia do drugiego ... –

+0

dla strumieniowego przesyłania dźwięku powinieneś używać własnych niestandardowych odtwarzaczy z usługami Android –

+0

Świetne pytanie ... niestety Android MediaPlayer jest jednym z najgorszych programów, jakie kiedykolwiek napisano. – StackOverflowed

Odpowiedz

1

W moim przypadku używam FFMPEG z OpenSL ES. Wadą jest złożoność. Musisz znać wiele rzeczy: JNI, OpenSL, FFMPEG. Trudno jest również debugować (w porównaniu z czystą aplikacją Android java). W twoim przypadku proponuję wypróbować niski poziom Media API. Jedyną rzeczą jest brak przykładów. Ale istnieje unit test, który pokazuje, w jaki sposób możesz obsłużyć dźwięk (musisz zmienić odniesienie InputStream - linia 82).

+1

Myślałem o FFMPEG, ale będzie to ostatnia rzecz, którą spróbuję, ponieważ jest bardzo trudna w użyciu. Dzięki za skierowanie mnie na Media API, spróbuję tego. – darja