2010-03-22 9 views
29

Nous mettons en œuvre un programme pour les téléphones Android qui diffuse de l'audio en streaming à partir d'Internet. Voici à peu près ce que nous faisons:Décodage MP3 sur Android

  1. Téléchargez un format crypté personnalisé. Déchiffrer pour obtenir des morceaux de données MP3 normales.
  2. Décode les données MP3 en données PCM brutes dans un tampon mémoire.
  3. pipe Les données brutes PCM à un AudioTrack

Nos dispositifs cibles jusqu'à présent sont Droid et Nexus One. Tout fonctionne très bien sur Nexus One, mais le décodage MP3 est trop lent sur Droid. La lecture audio commence à sauter si nous mettons le Droid en charge. Nous ne sommes pas autorisés à décoder les données MP3 sur la carte SD, mais je sais que ce n'est pas notre problème de toute façon.

Nous n'avons pas écrit notre propre décodeur MP3, mais utilisé MPADEC (http://sourceforge.net/projects/mpadec/). C'est gratuit et facile à intégrer à notre programme. Nous le compilons avec le NDK. Après une analyse exhaustive avec différents outils de profilage, nous sommes convaincus que ce décodeur est à la traîne.

est ici les options que nous pensons à:

  1. Trouver un autre décodeur MP3 que nous pouvons compiler avec l'Android NDK. Ce décodeur MP3 devrait être soit optimisé pour fonctionner sur les dispositifs ARM mobiles ou peut-être utiliser des maths entiers seulement ou d'autres optimisations pour augmenter les performances. Etant donné que le service intégré Android MediaPlayer prendra des URL, nous pourrons peut-être implémenter un minuscule serveur HTTP dans notre programme et servir le MediaPlayer avec les fichiers MP3 déchiffrés. De cette façon, nous pouvons profiter du décodeur MP3 intégré.

  2. Accédez au décodeur MP3 intégré via le NDK. Je ne sais pas si c'est possible.

Quelqu'un at-il des suggestions sur ce que nous pouvons faire pour accélérer notre décodage MP3?

- Rob Sz

+0

Dans votre option # 2 , Je m'attendrais à ce que la surcharge HTTP comprenne les gains que vous obtenez en utilisant le support de diffusion 'MediaPlayer' intégré. – CommonsWare

+2

Si je ne me trompe pas, le 'MediaPlayer' intégré peut également diffuser à partir de n'importe quel URI' content: // ', qui vous donne essentiellement un tube dans lequel écrire. –

+0

@jleedev: pouvez-vous élaborer là-dessus? Cela semble très intéressant! – Peterdk

Répondre

0

Je ne l'ai pas essayé, mais je crois que vous pouvez donner le lecteur multimédia un descripteur de fichier:

public void setDataSource (FileDescriptor fd, long offset, long length) 

Peut-être que vous pourriez écrire le mp3 déchiffré sur un fichier et jouez-le en utilisant le descripteur de fichier. Cela peut même fonctionner pour le streaming en supposant que vous connaissez la longueur du fichier à l'avance. Si cela fonctionne, ce serait beaucoup plus simple que de faire un serveur web localhost.

+0

Peut-être que vous n'avez pas remarqué sa remarque: "Nous ne sommes pas autorisés à décoder les données MP3 sur la carte SD". Pour des raisons de droits d'auteur et de gestion des droits numériques (je pense). –

+0

Peut-être que vous n'avez pas remarqué que twk n'a rien dit à propos des cartes SD. – CommonsWare

+1

Ah, c'est vrai - ça m'a manqué. Cependant, vous pourriez peut-être utiliser pipe() pour obtenir des descripteurs de fichiers liés. – twk

2

La meilleure façon de procéder consiste à créer votre propre microprogramme et à effectuer le décryptage dans le cadre d'un codec OpenCORE personnalisé. Cela, bien sûr, vous limitera aux appareils sur lesquels vous pouvez installer ce firmware.

Gardez à l'esprit que le reste est quelque peu spéculatif. J'ai vraiment besoin de faire quelque chose de similaire à ce que vous décrivez, mais je n'ai pas le temps de faire face au problème pendant quelques mois. Donc, je décrirai cela de la façon dont j'aborderais le problème.

Une solution est celle décrite dans la réponse de twk. Vous n'avez pas besoin d'utiliser la carte SD, mais vous devez probablement avoir un fichier temporaire lisible dans le monde dans votre magasin de fichiers app-local (getFilesDir()). Téléchargez le premier morceau, décryptez-le, écrivez-le sous la forme d'un fichier MP3 complet lisible à l'échelle mondiale (mais avec un répertoire/chemin convenablement obscur), et passez-le à MediaPlayer via setDataSource(). Pendant la lecture, vous téléchargez/décryptez et configurez une seconde instance MediaPlayer, qui démarre la lecture dès que la première se termine, pour une transition aussi transparente que possible. Vous réinitialisez alors le premier MediaPlayer et le réutilisez avec votre troisième morceau, ping-ponging entre les deux.

Une solution connexe serait dans le commentaire de jleedev. C'est à peu près la même chose, sauf que vous fournissez un FileDescriptor via un ContentProvider. Cela a une option pour vous permettre d'utiliser un socket, qui peut vous permet d'éviter le fichier temporaire. Cependant, le ContentProvider devra être lui-même accessible au public, et donc le fichier temporaire, avec un répertoire obscur, pourrait être plus privé.

