MediaPlayer tartamudea al inicio de la reproducción de mp3

He tenido un problema al reproducir un archivo mp3 almacenado en un recurso sin formato: cuando el archivo comienza a reproducirse por primera vez, genera quizás un cuarto de segundo de sonido y luego se reinicia. (Sé que esto es básicamente un duplicado del problema descrito aquí , pero la solución ofrecida allí no ha funcionado para mí.) He intentado varias cosas y he hecho algunos progresos en el problema, pero no es totalmente fijo.

He aquí cómo me estoy configurando para reproducir un archivo:

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

Si salgo de la actividad (que llama a mPlayer.release() ) y vuelvo a ella (creando un nuevo MediaPlayer), el tartamudeo es usualmente (pero no siempre) ido – siempre que cargue el mismo archivo de sonido. Intenté un par de cosas que no hicieron ninguna diferencia:

  • Cargue el archivo de sonido como un recurso en lugar de como un recurso.
  • Crear el MediaPlayer utilizando MediaPlayer.create(getContext(), mAudioId) y omitir las llamadas a setDataSource(...) y prepare() .

Entonces me di cuenta de que LogCat siempre muestra esta línea en el momento en que comienza la reproducción:

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

Me pregunto si la tartamudez se debe a la aparente rebeldía. Esto me llevó a intentar algo más:

  • Después de llamar a prepare() , llame a mPlayer.start() e inmediatamente llame a mPlayer.pause() .

Para mi sorpresa agradable, esto tuvo un gran efecto. Una gran parte de la tartamudez se ha ido, además de ningún sonido (que puedo oír) se juega realmente en ese punto en el proceso.

Sin embargo, todavía tartamudea de vez en cuando cuando llamo mPlayer.start() de verdad. Además, esto parece un enorme kludge. ¿Hay alguna manera de matar este problema de manera completa y limpia?

EDIT Más información; No está seguro si está relacionado. Si llamo a pause() durante la reproducción, busco una posición anterior, y llamo start() nuevo, oigo un bit corto (~ 1/4 s) de sonido adicional desde donde se pausó antes de que comience a tocar en la nueva posición . Esto parece apuntar a más problemas de amortiguamiento.

Además, los problemas de tartamudez (y pausado) aparecen en los emuladores de 1.6 a 3.0.

AFAIK las memorias intermedias que MediaPlayer crea internamente son para almacenar muestras descomprimidas, no para almacenar datos comprimidos prefetched. Sospecho que tu tartamudeo viene de la lentitud de I / O ya que carga más datos MP3 para la descompresión.

Recientemente tuve que resolver un problema similar con la reproducción de vídeo. Gracias a MediaPlayer no poder reproducir un InputStream arbitrario (la API es extrañamente cojo) la solución que me ocurrió fue escribir un pequeño servidor web en proceso para servir archivos locales (en la tarjeta SD) a través de HTTP. MediaPlayer carga a través de un URI de la forma http://127.0.0.1:8888/videofilename .

EDITAR:

A continuación se muestra la clase StreamProxy que utilizo para alimentar contenido en una instancia de MediaPlayer. El uso básico es que instáncielo, inícielo y configure su reproductor multimedia con algo como MediaPlayer.setDataSource("http://127.0.0.1:8888/localfilepath");

Debo señalar que es más bien experimental y probablemente no totalmente libre de errores. Se escribió para resolver un problema similar al suyo, a saber, que MediaPlayer no puede reproducir un archivo que también se está descargando. Fluir un archivo localmente de esta manera funciona alrededor de esa restricción (es decir, tengo un subproceso de descarga del archivo mientras StreamProxy lo alimenta en 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; } } } 

¿Utilizar prepareAsync y responder a setOnPreparedListener conviene mejor? Dependiendo del flujo de trabajo de su actividad, al inicializar el MediaPlayer , puede establecer el listener de preparación y, a continuación, llamar a mPlayer.prepareAsync() más adelante una vez que esté cargando el recurso, inicie la reproducción allí. Yo uso algo similar, aunque para un recurso de streaming basado en red:

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

Hay obviamente más que una solución completa (manejo de errores, etc.), pero creo que esto debería funcionar como un buen ejemplo para empezar desde el que puedes sacar el streaming.

  • Directorio para almacenar archivos de audio en Android
  • Cambio de velocidad de reproducción / tempo de Android
  • Cómo reproducir video de YouTube en mi aplicación de Android?
  • Lista de reproducción de Youtube mostrada en ListView
  • RTSP Stream en VideoView en Aplicación para Android
  • Ejemplo de streaming de vídeo de Android
  • Android, Java - Renderización de un vídeo utilizando marcos de mapa de bits para invertir un vídeo (Xuggler)
  • Cómo simular los botones de control de medios de hardware en un emulador de Android
  • La animación de un elemento de vista de lista tiene efecto en varias filas
  • Android Superpowered SDK Grabación y reproducción simultáneamente
  • Cómo reproducir archivos mp3 utilizando sonic NDK en Android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.