42

我使用套接字作为 MediaPlayer 的代理,因此我可以在将 mp3 音频写入套接字之前下载和解密它。这类似于 NPR 新闻应用程序中显示的示例,但是我将其用于所有 Android 版本 2.1 - 4 atm。

NPR StreamProxy 代码 - http://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java

我的问题是 2.1 - 2.3 的播放速度很快,但在 Android 4.0 ICS 中,MediaPlayer 在触发 onPrepared 侦听器之前缓冲了太多数据。

在 onPrepared() 之前写入 Socket OutputStream 的示例数据量:

在带有 2.3.4 的 SGS2 上 - 约 133920 字节后的 onPrepared()

在带有 4.0.4 的 Nexus S 上 - onPrepared() 在 ~ 961930 字节之后

这也发生在 Galaxy Nexus 上。

奇怪的是,4.0 仿真器缓冲的数据不如 4.0 设备多。有人在 ICS 上使用 MediaPlayer 遇到过类似问题吗?

编辑

这是代理写入套接字的方式。在此示例中,它来自从文件加载的 CipherInputStream,但从 HttpResponse 加载时也会发生同样的情况。

final Socket client = (setup above)

// encrypted file input stream
final CipherInputStream inputStream = getInputStream(file);

// setup the socket output stream
final OutputStream output =  client.getOutputStream();

// Writing the header
final String httpHeader = buildHttpHeader(file.length());
final byte[] buffer = httpHeader.getBytes("UTF-8");
output.write(buffer, 0, buffer.length);

int writtenBytes = 0;
int readBytes;
final byte[] buff = new byte[1024 * 12]; // 12 KB

while (mIsRunning && (readBytes = inputStream.read(buff)) != -1) {
    output.write(buff, 0, readBytes);
    writtenBytes += readBytes;
}

output.flush();
output.close();

在音频之前写入 MediaPlayer 的 HTTP 标头。

private String buildHttpHeader(final int contentLength) {
    final StringBuilder sb = new StringBuilder();

    sb.append("HTTP/1.1 200 OK\r\n");
    sb.append("Content-Length: ").append(contentLength).append("\r\n");
    sb.append("Accept-Ranges: bytes\r\n" );
    sb.append("Content-Type: audio/mpeg\r\n");
    sb.append("Connection: close\r\n" );
    sb.append("\r\n");

    return sb.toString();
}

我四处寻找替代实现,但由于我加密了音频并且 MediaPlayer 不支持 InputStreams 作为数据源,我唯一的选择(我认为..)是使用这样的代理。

同样,这在 Android 2.1 - 2.3 中运行良好,但在 ICS 中,MediaPlayer 在播放之前缓冲了大量此类数据。

编辑 2:

进一步的测试表明,这也是 SGS2 升级到 Android 4.0.3 后的问题。因此,MediaPlayer 的缓冲实现似乎在 4.0 中发生了重大变化。这令人沮丧,因为 API 没有提供改变行为的方法。

编辑 3:

已创建 Android 错误。请在此处添加评论并加注星 标 http://code.google.com/p/android/issues/detail?id=29870

编辑 4:

我的播放代码是相当标准的。我在 onPrepared() 方法中对 MediaPlayer 进行了 start() 调用。

mCurrentPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mCurrentPlayer.setDataSource(url);
mCurrentPlayer.prepareAsync();

仅使用 prepare() 以及 ajacian81 的推荐方式进行了尝试,但无济于事。

我应该补充一点,最近一位 Google 员工就我的问题回复了我,并确认 ICS(用于高清内容)中的缓冲区大小是故意增加的。已要求 API 开发人员添加在 MediaPlayer 上设置缓冲区大小的功能。

虽然我认为这个 API 更改请求在我出现之前就已经存在,所以我不建议任何人屏住呼吸。

4

2 回答 2

2

是否可以看到您开始()使用 MediaPlayer 的代码?

您使用的是STREAM_MUSIC音频流类型吗?

player.setAudioStreamType(AudioManager.STREAM_MUSIC);

你是否也尝试过 player.prepareAsync(); 和 player.prepare();?

我记得去年有一个类似的问题,解决方案是:启动、暂停,然后 onPrepared 到 start():

player.setAudioStreamType(AudioManager.STREAM_MUSIC); 
player.setDataSource(src); 
player.prepare(); 
player.start(); 
player.pause(); 
player.setOnPreparedListener(new OnPreparedListener() {     
@Override
                public void onPrepared(MediaPlayer mp) {
                    player.start();                
                }
          });

在这种情况下不太可能是解决方法,但是当您旋转车轮时,这可能值得一试。

于 2012-05-17T17:31:21.950 回答
0

对我来说,解决方案是将 MediaCodec 与 AudioTrack 结合使用,我在这里找到了所有我需要知道的信息:

这可能是一个解决方案:http ://www.piterwilson.com/blog/2014/03/15/mediacodec-mediaextractor-and-audiotrack-to-the-rescue/

于 2014-10-01T16:01:50.373 回答