2013-05-06 14 views
8

Ich schreibe eine Anwendung zum Abspielen von Audio vom Remote-Server. Ich habe verschiedene Möglichkeiten versucht, Streaming-Audio zu implementieren, aber alle sind nicht gut genug für mich. Das ist, was ich versucht habe:Best Practices für Audio-Streaming

Naive von Mediaplayer mit

Etwas wie:

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

(oder prepareAsync, egal)

Aber Standard MediaPlayer ist ziemlich instabil, wenn die Fern spielen Inhalt. Es fällt oft ab oder stoppt die Wiedergabe und ich kann das nicht verarbeiten. Auf der anderen Seite möchte ich Media Caching implementieren. Ich habe jedoch keine Möglichkeit gefunden, gepufferten Inhalt vom MediaPlayer zu holen, um ihn irgendwo auf dem Gerät zu speichern.

Implementierung eigene Puffer

Dann wurde es eine Idee Mediendatei von Chunks herunterladen, um sie in einer lokalen Datei kombinieren und diese Datei spielen. Das Herunterladen der gesamten Datei kann wegen schlechter Verbindung langsam sein, daher ist es in Ordnung, zunächst genug Stück herunterzuladen, dann die Wiedergabe zu starten und das Herunterladen und Anhängen der lokalen Datei fortzusetzen. Außerdem erhalten wir Caching-Funktionalität.

Klingt wie ein Plan, aber es hat nicht immer funktioniert. Es funktioniert perfekt auf HTC Sensation XE, aber nicht auf 4.1 Tablet-Wiedergabe gestoppt nach Abschluss dieses ersten Stück. Weiß nicht, warum ist das so. Ich habe question darüber gefragt, aber keine Antworten erhalten.

Verwendung von zwei Mediaplayers

Ich habe zwei Mediaplayer-Instanzen erstellt und versucht, sie einander zu machen ändern. Die Logik ist folgende:

  • starten Anfangsstück Medien
  • Download Wenn es heruntergeladen haben, starten Sie die Wiedergabe über currentMediaPlayer. Der Rest der Medien weiterhin Download
  • Wenn heruntergeladen Stück fast gespielt wird (1 sec vor Ende), bereiten secondaryMediaPlayer mit der gleichen Quelldatei
  • 261 ms vor dem Abgang von currentMediaPlayer (wie es bei der Wiedergabe angehängt wurde) - es pausiert , starte sekundär, setze sekundär als aktuell, plane die Vorbereitung des nächsten sekundären Spielers.

Die Quelle:

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 - Töne, wie ein Plan funktioniert, aber nicht perfekt. Die Momente des Wechselns von MediaPlayern sind durchaus hörbar. Hier habe ich entgegengesetzte Situation: auf 4.1 Tablet ist es ok, aber auf HTC Sensation gibt es deutliche Verzögerungen.

Ich habe auch versucht, verschiedene Download-Techniken zu implementieren. Ich habe Download von 10Kb Chunks und von MP3-Frames implementiert. Ich weiß es nicht genau, aber es scheint, dass im Falle von MP3-Frames suchen und besser arbeiten. Aber es ist nur ein Gefühl, ich weiß keine Erklärung.

StreamingMediaPlayer

Ich sah mehrmals auf dieses Wort während googeln und fand diese Implementierung: https://code.google.com/p/mynpr/source/browse/trunk/mynpr/src/com/webeclubbin/mynpr/StreamingMediaPlayer.java?r=18

Es wird eine Lösung jeder benutzen?

Wenn ja, ist es traurig, denn es funktioniert auch nicht gut für mich. Und ich sehe keine neuen Ideen in der Umsetzung.

Also, die Frage

Wie implementiere ihr Audio-Streaming in Ihren Anwendungen? Ich glaube nicht, dass ich die einzige Person bin, die solche Probleme hatte. Es sollte einige gute Praktiken geben.

+0

Sehr nette Frage ... Es nervt mich auch für die letzten 2 Wochen ... Aber ich brauche es für Video-Streaming von einem Gerät zum anderen ... –

+0

für Audio-Streaming sollten Sie Ihre eigenen benutzerdefinierten Player mit Android-Diensten verwenden –

+0

Gute Frage ... Leider ist der MediaPlayer von Android einer der schlechtesten Softwares, die je geschrieben wurden. – StackOverflowed

Antwort

1

In meinem Fall verwende ich FFMPEG mit OpenSL ES. Der Nachteil ist Komplexität. Sie müssen mit vielen Dingen vertraut sein: JNI, OpenSL, FFMPEG. Es ist auch schwer zu debuggen (im Vergleich mit reinem Java Android App). In Ihrem Fall schlage ich vor, Sie versuchen, niedrige Ebene Media API. Das einzige ist der Mangel an Beispielen. Aber es gibt eine unit test, die zeigt, wie Sie mit Audio umgehen können (Sie müssen ändern InputStream Referenz - Zeile 82).

+1

Ich dachte über FFMPEG nach, aber es wird das allerletzte sein, was ich versuchen werde, weil es sehr schwer zu benutzen ist. Vielen Dank, dass Sie mich auf Media API hingewiesen haben. Ich werde es versuchen. – darja