我正在开发一个音乐播放器,它接收带有远程mp3文件(HTTP)的播放列表,然后播放它们。
我想让它开始流式传输第一首曲目,如果缓冲了足够多的歌曲来播放,它应该已经开始将下一首歌曲缓冲到内存中了。这是为了弥补该程序应该运行的不稳定的互联网连接。
如何告诉BufferedInputStream只下载整个文件?
我也很高兴听到关于如何解决这个问题的其他建议。
我正在使用JLayer/BasicPlayer库来播放音频,这是代码。
String mp3Url = "http://ia600402.us.archive.org/6/items/Stockfinster.-DeadLinesutemos025/01_Push_Push.mp3";
URL url = new URL(mp3Url);
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
BasicPlayer player = new BasicPlayer();
player.open(bis);
player.play();
这里您将获得一个如何在java中预缓冲音频文件的示例
好的,为了回答我的问题,这里有一个工作实现:
/**
* <code>DownloaderInputStream</code>
*/
public class DownloaderInputStream extends InputStream {
/**
* <code>IDownloadNotifier</code> - download listener.
*/
public static interface IDownloadListener {
/**
* Notifies about download completion.
*
* @param buf
* @param offset
* @param length
*/
public void onComplete(final byte[] buf, final int offset, final int length);
}
/**
* <code>ByteArrayOutputStreamX</code> - {@link ByteArrayOutputStream}
* extension that exposes buf variable (to avoid copying).
*/
private final class ByteArrayOutputStreamX extends ByteArrayOutputStream {
/**
* Constructor.
*
* @param size
*/
public ByteArrayOutputStreamX(final int size) {
super(size);
}
/**
* Returns inner buffer.
*
* @return inner buffer
*/
public byte[] getBuffer() {
return buf;
}
}
private final class Downloader extends Object implements Runnable {
// fields
private final InputStream is;
/**
* Constructor.
*
* @param is
*/
public Downloader(final InputStream is) {
this.is = is;
}
// Runnable implementation
public void run() {
int read = 0;
byte[] buf = new byte[16 * 1024];
try {
while ((read = is.read(buf)) != -1) {
if (read > 0) {
content.write(buf, 0, read);
downloadedBytes += read;
} else {
Thread.sleep(50);
}
}
} catch (Exception e) {
e.printStackTrace();
}
listener.onComplete(content.getBuffer(), 0 /*
* offset
*/, downloadedBytes);
}
}
// fields
private final int contentLength;
private final IDownloadListener listener;
// state
private ByteArrayOutputStreamX content;
private volatile int downloadedBytes;
private volatile int readBytes;
/**
* Constructor.
*
* @param contentLength
* @param is
* @param listener
*/
public DownloaderInputStream(final int contentLength, final InputStream is, final IDownloadListener listener) {
this.contentLength = contentLength;
this.listener = listener;
this.content = new ByteArrayOutputStreamX(contentLength);
this.downloadedBytes = 0;
this.readBytes = 0;
new Thread(new Downloader(is)).start();
}
/**
* Returns number of downloaded bytes.
*
* @return number of downloaded bytes
*/
public int getDownloadedBytes() {
return downloadedBytes;
}
/**
* Returns number of read bytes.
*
* @return number of read bytes
*/
public int getReadBytes() {
return readBytes;
}
// InputStream implementation
@Override
public int available() throws IOException {
return downloadedBytes - readBytes;
}
@Override
public int read() throws IOException {
// not implemented (not necessary for BasicPlayer)
return 0;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (readBytes == contentLength) {
return -1;
}
int tr = 0;
while ((tr = Math.min(downloadedBytes - readBytes, len)) == 0) {
try {
Thread.sleep(100);
} catch (Exception e) {/*
* ignore
*/
}
}
byte[] buf = content.getBuffer();
System.arraycopy(buf, readBytes, b, off, tr);
readBytes += tr;
return tr;
}
@Override
public long skip(long n) throws IOException {
// not implemented (not necessary for BasicPlayer)
return n;
}
}