Si vous êtes préoccupé par le fait que ces choses peuvent être lues par d'autres processus, veuillez comprendre que MediaPlayer lui-même (ou, plutôt, le sous-système OpenCORE) est dans un autre processus. En outre, votre serveur HTTP proposé est également lisible sur le périphérique. Donc, la sécurité par l'obscurité est votre seule option viable si vous allez laisser MediaPlayer faire le décodage. AFAIK, le NDK n'accorde pas l'accès à OpenCORE, bien que j'avoue avoir une expérience limitée de NDK, donc je peux me tromper. Il y a certainement d'autres décodeurs MP3 disponibles (ffmpeg/mplayer, etc.), bien que la facilité avec laquelle ils pourraient être convertis en une bibliothèque NDK n'est pas claire. Donc, ça se résume vraiment à qui vous essayez de vous défendre. Si vous essayez de vous défendre contre l'utilisateur, vous devrez probablement le décoder vous-même, d'une manière ou d'une autre.

+0

MediaPlayer.create (context, uri) ?? – TacB0sS

1

MAD est une bibliothèque de décodeurs d'entier-arithmétique seulement qui semble être bien considérée.

(Ne serait-décoder les données sur une carte SD vous ralentir de toute façon?)

+3

Comme une note, MAD est sous la licence GPL. – yincrash

0

Pour Décrypté un fichier mp3 crypté avant le jouer.Il y a 3 façons:

1, pour une utilisation MediaPlayer api:

Après la version Android M:

you can play mp3 data in byte array directly by: 

//Read encrypted mp3: 
byte[] bytesAudioData = Utils.readFile(path); // or from a url. 

final byte[] bytesAudioDataDecrypted = YourUtils.decryptFileToteArray(bytesAudioData); 

       ByteArrayMediaDataSource bds = new ByteArrayMediaDataSource(bytesAudioDataDecrypted) ; 
       mediaPlayer.setDataSource(bds); 

Avant la version Android M:

//Read encrypted mp3: 
byte[] bytesAudioData = Utils.readFile(path); // or from a url. 

final byte[] bytesAudioDataDecrypted = YourUtils.decryptFileToteArray(bytesAudioData); 

File file =new File(dirofyouraudio.mp3"); 

       FileOutputStream fos = new FileOutputStream(file); 
       fos.write(bytesAudioDataDecrypted); 
       Log.d(TAG, "playSound :: file write to temp :: System. nanoTime() ="+System. nanoTime()); 
       fos.close(); 

       FileInputStream fis = new FileInputStream(file); 
       mediaPlayer.setDataSource(fis.getFD()); 

2, Pour AudioTrack api si vous utilisez:

int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, 
      AudioFormat.CHANNEL_OUT_MONO, 
      AudioFormat.ENCODING_PCM_16BIT); 

    AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, 
      AudioFormat.CHANNEL_OUT_MONO, 
      AudioFormat.ENCODING_PCM_16BIT, minBufferSize, 
      AudioTrack.MODE_STREAM); 

    int i = 0; 
    byte[] tempMinBufferToRead = new byte[minBufferSize]; 

    try { 

     byte[] bytesAudioData = Utils.readFile(lastRecordedAudiofilePath); 

     bytesAudioData = yourUtils.decryptYourFileToByteArray(bytesAudioData); 

     try { 
      fileInputStremForPlay = new FileInputStream(lastRecordedAudiofilePath); 

      dataInputStreamForPlay = new DataInputStream(new ByteArrayInputStream(bytesAudioData)); 

      track.play(); 
      while ((i = dataInputStreamForPlay.read(tempMinBufferToRead, 0, minBufferSize)) > -1) { 
       Log.d(LOG_TAG, "mThreadExitFlag= " + mThreadExitFlag); 

       if ((mThreadExitFlag == true) || (!audioFilePathInThisThread.equals(lastRecordedAudiofilePath))) { 

        m_handler.post(new Runnable() { 
         public void run() { 
          Log.d(LOG_TAG, "startPlaying: break and reset play button "); 

          startPlayBtnInToolbar.setEnabled(true); 
          stopPlayBtnInToolbar.setEnabled(false); 
         } 
        }); 

        break; 
       } 

       track.write(tempMinBufferToRead, 0, i); 
      } 

      Log.d(LOG_TAG, "===== Playing Audio Completed ===== "); 
      track.stop(); 

      track.release(); 

      dataInputStreamForPlay.close(); 

      fileInputStremForPlay.close(); 


     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

3, utilisation MediaExtractor, MediaCodec avec AudioTrack, vous pouvez définir la source de données avec extracteur:

  extractor.setDataSource(this.url); 

Ou

  extractor.setDataSource(this.Mediadatasource); 

ps: La classe ByteArrayMediaDataSource:

import android.annotation.TargetApi; 
import android.media.MediaDataSource; 
import android.os.Build; 
import java.io.IOException; 



import android.content.res.AssetFileDescriptor; 
import android.media.MediaDataSource; 
import android.util.Log; 
import java.io.FileInputStream; 
import java.io.InputStream; 
import java.io.IOException; 

@TargetApi(Build.VERSION_CODES.M) 
public class ByteArrayMediaDataSource extends MediaDataSource { 

    private static final String TAG = ByteArrayMediaDataSource.class.getSimpleName(); 


    private byte[] data; 

    public ByteArrayMediaDataSource(byte []data) { 
//  assert data != null; 
     this.data = data; 
    } 
    @Override 
    public int readAt(long position, byte[] buffer, int offset, int size) throws IOException { 


     if (position >= data.length) { 
      return -1; // -1 indicates EOF 
     } 
     if (position + size > data.length) { 
      size -= (position + size) - data.length; 
     } 

     Log.d(TAG, "MediaDataSource size = "+size); 


     System.arraycopy(data, (int)position, buffer, offset, size); 
     return size; 

    } 



    @Override 
    public long getSize() throws IOException { 

     Log.d(TAG, "MediaDataSource data.length = "+data.length); 

     return data.length; 
    } 

    @Override 
    public void close() throws IOException { 
     // Nothing to do here 
     data =null; 
    } 
}