MediaPlayer 在开始播放 mp3 时卡顿
我在播放存储在原始资源中的 mp3 文件时遇到问题:当文件第一次开始播放时,它可能会生成四分之一秒的声音,然后重新启动。 (我知道这基本上是此处描述的问题的重复,但那里提供的解决方案还没有' t为我工作。)我已经尝试了几件事并在问题上取得了一些进展,但它还没有完全解决。
以下是我设置播放文件的方式:
mPlayer.reset();
try {
AssetFileDescriptor afd = getResources().openRawResourceFd(mAudioId);
if (afd == null) {
Toast.makeText(mOwner, "Could not load sound.",
Toast.LENGTH_LONG).show();
return;
}
mPlayer.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getLength());
afd.close();
mPlayer.prepare();
} catch (Exception e) {
Log.d(LOG_TAG, "Could not load sound.", e);
Toast.makeText(mOwner, "Could not load sound.", Toast.LENGTH_LONG)
.show();
}
如果我退出 Activity(调用 mPlayer.release()
)并返回到它(创建一个新的 MediaPlayer),则卡顿通常是(但并不总是)消失了——假设我加载相同的声音文件。我尝试了一些没有什么区别的事情:
- 将声音文件加载为资产而不是资源。
- 使用
MediaPlayer.create(getContext(), mAudioId)
创建 MediaPlayer 并跳过对setDataSource(...)
和prepare()
的调用。
然后我注意到 LogCat 总是在播放开始时显示这一行:
DEBUG/AudioSink(37): bufferCount (4) is too small and increased to 12
这让我想知道卡顿是否是由于明显的重新缓冲造成的。这导致我尝试其他方法:
- 调用
prepare()
后,调用mPlayer.start()
并立即调用mPlayer.pause()
。
令我惊喜的是,这产生了很大的效果。大部分的卡顿现象都消失了,而且在这个过程中的那个时刻实际上没有播放任何声音(我能听到)。
然而,当我真正调用 mPlayer.start() 时,它仍然时不时地卡住。另外,这似乎是一个巨大的混乱。有什么办法可以彻底、干净地解决这个问题吗?
编辑更多信息;不确定是否相关。如果我在播放期间调用 pause()
,寻找较早的位置,然后再次调用 start()
,我会听到一小段(~1/4 秒)额外的声音声音从暂停处开始播放,然后在新位置开始播放。这似乎表明存在更多缓冲问题。
此外,从 1.6 到 3.0 的模拟器上都会出现卡顿(和暂停的缓冲区)问题。
I've been having a problem playing an mp3 file stored in a raw resource: when the file first starts playing, it generates perhaps a quarter of a second of sound and then restarts. (I know that this is basically a duplicate of the problem described here, but the solution offered there hasn't worked for me.) I have tried several things and have made some progress on the problem, but it isn't totally fixed.
Here's how I'm setting up to play a file:
mPlayer.reset();
try {
AssetFileDescriptor afd = getResources().openRawResourceFd(mAudioId);
if (afd == null) {
Toast.makeText(mOwner, "Could not load sound.",
Toast.LENGTH_LONG).show();
return;
}
mPlayer.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getLength());
afd.close();
mPlayer.prepare();
} catch (Exception e) {
Log.d(LOG_TAG, "Could not load sound.", e);
Toast.makeText(mOwner, "Could not load sound.", Toast.LENGTH_LONG)
.show();
}
If I exit the activity (which calls mPlayer.release()
) and come back to it (creating a new MediaPlayer), the stutter is usually (but not always) gone—provided I load the same sound file. I tried a couple of things that made no difference:
- Load the sound file as an asset instead of as a resource.
- Create the MediaPlayer using
MediaPlayer.create(getContext(), mAudioId)
and skip the calls tosetDataSource(...)
andprepare()
.
Then I noticed that LogCat always shows this line at about the time that playback starts:
DEBUG/AudioSink(37): bufferCount (4) is too small and increased to 12
It got me wondering if the stuttering is due to the apparent rebuffering. This led me to try something else:
- After calling
prepare()
, callmPlayer.start()
and immediately callmPlayer.pause()
.
To my pleasant surprise, this had a big effect. A great deal of the stutter is gone, plus no sound (that I can hear) is actually played at that point in the process.
However, it still stutters from time to time when I call mPlayer.start()
for real. Plus, this seems like a huge kludge. Is there any way to kill this problem completely and cleanly?
EDIT More info; not sure if related. If I call pause()
during playback, seek to an earlier position, and call start()
again, I hear a short bit (~1/4 sec) of additional sound from where it was paused before it starts playing at the new position. This seems to point to more buffering problems.
Also, the stuttering (and paused buffer) problems show up on emulators from 1.6 through 3.0.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
AFAIK MediaPlayer 内部创建的缓冲区用于存储解压缩的样本,而不是用于存储预取的压缩数据。我怀疑您的口吃是由于 I/O 缓慢造成的,因为它会加载更多 MP3 数据进行解压缩。
我最近必须解决视频播放方面的类似问题。由于
MediaPlayer
无法播放任意InputStream
(API 奇怪地蹩脚),我想出的解决方案是编写一个小型进程内网络服务器来提供本地服务通过 HTTP 传输文件(在 SD 卡上)。MediaPlayer
然后通过 http://127.0.0.1:8888/videofilename 形式的 URI 加载它。编辑:
下面是我用来将内容提供给 MediaPlayer 实例的 StreamProxy 类。基本用法是实例化它,start() 它,然后将媒体播放器设置为类似
MediaPlayer.setDataSource("http://127.0.0.1:8888/localfilepath");
I应该注意的是,它是相当实验性的,可能并不完全没有错误。它的编写是为了解决与您类似的问题,即 MediaPlayer 无法播放正在下载的文件。以这种方式在本地流式传输文件可以解决该限制(即我有一个线程下载文件,而 StreamProxy 将其输入媒体播放器)。
AFAIK the buffers that MediaPlayer creates internally are for storing decompressed samples, not for storing prefetched compressed data. I suspect your stuttering comes from I/O slowness as it loads more MP3 data for decompression.
I recently had to solve a similar problem with video playback. Thanks to
MediaPlayer
being unable to play an arbitraryInputStream
(the API is strangely lame) the solution I came up with was to write a small in-process webserver for serving up local files (on the SD card) over HTTP.MediaPlayer
then loads it via a URI of the form http://127.0.0.1:8888/videofilename.EDIT:
Below is the StreamProxy class I use to feed content into a MediaPlayer instance. The basic use is that you instantiate it, start() it, and set your media player going with something like
MediaPlayer.setDataSource("http://127.0.0.1:8888/localfilepath");
I should note that it is rather experimental and probably not entirely bug-free. It was written to solve a similar problem to yours, namely that MediaPlayer cannot play a file that is also being downloaded. Streaming a file locally in this way works around that restriction (i.e. I have a thread downloading the file while the StreamProxy feeds it into mediaplayer).
使用
prepareAsync
和响应setOnPreparedListener
是否更适合您?根据您的活动工作流程,首次初始化MediaPlayer
时,您可以设置 准备侦听器,然后在实际加载资源后调用mPlayer.prepareAsync()
,然后在此处开始播放。我使用类似的东西,尽管对于基于网络的流媒体资源:显然,完整的解决方案还有更多内容(错误处理等),但我认为这应该作为一个很好的例子,您可以从其中拉出流媒体。
Would using
prepareAsync
and responding tosetOnPreparedListener
suit you better? Depending on your activity workflow, when theMediaPlayer
is first initialized you could set the preparation listener and then callmPlayer.prepareAsync()
later once you're actually loading the resource, then start playback there. I use something similar, albeit for a network-based streaming resource:There's obviously more to a complete solution (error-handling, etc.) but I think this should work as a good example to start from that you can pull the streaming out of.