2012-01-10 55 views
2

我想使用任意InputStream作爲MediaPlayer對象的數據源。使用任意流作爲MediaPlayer的源

原因是我使用的InputStream實際上是授權的HTTPS連接到遠程服務器上的媒體資源。在這種情況下傳遞URL顯然不會起作用,因爲需要進行身份驗證。然而,我可以單獨進行身份驗證並獲取InputStream資源 - 問題是我一旦擁有了該怎麼辦?

我想過使用命名管道並將其FileDescriptor傳遞給MediaPlayer的setDataResource方法的選項。有沒有辦法在Android中創建命名管道(不使用NDK)?

任何其他建議是最受歡迎的。

+0

我無法驗證這是否有所作爲,但如果幸運的話,MediaPlayer Uri可能會支持身份驗證「https:// username:password @ server.com/stream.data」。如果您的身份驗證與默認身份驗證有所不同,那麼這顯然是不可行的。 – harism 2012-01-10 20:01:52

+0

不,認證不是基於密碼的。 – smichak 2012-01-10 23:31:44

回答

1

另一種解決辦法是開始在本地主機上的代理HTTP服務器。媒體播放器將使用setDataSource(Context context,Uri uri)連接到此服務器。這種解決方案比以前更好,不會導致播放出現故障。

+1

對於後代,下面是所述代理服務器的示例:http://stackoverflow.com/a/5432091/931277 – dokkaebi 2012-10-23 23:28:38

3

我想我已經找到了解決方案。如果其他感興趣的人可以自己嘗試這種方式,並使用他們的設備型號和SDK版本報告結果,我將不勝感激。

我已經看過類似的帖子,但是我認爲我會發布它,因爲它更新,似乎可以在較新版本的SDK上工作 - 到目前爲止它適用於運行Android 2.3.6的Nexus One。

該解決方案依賴於將輸入流緩衝到本地文件(我在外部存儲器上有此文件,但它也可能放置在內部存儲上),並將該文件的描述符提供給MediaPlayer實例。

在一些的AsyncTask,做AudioPlayback的doInBackground方法如下運行:

@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; 
} 

的bufferMedia方法緩衝器的nbytes字節或直到達到輸入的末尾:

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

的setListeners方法爲各種MediaPlayer事件設置處理程序。最重要的是在播放完成時調用OnCompletionListener,其中 被調用。在緩衝區不足(由於暫時的網絡連接緩慢)的情況下,播放器 將到達本地文件的末尾並轉換到PlaybackCompleted狀態。我通過比較_localInStream的 位置和輸入流的大小來識別這些情況。如果位置是較小的,則播放現在真正完成 和我重置的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 
       } 
      } 
     } 
    ); 
} 
相關問題