33 votes

MediaPlayer bégaie au début de la lecture mp3

J'ai eu un problème de lecture d'un fichier mp3 stockés dans une ressource brute: lorsque le fichier de la première commence à jouer, il génère peut-être un quart de seconde de son, puis redémarre. (Je sais que c'est fondamentalement un double du problème décrit ici, mais la solution proposée n'a pas fonctionné pour moi.) J'ai essayé plusieurs choses et ont fait des progrès sur le problème, mais il n'est pas totalement fixe.

Voici comment je me suis mise à lire un fichier:

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 je quitte l'activité (qui appelle mPlayer.release()) et de revenir à elle (la création d'un nouveau lecteur multimédia), le bégaiement est généralement (mais pas toujours), les partis—à condition que je charge le même fichier audio. J'ai essayé un couple de choses que ne fait pas de différence:

  • Charger le fichier audio comme un atout plutôt que comme une ressource.
  • Créer le lecteur multimédia à l'aide de MediaPlayer.create(getContext(), mAudioId) et ignorer les appels à l' setDataSource(...) et prepare().

Ensuite, j'ai remarqué que LogCat montre toujours cette ligne à environ le temps que la lecture commence:

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

Il m'a demande si le bégaiement est en raison de l'apparente rebuffering. Cela m'a conduit à essayer quelque chose d'autre:

  • Après l'appel de prepare(), appelez - mPlayer.start() et appeler immédiatement mPlayer.pause().

À mon agréable surprise, ça a eu un grand effet. Une grande partie de la bégaiement est parti, plus aucun son (que je peux entendre) est effectivement joué à ce point dans le processus.

Cependant, il bégaie toujours de temps en temps quand je l'appelle mPlayer.start() pour de vrai. De Plus, cela semble être un énorme bidouille. Est-il possible de tuer ce problème complètement et proprement?

MODIFIER Plus d'infos; vous ne savez pas si liés. Si je l'appelle, pause() au cours de la lecture, demander à une position antérieure, et appelez - start() de nouveau, j'entends un peu court (~1/4 s) de sons supplémentaires, d'où il a été interrompu avant qu'il ne commence à jouer à la nouvelle position. Cela semble plus de mise en mémoire tampon des problèmes.

Aussi, le bégaiement (en pause et en mémoire tampon) les problèmes apparaissent sur les émulateurs de 1,6 à 3.0.

69voto

Reuben Scratton Points 22314

Autant que je sache, les tampons qui MediaPlayer crée en interne sont pour le stockage de décompressé échantillons, pas pour le stockage de prefetched données compressées. Je soupçonne votre bégaiement vient de I/O lenteur qu'il se charge plus MP3 de données pour la décompression.

J'ai récemment eu à résoudre un problème similaire avec la lecture vidéo. Grâce à MediaPlayer étant pas en mesure de jouer un arbitraire InputStream (l'API est étrangement boiteux) la solution je suis venu avec était d'écrire un petit cours de processus de serveur pour servir des fichiers locaux (sur la carte SD) via HTTP. MediaPlayer charge ensuite via une URI de la forme http://127.0.0.1:8888/videofilename.

EDIT:

Ci-dessous est la StreamProxy classe-je utiliser pour nourrir le contenu dans un lecteur multimédia instance. L'utilisation de base est que vous instanciez, start (), et votre lecteur multimédia aller avec quelque chose comme MediaPlayer.setDataSource("http://127.0.0.1:8888/localfilepath");

Je note que c'est plutôt expérimental et n'est probablement pas entièrement exempt de bogues. Il a été écrit pour résoudre un problème similaire à la vôtre, à savoir que MediaPlayer ne peut pas lire un fichier qui est également en cours de téléchargement. De la diffusion d'un fichier localement dans cette façon fonctionne autour de cette restriction (c'est à dire, j'ai un fil de télécharger le fichier alors que la StreamProxy insère dans 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;
        }

    }
}

3voto

Matt Bishop Points 776

Serait à l'aide d' prepareAsync et de répondre à l' setOnPreparedListener vous conviennent le mieux? En fonction de votre activité flux de travail, lorsque l' MediaPlayer est d'abord initialisé, vous pouvez attribuer la préparation de l'écouteur , puis appelez mPlayer.prepareAsync() plus tard, une fois que vous êtes en fait le chargement de la ressource, puis démarrez la lecture. J'utilise quelque chose de similaire, mais pour un réseau de diffusion de ressources:

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

Il y a évidemment plus d'une solution complète (gestion des erreurs, etc.) mais je pense que cela doit fonctionner comme un bon exemple pour démarrer à partir de ce que vous pouvez tirer le streaming de.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X