MediaPlayer在播放开始时口吃

播放保存在原始资源中的MP3文件时,我遇到了一个问题:当文件开始播放时,它可能会产生四分之一秒的声音,然后重新启动。 (我知道这基本上是这里描述的问题的一个重复,但是提供的解决scheme并没有为我工作。)我已经尝试了几件事情,并在这个问题上取得了一些进展,但并不完全固定。

以下是我如何设置播放文件:

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

如果我退出活动(调用mPlayer.release() )并返回它(创build一个新的MediaPlayer),则通常(但不是总是)结束, 只要我加载相同的声音文件。 我尝试了几个没有任何区别的东西:

  • 将声音文件加载为资源而不是资源。
  • 使用MediaPlayer.create(getContext(), mAudioId)创buildMediaPlayer并跳过对setDataSource(...)prepare()的调用。

然后我注意到LogCat总是在播放开始的时候显示这行:

 DEBUG/AudioSink(37): bufferCount (4) is too small and increased to 12 

这让我想知道这个口吃是否是由于明显的缓解。 这导致我尝试别的东西:

  • 在调用prepare() ,调用mPlayer.start()并立即调用mPlayer.pause()

令我惊喜的是,这有很大的作用。 大部分的口吃都消失了,加上没有声音(我能听到的)实际上是在这个过程中的那个时候发挥的。

但是,当我打电话给mPlayer.start()时候,它仍然会出现mPlayer.start() 。 另外,这似乎是一个巨大的kludge。 有没有办法彻底干净地解决这个问题?

编辑更多信息; 不确定是否相关。 如果我在回放过程中调用pause() ,请先find一个位置,然后再次调用start() ,我会在它开始播放新位置之前听到暂停的位置(〜1/4秒) 。 这似乎指向更多的缓冲问题。

此外,在从1.6到3.0的模拟器上出现口吃(和暂停的缓冲区)问题。

AFAIK MediaPlayer内部创build的缓冲区用于存储解压缩的样本,而不用于存储预取的压缩数据。 我怀疑你的口吃来自I / O缓慢,因为它加载更多的MP3数据进行解压缩。

我最近不得不解决与video播放类似的问题。 由于MediaPlayer无法播放任意的InputStream (这个API很奇怪),我提出的解决scheme是编写一个小型的进程内Web服务器,通过HTTP提供本地文件(在SD卡上)。 然后MediaPlayer通过http://127.0.0.1:8888/videofilename格式的URI加载它。;

编辑:

以下是我用来将内容提供给MediaPlayer实例的StreamProxy类。 基本的用法是你实例化它,启动()它,并设置你的媒体播放器像MediaPlayer.setDataSource("http://127.0.0.1:8888/localfilepath");

