如何在 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