2012-01-10 2 views
2

Я хотел бы использовать произвольный InputStream в качестве источника данных для объекта MediaPlayer.Использование произвольного потока в качестве источника для MediaPlayer

Причина этого заключается в том, что InputStream, который я использую, фактически является авторизованным HTTPS-соединением с медиа-ресурсом на удаленном сервере. Передача URL-адреса в этом случае, очевидно, не будет работать, поскольку требуется аутентификация. Однако я могу выполнить аутентификацию отдельно и получить InputStream для ресурса - проблема в том, что я делаю, когда есть у меня?

Я подумал о возможности использования именованного канала и передачи его FileDescriptor методу setDataResource MediaPlayer. Есть ли способ создать именованные каналы в Android (без использования NDK)?

Любое другое предложение приветствуется.

+0

Я не могу проверить, было ли это иметь какое-то значение, но если вам повезет, MediaPlayer Uri может поддерживать аутентификацию '' https: // имя пользователя: пароль @ server.com/stream.data''. Если ваша аутентификация отличается от стандартной, это совершенно очевидно. – harism

+0

Нет, аутентификация не основана на пароле. – smichak

ответ

1

Другим решением было бы запустить прокси-сервер HTTP на локальном хосте. Медиаплеер подключится к этому серверу с помощью setDataSource (контекст контекста, Uri uri). Это решение работает лучше, чем предыдущее, и не приводит к сбою воспроизведения.

+1

Для потомков, вот пример упомянутого прокси-сервера: http://stackoverflow.com/a/5432091/931277 – dokkaebi

3

Я думаю, что нашел решение. Я был бы признателен, если бы другие, кто интересуется, попробовали бы это самостоятельно и сообщали результаты с их моделями устройств и версией SDK.

Я видел похожие записи, которые прямо на это, но я думал, что буду публиковать его в любом случае, так как он новее и, похоже, работает над более новыми версиями SDK - пока он работает на моем Nexus One под управлением Android 2.3.6.

Решение использует буферизацию входного потока для локального файла (у меня есть этот файл во внешнем хранилище, но, возможно, его также можно будет разместить на внутреннем хранилище) и предоставить дескриптор этого файла экземпляру MediaPlayer ,

Следующие работает в методе doInBackground некоторого AsyncTask, что делает аудиовоспроизведение:

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

The bufferMedia метод буфера число-байты байты или до конца ввода не будет достигнут:

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, который вызывается, когда воспроизведение завершено. В случае переполнения буфера (из-за, например, временного медленного сетевого подключения) игрок достигнет конца локального файла и перейдет в состояние воспроизведения. Я идентифицирую эти ситуации, сравнивая положение __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 
       } 
      } 
     } 
    ); 
} 
Смежные вопросы