如何在 Android 中下载和流式传输音频?
How to download and stream audio in Android?
我正在尝试在我制作的应用程序中实现一项功能,使用户能够收听在线流媒体。我需要做的是在播放文件的同时下载文件。
我想通了这么多,我需要一个我使用的本地 HTTP 服务器 NanoHTTPD。现在棘手的部分是如何同时实际下载和流式传输音频。
这是我到目前为止想出的代码:
public class LocalHttpServer extends NanoHTTPD {
public static final int SERVER_PORT = 5987;
private String mUrl;
private InputStream input;
private FileOutputStream output;
public LocalHttpServer(String url) {
super(SERVER_PORT);
mUrl = url;
}
private File createFile(String url) {
File path = new File(MyApplication.getContext().getFilesDir(), "audio/");
path.mkdirs();
return new File(path, Util.md5(url));
}
@Override
public Response serve(IHTTPSession session) {
input = null;
output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(mUrl);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return new Response(Response.Status.BAD_REQUEST, "audio/mpeg3", null, 0);
}
int fileLength = connection.getContentLength();
input = connection.getInputStream();
output = new FileOutputStream(createFile(mUrl));
new Thread(new Runnable() {
@Override
public void run() {
byte data[] = new byte[4096];
int count;
try {
while ((count = input.read(data)) != -1) {
output.write(data, 0, count);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (output != null)
output.close();
if (input != null)
//input.close(); don't close it
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
return new Response(Response.Status.OK, "audio/mpeg3", input, fileLength);
} catch (IOException e) {
e.printStackTrace();
}
return new Response(Response.Status.BAD_REQUEST, "audio/mpeg3", null, 0);
}
}
问题是当输入到 MediaPlayer 时,the unexpected end of stream
发生异常。
由于没有人回答我的问题,所以我带来了另一个解决方案,我曾用它来完成类似的任务。
我想我可以使用 AsyncTask 来下载文件,当下载达到总数的 10% 时,使用接口回调启动 MediaPlayer。并在下载另外 20% 时更新 MediaPlayer。
这是上述 AsyncTask 的来源:https://gist.github.com/2hamed/63a31bd55fc6514d12b5
public class DownloadAndPlayAsyncTask extends AsyncTask<String, Integer, Integer> {
private static final String TAG = "DownloadAndPlayAsync";
DownloadCallback downloadCallback;
File tempFile, fullFile;
private void createFiles(String url) {
tempFile = Util.getTempFilePath(url);
fullFile = Util.getFilePath(url);
}
public void setOnDownloadCallback(DownloadCallback callback) {
downloadCallback = callback;
}
@Override
protected Integer doInBackground(String... strings) {
if (Util.isFileDownloaded(strings[0])) {
createFiles(strings[0]);
return 1;
}
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(strings[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
Log.d(TAG, "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage());
return -1;
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
createFiles(strings[0]);
// download the file
input = connection.getInputStream();
output = new FileOutputStream(tempFile);
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
if (fileLength > 0) // only if total length is known
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
e.printStackTrace();
return -1;
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return 0;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
if (result == 0) {
try {
Util.copy(tempFile, fullFile);
tempFile.delete();
if (downloadCallback != null) {
downloadCallback.onFinished(fullFile.getAbsolutePath());
}
} catch (IOException e) {
e.printStackTrace();
}
} else if (result == 1) {
if (downloadCallback != null) {
downloadCallback.onFinished(fullFile.getAbsolutePath());
}
} else {
if (downloadCallback != null) {
downloadCallback.onFailed();
}
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (downloadCallback != null) {
downloadCallback.onProgressUpdate(values[0], tempFile.getAbsolutePath());
}
}
@Override
protected void onCancelled(Integer result) {
super.onCancelled(result);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
public interface DownloadCallback {
void onProgressUpdate(int progress, String filePath);
void onFinished(String fullFile);
void onFailed();
}
}
如果您注意到代码末尾有 DownloadCallback
。
我正在尝试在我制作的应用程序中实现一项功能,使用户能够收听在线流媒体。我需要做的是在播放文件的同时下载文件。
我想通了这么多,我需要一个我使用的本地 HTTP 服务器 NanoHTTPD。现在棘手的部分是如何同时实际下载和流式传输音频。
这是我到目前为止想出的代码:
public class LocalHttpServer extends NanoHTTPD {
public static final int SERVER_PORT = 5987;
private String mUrl;
private InputStream input;
private FileOutputStream output;
public LocalHttpServer(String url) {
super(SERVER_PORT);
mUrl = url;
}
private File createFile(String url) {
File path = new File(MyApplication.getContext().getFilesDir(), "audio/");
path.mkdirs();
return new File(path, Util.md5(url));
}
@Override
public Response serve(IHTTPSession session) {
input = null;
output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(mUrl);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return new Response(Response.Status.BAD_REQUEST, "audio/mpeg3", null, 0);
}
int fileLength = connection.getContentLength();
input = connection.getInputStream();
output = new FileOutputStream(createFile(mUrl));
new Thread(new Runnable() {
@Override
public void run() {
byte data[] = new byte[4096];
int count;
try {
while ((count = input.read(data)) != -1) {
output.write(data, 0, count);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (output != null)
output.close();
if (input != null)
//input.close(); don't close it
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
return new Response(Response.Status.OK, "audio/mpeg3", input, fileLength);
} catch (IOException e) {
e.printStackTrace();
}
return new Response(Response.Status.BAD_REQUEST, "audio/mpeg3", null, 0);
}
}
问题是当输入到 MediaPlayer 时,the unexpected end of stream
发生异常。
由于没有人回答我的问题,所以我带来了另一个解决方案,我曾用它来完成类似的任务。
我想我可以使用 AsyncTask 来下载文件,当下载达到总数的 10% 时,使用接口回调启动 MediaPlayer。并在下载另外 20% 时更新 MediaPlayer。
这是上述 AsyncTask 的来源:https://gist.github.com/2hamed/63a31bd55fc6514d12b5
public class DownloadAndPlayAsyncTask extends AsyncTask<String, Integer, Integer> {
private static final String TAG = "DownloadAndPlayAsync";
DownloadCallback downloadCallback;
File tempFile, fullFile;
private void createFiles(String url) {
tempFile = Util.getTempFilePath(url);
fullFile = Util.getFilePath(url);
}
public void setOnDownloadCallback(DownloadCallback callback) {
downloadCallback = callback;
}
@Override
protected Integer doInBackground(String... strings) {
if (Util.isFileDownloaded(strings[0])) {
createFiles(strings[0]);
return 1;
}
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(strings[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
Log.d(TAG, "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage());
return -1;
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
createFiles(strings[0]);
// download the file
input = connection.getInputStream();
output = new FileOutputStream(tempFile);
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
if (fileLength > 0) // only if total length is known
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
e.printStackTrace();
return -1;
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return 0;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
if (result == 0) {
try {
Util.copy(tempFile, fullFile);
tempFile.delete();
if (downloadCallback != null) {
downloadCallback.onFinished(fullFile.getAbsolutePath());
}
} catch (IOException e) {
e.printStackTrace();
}
} else if (result == 1) {
if (downloadCallback != null) {
downloadCallback.onFinished(fullFile.getAbsolutePath());
}
} else {
if (downloadCallback != null) {
downloadCallback.onFailed();
}
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (downloadCallback != null) {
downloadCallback.onProgressUpdate(values[0], tempFile.getAbsolutePath());
}
}
@Override
protected void onCancelled(Integer result) {
super.onCancelled(result);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
public interface DownloadCallback {
void onProgressUpdate(int progress, String filePath);
void onFinished(String fullFile);
void onFailed();
}
}
如果您注意到代码末尾有 DownloadCallback
。