web-dev-qa-db-fra.com

Pourquoi MediaPlayer d'Android a-t-il mis si longtemps à préparer certains flux en direct pour la lecture?

Je constate de grandes différences dans le temps qu'il faut à Android MediaPlayer pour se préparer à la lecture de flux en direct avec différents flux.

Les données matérielles

J'ai ajouté la journalisation entre prepareAsync () et le rappel onPrepared (MediaPlayer mp) et j'ai testé plusieurs flux plusieurs fois. Les temps pour chaque flux étaient très cohérents (+/- une seconde), et voici les résultats:

  1. Flux de nouvelles MPR: 27 secondes (http://newsstream1.publicradio.org:80/)
  2. Flux de musique classique MPR: 15 secondes (http://classicalstream1.publicradio.org:80/)
  3. MPR Le flux actuel: 7 secondes (http://currentstream1.publicradio.org:80/)
  4. Flux PRI: 52 secondes (http://pri-ice.streamguys.biz/pri1)

Les tests ont été réalisés sur un Nexus S avec Android 2.3.4 sur une connexion 3G (~ 1100 Kbps).

La lecture de fichiers audio MP3 sans streaming n’est pas un problème.

Voici des extraits de la façon dont je joue les flux:

Préparez MediaPlayer:

...
mediaPlayer.setDataSource(playUrl);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.prepareAsync();
...

Puis dans onPrepared (MediaPlayer mp):

mediaPlayer.start();

Pourquoi faut-il tant de temps pour préparer certains flux mais pas d'autres? Les données ci-dessus semblent suggérer que pourrait être basé sur la quantité de données mises en mémoire tampon et non sur la durée du contenu audio mis en mémoire tampon. Cela pourrait-il vraiment être?

Update: J'ai testé la diffusion en direct sur des périphériques physiques sous Android 1.6, 2.2 et 2.3.4 et des émulateurs sous 1.6, 2.1, 2.2, 2.3.1 et 2.3.3. Je ne vois que le long retard sur 2.3.3 et 2.3.4. Les versions plus anciennes commencent la lecture dans les 5 secondes.

47
Jeremy Haberman

Il semble en effet qu'il mette en mémoire tampon une quantité fixe de données plutôt qu'une quantité fixe de temps. Pour ceux qui ne connaissent pas les débits binaires de différents types de flux NPR, les données ressemblent à ceci:

  1. Flux de nouvelles MPR: 27 secondes ( http://newsstream1.publicradio.org:80/ ), 64 kbps
  2. Flux de musique classique MPR: 15 secondes ( http://classicalstream1.publicradio.org:80/ ), 128 kbps
  3. MPR Le flux actuel: 7 secondes ( http://currentstream1.publicradio.org:80/ ), 128 kbps
  4. Flux PRI: 52 secondes ( http://pri-ice.streamguys.biz/pri1 ), 32 kbps

Outre la discordance entre les deux flux à 128 kbps, il existe une très bonne corrélation entre le débit binaire et la durée de mise en mémoire tampon.

Dans tous les cas, Android est open-source, vous pouvez donc toujours regardez ce qu'il fait . Malheureusement, prepareAsync() et prepare() sont des méthodes natives et il apparaît que les événements liés à la mémoire tampon sont également distribués à partir d'un processus natif. 

Avez-vous essayé de joindre une OnBufferingUpdateListener au MediaPlayer pour obtenir des mises à jour plus détaillées sur l'état de la mémoire tampon? Il peut être intéressant de comparer la vitesse à laquelle les événements sont livrés et le pourcentage de remplissage de la mémoire tampon pour chaque événement dans les différents flux. Vous pouvez comparer cela avec les débits binaires du flux et si 4 secondes de mise en mémoire tampon à 32 Kbps remplissent la mémoire tampon du même pourcentage que 1 seconde de mise en mémoire tampon à 128 Kbps, je pense que vous aurez trouvé votre réponse.

23
aroth

Basculez MediaPlayer par FFmpegMediaPlayer fonctionne beaucoup mieux que le MediaPlayer si vous voulez tester vos flux, vous pouvez le faire via le demo qu'ils ont.

7
4gus71n

J'ai récemment débogué ce même problème avec un fournisseur audio en streaming. Le problème est lié à stagefright et aux sources de streaming de 32 kbps et moins. Nous avons effectué le même flux continu en mesurant le temps de réponse à 24, 32, 48, 64 et 128 kbps.

  • 24 -> 46 secondes pour commencer le streaming
  • 32 -> 24 secondes pour commencer le streaming
  • 48 -> 2 secondes pour commencer le streaming
  • 64 -> 2 secondes pour commencer le streaming
  • 128 -> 2 secondes pour commencer le streaming

Cela provient d'une connexion sans fil cohérente calculée en moyenne sur 10 tentatives à chaque débit. Comme Travis l'a souligné, la clé était que la scène ne pouvait pas déterminer combien de temps mettre le son en mémoire tampon. Parfois, je voyais un message 'erreur: 1, -21492389' ou plus, qui semblait planter le joueur de stagefright en silence. J'ai essayé de le localiser et je suis finalement parvenu à la conclusion que les flux très lents (inférieurs à 24 kbps) semblaient provoquer un débordement de mémoire tampon, car ils mettraient en mémoire tampon jusqu'à ce que le périphérique manque d'espace pour le flux audio.

Je voulais ajouter que OnBufferingUpdateListener ne m'a pas tiré du tout pendant tout le test. Je ne sais pas ce que c'est. Je pense que la seule façon de savoir comment se passe le chargement est d’envoyer un proxy, de la même manière que l’application NPR mentionnée ci-dessus.

4
Nick Campion

Si vous diffusez depuis Icecast, examinez le paramètre burst-size:

La taille de rafale est la quantité de données (en octets) à envoyer en rafale à un client au moment de la connexion. Comme en rafale, c'est pour remplir rapidement le fichier pré-tampon utilisé par les lecteurs multimédias. La valeur par défaut est 64 Ko, ce qui est un taille typique utilisée par la plupart des clients, le changement n’est donc généralement pas Champs obligatoires. Ce paramètre s'applique à tous les points de montage, sauf substitution dans les paramètres de montage. Assurez-vous que cette valeur est inférieure à la taille de la file d'attente, Si nécessaire, augmentez la taille de la file d'attente pour qu'elle soit plus grande que votre nombre souhaité éclaté-taille. Dans le cas contraire, le client du programme d'écoute pourrait être abandonné tentatives de connexion, en raison d'une rafale initiale menant à la connexion dépassant déjà la limite de taille de la file d'attente.

J'ai augmenté le burst-size à 131072 sur mon serveur et maintenant, mon application Android basée sur MediaPlayer lit les flux sans trop de retard.

2
Matt Harrington

J'ai essayé cela avec 10 points de données, trois rapides, 7 lents ... C'est cohérent, c'est-à-dire qu'un flux rapide est rapide et qu'un flux lent est toujours lent.

Je pense que cela est lié à la "longueur du contenu" fournie par le serveur, "Android ne sait pas combien de mémoire tampon mettre en mémoire tampon si la longueur du contenu n'est pas correctement spécifiée.

Pourrait se tromper, n’est pas allé aussi loin que le wireharking.

1
Travis Biehn

Quand j'ai eu ce problème, j'ai décidé de vérifier si le flux est disponible avant d'ouvrir le lecteur. Si vous obligez l'utilisateur à attendre longtemps et que la musique commence à fonctionner correctement (ce n'est pas le cas, mais disons que c'est ok) . Le pire des scénarios est de le faire attendre longtemps et la musique ne commence jamais! Donc, nous avons 2 situations:

  • Le scénario de diffusion en direct, comme une station de radio.
  • Le fichier mp3 enregistré qui est disponible en ligne.

Au scénario radio , nous pourrions vérifier si le port accepte les connexions (état ouvert/fermé). S'il est ouvert, préparez le lecteur pour la musique, sinon ne le préparez pas du tout.

public static boolean isLiveStreamingAvailable() {
        SocketAddress sockaddr = new InetSocketAddress(STREAMING_Host, STREAMING_PORT);
        // Create your socket
        Socket socket = new Socket();
        boolean online = true;
        // Connect with 10 s timeout
        try {
            socket.connect(sockaddr, 10000);
        } catch (SocketTimeoutException stex) {
            // treating timeout errors separately from other io exceptions
            // may make sense
            return false;
        } catch (IOException iOException) {
            return false;
        } finally {
            // As the close() operation can also throw an IOException
            // it must caught here
            try {
                socket.close();
            } catch (IOException ex) {
                // feel free to do something moderately useful here, eg log the event
            }

        }
        return true;
    }

Dans le scénario de fichier mp3 les choses sont un peu différentes. Vous devriez vérifier le code de réponse qui suit après la requête http.

public static boolean isRecordedStreamingAvailable() {
        try {
            HttpURLConnection.setFollowRedirects(false);
            // note : you may also need
            //        HttpURLConnection.setInstanceFollowRedirects(false)
            HttpURLConnection con =
                    (HttpURLConnection) new URL(RECORDED_URL).openConnection();
            con.setRequestMethod("HEAD");
            return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
        }
        catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
0
LiTTle