2012-01-10 1 views
2

Je souhaite utiliser un InputStream arbitraire en tant que source de données pour un objet MediaPlayer.Utilisation d'un flux arbitraire comme source pour MediaPlayer

La raison en est que le InputStream que j'utilise est en fait une connexion HTTPS autorisée à une ressource multimédia sur un serveur distant. Passer l'URL dans ce cas ne fonctionnera évidemment pas car une authentification est requise. Je peux cependant faire l'authentification séparément et obtenir un InputStream à la ressource - le problème est que dois-je faire une fois que je l'ai?

J'ai réfléchi à l'option d'utiliser un canal nommé et de passer son FileDescriptor à la méthode setDataResource de MediaPlayer. Y at-il un moyen de créer des tuyaux nommés dans Android (sans utiliser NDK)?

Toute autre suggestion est la bienvenue.

+0

Je ne peux pas vérifier si cela a fait une différence, mais si vous êtes chanceux, MediaPlayer Uri pourrait supporter l'authentification '' https: // nom d'utilisateur: mot de passe @ server.com/stream.data''. Si votre authentification est quelque chose de différent de celui par défaut, il s'agit d'un refus. – harism

+0

Non, l'authentification n'est pas basée sur un mot de passe. – smichak

Répondre

1

Une autre solution serait de démarrer un serveur HTTP proxy sur localhost. Le lecteur multimédia se connectera à ce serveur avec setDataSource (Context context, Uri uri). Cette solution fonctionne mieux que la précédente et ne provoque pas de problèmes de lecture.

+1

Pour la postérité, voici un exemple de serveur proxy: http://stackoverflow.com/a/5432091/931277 – dokkaebi

3

Je pense avoir trouvé une solution. J'apprécierais que d'autres personnes intéressées l'essaient d'elles-mêmes et rapportent les résultats avec leurs modèles d'appareils et leur version SDK.

J'ai vu des messages similaires qui directement à cela, mais je pensais que je le publierais car il est plus récent et semble fonctionner sur les nouvelles versions du SDK - jusqu'à présent, il fonctionne sur mon Nexus One fonctionnant sous Android 2.3.6. La solution repose sur la mise en mémoire tampon du flux d'entrée dans un fichier local (j'ai ce fichier sur le stockage externe mais il sera probablement possible de le placer sur le stockage interne) et de fournir le descripteur de ce fichier à l'instance MediaPlayer .

Les essais suivants dans un procédé doInBackground de certains AsyncTask qui fait AudioPlayback:

@Override 
protected 
Void doInBackground(LibraryItem... params) 
{ 
    ... 

    MediaPlayer player = new MediaPlayer(); 
    setListeners(player); 

    try { 
     _remoteStream = getMyInputStreamSomehow(); 
     File tempFile = File.createTempFile(...); 
     tempFile.deleteOnExit(); 
     _localInStream = new FileInputStream(tempFile); 
     _localOutStream = new FileOutputStream(tempFile); 
     int buffered = bufferMedia(
      _remoteStream, _localOutStream, BUFFER_TARGET_SIZE  // = 128KB for instance 
     ); 

     player.setAudioStreamType(AudioManager.STREAM_MUSIC); 
     player.setDataSource(_localInStream.getFD()); 
     player.prepareAsync(); 

     int streamed = 0; 
     while (buffered >= 0) { 
      buffered = bufferMedia(
       _remoteStream, _localOutStream, BUFFER_TARGET_SIZE 
      ); 
     } 
    } 
    catch (Exception exception) { 
     // Handle errors as you see fit 
    } 

    return null; 
} 

les tampons de méthode bufferMedia nbytes octets ou jusqu'à la fin de l'entrée est atteinte:

private 
int bufferMedia(InputStream inStream, OutputStream outStream, int nBytes) 
throws IOException 
{ 
    final int BUFFER_SIZE = 8 * (1 << 10); 
    byte[] buffer = new byte[BUFFER_SIZE];   // TODO: Do static allocation instead 

    int buffered = 0, read = -1; 
    while (buffered < nBytes) { 
     read = inStream.read(buffer); 
     if (read == -1) { 
      break; 
     }   
     outStream.write(buffer, 0, read); 
     outStream.flush(); 
     buffered += read; 
    } 

    if (read == -1 && buffered == 0) { 
     return -1; 
    } 

    return buffered; 
} 

La méthode setListeners Définit des gestionnaires pour divers événements MediaPlayer. Le plus important est le OnCompletionListener qui est appelé lorsque la lecture est terminée. En cas de sous-utilisation du tampon (en raison, disons, d'une connexion réseau lente temporaire), le lecteur atteindra la fin du fichier local et passera à l'état PlaybackCompleted. J'identifie ces situations en comparant la position de _localInStream par rapport à la taille du flux d'entrée. Si la position est plus petite, puis la lecture est vraiment terminée et je réinitialiser le MediaPlayer:

private 
void setListeners(MediaPlayer player) 
{ 
    // Set some other listeners as well 

    player.setOnSeekCompleteListener(
     new MediaPlayer.OnSeekCompleteListener() 
     { 
      @Override 
      public 
      void onSeekComplete(MediaPlayer mp) 
      { 
       mp.start(); 
      } 
     } 
    ); 

    player.setOnCompletionListener(
     new MediaPlayer.OnCompletionListener() 
     { 
      @Override 
      public 
      void onCompletion(MediaPlayer mp) 
      { 
       try { 
        long bytePosition = _localInStream.getChannel().position(); 
        int timePosition = mp.getCurrentPosition(); 
        int duration = mp.getDuration(); 

        if (bytePosition < _track.size) {       
         mp.reset(); 
         mp.setDataSource(_localInStream.getFD()); 
         mp.prepare(); 
         mp.seekTo(timePosition); 
        } else {        
         mp.release(); 
        } 
       } catch (IOException exception) { 
        // Handle errors as you see fit 
       } 
      } 
     } 
    ); 
} 
Questions connexes