音频流的最佳实践

我正在编写一个从远程服务器播放音频的应用程序。 我尝试了几种实现流式音频的方法,但它们对我来说都不够好。 这就是我尝试过的:

天真地使用MediaPlayer

就像是:

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

(或者prepareAsync ,不管)

但是在播放远程内容时,标准的MediaPlayer非常不稳定。 它经常会掉落或停止播放,我无法处理。 另一方面,我想实现媒体缓存。 但我还没有find任何方法从MediaPlayer获取缓冲内容以将其保存在设备上的某个位置。

实现自定义缓冲

然后有一个想法是按块下载媒体文件,将它们组合成一个本地文件并播放该文件。 由于连接不良,下载整个文件可能会很慢,因此可以下载足够的初始文件,然后开始播放并继续下载和附加本地文件。 此外,我们还获得了缓存function。

听起来像一个计划,但它并不总是有效。 它完美地适用于HTC Sensation XE,但在完成这个初始作品后没有停止4.1平板电脑播放。 不知道,为什么会这样。 我问过这个问题 ,但没有得到答案。

使用两个MediaPlayers

我创建了两个MediaPlayer实例并试图让它们互相改变。 逻辑如下:

  • 开始下载初始媒体
  • 下载后,通过currentMediaPlayer开始播放。 其余媒体继续下载
  • 当几乎播放下载的片段(完成前1秒)时,准备具有相同源文件的secondaryMediaPlayer(在播放期间附加)
  • 在currentMediaPlayer结束前261毫秒 – 暂停它,启动辅助,将辅助设置为当前,安排准备下一个辅助播放器。

来源:

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

再次 – 声音,就像一个计划,但工作不完美。 切换MediaPlayers的时刻非常可以接受。 在这里我有相反的情况:在4.1平板电脑上没关系,但在HTC Sensation上有明显的滞后。

我还试图实现不同的下载技术。 我已经实现了10Kb块和MP3帧的下载。 我不确切知道,但似乎在MP3帧搜索并开始工作更好。 但这只是一种感觉,我不知道解释。

StreamingMediaPlayer

谷歌搜索时我多次看到这个词,并发现了这个实现: https : //code.google.com/p/mynpr/source/browse/trunk/mynpr/src/com/webeclubbin/mynpr/StreamingMediaPlayer.java?r = 18

这是每个人都使用的解决方案吗?

如果是的话,那很难过,因为它对我来说也不好用。 我没有看到任何新的想法在实施。

所以,问题

你们如何在你的应用程序中实现音频流? 我不相信我是唯一一个遇到这样问题的人。 应该有一些好的做法。

在我的情况下,我使用FFMPEG与OpenSL ES。 缺点是复杂性。 你必须熟悉很多东西:JNI,OpenSL,FFMPEG。 它也很难调试(与纯Java android应用程序相比)。 在您的情况下,我建议您尝试低级Media API 。 唯一的缺点是缺乏例子。 但是有一个unit testing显示你如何处理音频(你需要改变InputStream参考 – 第82行)。