我想使用任意 InputStream 作为 MediaPlayer 对象的数据源。
原因是我使用的InputStream实际上是到远程服务器上媒体资源的授权HTTPS连接。在这种情况下传递 URL 显然不起作用,因为需要身份验证。不过,我可以单独进行身份验证并获取资源的 InputStream - 问题是一旦获得它我该怎么办?
我考虑过使用命名管道并将其 FileDescriptor 传递给 MediaPlayer 的 setDataResource 方法的选项。有没有办法在 Android 中创建命名管道(不使用 NDK)?
欢迎任何其他建议。
最佳答案
我想我已经找到解决办法了。如果其他有兴趣的人能够自己尝试一下并报告结果以及他们的设备型号和 SDK 版本,我将不胜感激。
我看过类似的帖子,但我想无论如何我都会发布它,因为它是较新的,并且似乎可以在较新版本的 SDK 上运行 - 到目前为止,它可以在运行 Android 2.3.6 的我的 Nexus One 上运行。
该解决方案依赖于将输入流缓冲到本地文件(我将此文件放在外部存储上,但也可能将其放在内部存储上)并向 MediaPlayer 实例提供该文件的描述符。
以下内容在执行 AudioPlayback 的某些 AsyncTask 的 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
}
}
}
);
}
关于android - 使用任意流作为 MediaPlayer 的源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8809608/