Использование произвольного потока в качестве источника для MediaPlayer
-
26-10-2019 - |
Вопрос
Я хотел бы использовать произвольный inputstream в качестве источника данных для объекта MediaPlayer.
Причина этого заключается в том, что используемым INPUTSTREAM, который я использую, является авторизованным HTTPS -соединением с медиа -ресурсом на удаленном сервере. Передача URL в этом случае, очевидно, не будет работать как аутентификация. Однако я могу выполнить аутентификацию отдельно и получить входную серию для ресурса - проблема в том, что мне делать, когда у меня есть?
Я подумал о возможности использования именованной трубы и передачи его FileScriptor в метод SetDatareSource MediaPlayer. Есть ли способ создать названные трубы в Android (без использования NDK)?
Любое другое предложение приветствуются.
Решение 2
Другим решением было бы запустить прокси -сервер HTTP на LocalHost. Медиаплеер подключится к этому серверу с SetDataSource (контекст контекста, URI URI). Это решение работает лучше, чем предыдущее и не вызывает воспроизведения сбоя.
Другие советы
Я думаю, что нашел решение. Я был бы признателен, если бы другие заинтересованные лица попробовали это самостоятельно и сообщили о результатах с помощью моделей устройств и версии SDK.
Я видел подобные посты, которые прямо к этому, но я подумал, что все равно опубликую их, так как они новее и, кажется, работают над более новыми версиями SDK - пока он работает на моем Nexus One, работающем Android 2.3.6.
Решение опирается на то, что вводится входной поток в локальный файл (у меня есть этот файл на внешнем хранилище, но, вероятно, можно поместить его на хранение Intenal) и предоставить дескриптор этого файла для экземпляра MediaPlayer.
Следующее работает в методе Doinbackground некоторой Asynctask, который делает 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;
}
Метод 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
}
}
}
);
}