我应该指出,这是相当实验性的,可能不是完全没有错误的。 它是为解决与您相似的问题而编写的,即MediaPlayer无法播放同时正在下载的文件。 以这种方式在本地stream式传输文件可以解决这个限制(也就是说,在StreamProxy将其送入mediaplayer时,我有一个线程正在下载文件)。

 import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import android.os.AsyncTask; import android.os.Looper; import android.util.Log; public class StreamProxy implements Runnable { private static final int SERVER_PORT=8888; private Thread thread; private boolean isRunning; private ServerSocket socket; private int port; public StreamProxy() { // Create listening socket try { socket = new ServerSocket(SERVER_PORT, 0, InetAddress.getByAddress(new byte[] {127,0,0,1})); socket.setSoTimeout(5000); port = socket.getLocalPort(); } catch (UnknownHostException e) { // impossible } catch (IOException e) { Log.e(TAG, "IOException initializing server", e); } } public void start() { thread = new Thread(this); thread.start(); } public void stop() { isRunning = false; thread.interrupt(); try { thread.join(5000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void run() { Looper.prepare(); isRunning = true; while (isRunning) { try { Socket client = socket.accept(); if (client == null) { continue; } Log.d(TAG, "client connected"); StreamToMediaPlayerTask task = new StreamToMediaPlayerTask(client); if (task.processRequest()) { task.execute(); } } catch (SocketTimeoutException e) { // Do nothing } catch (IOException e) { Log.e(TAG, "Error connecting to client", e); } } Log.d(TAG, "Proxy interrupted. Shutting down."); } private class StreamToMediaPlayerTask extends AsyncTask<String, Void, Integer> { String localPath; Socket client; int cbSkip; public StreamToMediaPlayerTask(Socket client) { this.client = client; } public boolean processRequest() { // Read HTTP headers String headers = ""; try { headers = Utils.readTextStreamAvailable(client.getInputStream()); } catch (IOException e) { Log.e(TAG, "Error reading HTTP request header from stream:", e); return false; } // Get the important bits from the headers String[] headerLines = headers.split("\n"); String urlLine = headerLines[0]; if (!urlLine.startsWith("GET ")) { Log.e(TAG, "Only GET is supported"); return false; } urlLine = urlLine.substring(4); int charPos = urlLine.indexOf(' '); if (charPos != -1) { urlLine = urlLine.substring(1, charPos); } localPath = urlLine; // See if there's a "Range:" header for (int i=0 ; i<headerLines.length ; i++) { String headerLine = headerLines[i]; if (headerLine.startsWith("Range: bytes=")) { headerLine = headerLine.substring(13); charPos = headerLine.indexOf('-'); if (charPos>0) { headerLine = headerLine.substring(0,charPos); } cbSkip = Integer.parseInt(headerLine); } } return true; } @Override protected Integer doInBackground(String... params) { long fileSize = GET CONTENT LENGTH HERE; // Create HTTP header String headers = "HTTP/1.0 200 OK\r\n"; headers += "Content-Type: " + MIME TYPE HERE + "\r\n"; headers += "Content-Length: " + fileSize + "\r\n"; headers += "Connection: close\r\n"; headers += "\r\n"; // Begin with HTTP header int fc = 0; long cbToSend = fileSize - cbSkip; OutputStream output = null; byte[] buff = new byte[64 * 1024]; try { output = new BufferedOutputStream(client.getOutputStream(), 32*1024); output.write(headers.getBytes()); // Loop as long as there's stuff to send while (isRunning && cbToSend>0 && !client.isClosed()) { // See if there's more to send File file = new File(localPath); fc++; int cbSentThisBatch = 0; if (file.exists()) { FileInputStream input = new FileInputStream(file); input.skip(cbSkip); int cbToSendThisBatch = input.available(); while (cbToSendThisBatch > 0) { int cbToRead = Math.min(cbToSendThisBatch, buff.length); int cbRead = input.read(buff, 0, cbToRead); if (cbRead == -1) { break; } cbToSendThisBatch -= cbRead; cbToSend -= cbRead; output.write(buff, 0, cbRead); output.flush(); cbSkip += cbRead; cbSentThisBatch += cbRead; } input.close(); } // If we did nothing this batch, block for a second if (cbSentThisBatch == 0) { Log.d(TAG, "Blocking until more data appears"); Thread.sleep(1000); } } } catch (SocketException socketException) { Log.e(TAG, "SocketException() thrown, proxy client has probably closed. This can exit harmlessly"); } catch (Exception e) { Log.e(TAG, "Exception thrown from streaming task:"); Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage()); e.printStackTrace(); } // Cleanup try { if (output != null) { output.close(); } client.close(); } catch (IOException e) { Log.e(TAG, "IOException while cleaning up streaming task:"); Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage()); e.printStackTrace(); } return 1; } } } 

将使用prepareAsync和响应setOnPreparedListener适合你更好? 根据您的活动工作stream程,当MediaPlayer第一次初始化时,您可以设置准备监听器 ,然后在实际加载资源后再调用mPlayer.prepareAsync() ,然后在那里开始播放。 我使用类似的东西,尽pipe是基于networking的stream媒体资源:

 MediaPlayer m_player; private ProgressDialog m_progressDialog = null; ... try { if (m_player != null) { m_player.reset(); } else { m_player = new MediaPlayer(); } m_progressDialog = ProgressDialog .show(this, getString(R.string.progress_dialog_please_wait), getString(R.string.progress_dialog_buffering), true); m_player.setOnPreparedListener(this); m_player.setAudioStreamType(AudioManager.STREAM_MUSIC); m_player.setDataSource(someSource); m_player.prepareAsync(); } catch (Exception ex) { } ... public void onPrepared(MediaPlayer mp) { if (m_progressDialog != null && m_progressDialog.isShowing()) { m_progressDialog.dismiss(); } m_player.start(); } 

显然更多的是一个完整的解决scheme(error handling等),但我认为这应该作为一个很好的例子,从你可以拉出stream出。