2013-05-06 49 views
8

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

朴素使用的MediaPlayer的

喜欢的东西:

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

(或prepareAsync,不管)

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

实现自定义缓冲

再就是成为一个想法,通过块下载媒体文件,将它们组合成一个本地文件并播放该文件。由于连接不好,下载整个文件可能会很慢,所以最初可以下载足够的文件,然后开始播放并继续下载和附加本地文件。此外,我们获得缓存功能。

听起来像一个计划,但它并不总是工作。它在HTC Sensation XE上完美运行,但在完成这个首发片后并没有停止4.1平板电脑播放。不知道,为什么如此。我问过question这个问题,但没有收到答案。

使用两个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帧seekTo的情况下,并开始工作更好。但这只是一种感觉,我不知道解释。

StreamingMediaPlayer

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

它是一个解决方案大家使用?

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

因此,问题

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

+0

非常好的问题...它也在过去的2个星期里困扰着我......但我需要它从一个设备到另一个设备的视频流...... – 2013-05-11 08:26:46

+0

音频流你应该使用你自己的定制播放器与Android服务 – 2013-07-12 14:28:55

+0

很好的问题......不幸的是,Android的MediaPlayer是有史以来最糟糕的软件之一。 – StackOverflowed 2013-11-04 04:46:18

回答

1

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

+1

我曾想过FFMPEG,但这将是我将尝试的最后一件事,因为它非常难以使用。感谢您在Media API上指点我,我会试试这个。 – darja 2013-05-07 07:16:35