Android:StreamProxy 无法在 Nexus4/5 上使用 Android 5.x

Android: StreamProxy not working on Nexus4/5 with Android 5.x

这是我在我的项目中使用的 StreamProxy 的来源

public class StreamProxy implements Runnable {
    private static final String LOG_TAG = "Stream proxy: %s";

    private int port = 0;
    private boolean isRunning = true;
    private ServerSocket socket;
    private Thread thread;

    public StreamProxy() {
        init();
        start();
    }

    public int getPort() {
        return port;
    }

    public String getProxyUrl(String uri, String tag) {
        return String.format("http://127.0.0.1:%d/%s", getPort(), uri);
    }

    private void init() {
        try {
            socket = new ServerSocket(port, 0, InetAddress.getByAddress(new byte[]{127, 0, 0, 1}));
            socket.setSoTimeout(5000);
            port = socket.getLocalPort();
            Timber.d(LOG_TAG, "port " + port + " obtained");
        } catch (IOException e) {
            Timber.e(e, "Error initializing server");
        }
    }

    private void start() {

        if (socket == null) {
            throw new IllegalStateException("Cannot start proxy; it has not been initialized.");
        }

        thread = new Thread(this);
        thread.start();
    }

    public void stop() {
        isRunning = false;

        if (thread == null) {
            throw new IllegalStateException("Cannot stop proxy; it has not been started.");
        }

        thread.interrupt();
        try {
            thread.join(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        Timber.d(LOG_TAG, "running");
        while (isRunning) {
            try {
                Socket client = socket.accept();
                if (client == null) {
                    continue;
                }
                Timber.d(LOG_TAG, "client connected");
                client.setKeepAlive(false);
                readRequest(client);

            } catch (SocketTimeoutException e) {
            } catch (IOException e) {
                Timber.e(e, "Error connecting to client");
            }
        }
        Timber.d(LOG_TAG, "Proxy interrupted. Shutting down.");
    }

    @Nullable
    private void readRequest(Socket client) throws IOException {
        InputStream is;
        String firstLine;
        try {
            is = client.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            firstLine = reader.readLine();
        } catch (IOException e) {
            Timber.e(LOG_TAG, "Error parsing request", e);
            return;
        }

        if (firstLine == null) {
            Timber.i(LOG_TAG, "Proxy client closed connection without a request.");
            return;
        }

        StringTokenizer st = new StringTokenizer(firstLine);
        st.nextToken();
        String uri = st.nextToken().substring(1);
        Timber.d(LOG_TAG, uri);
        processRequest(client, uri, "");
    }

    @Nullable
    private HttpURLConnection download(String path) throws IOException {
        URL url = new URL(path);
        HttpURLConnection 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) {
            throw new IOException("Server returned HTTP " + connection.getResponseCode()
                    + " " + connection.getResponseMessage());
        }
        return connection;
    }

    private void processRequest(Socket client, String url, String tag)
            throws IllegalStateException, IOException {

        Timber.d(LOG_TAG, "processing");
        HttpURLConnection realResponse = download(url);
        if (realResponse == null) {
            return;
        }

        InputStream data = realResponse.getInputStream();
        socketWriter.setClient(client);
        try {
            int readBytes;
            Timber.d(LOG_TAG, "writing data to client");
            // Start streaming content.
            byte[] buff = new byte[1024 * 8];
            while (isRunning && (readBytes = data.read(buff)) != -1) {
                client.getOutputStream().write(buff, 0, readBytes)
            }
            Timber.d(LOG_TAG, "end writing data");
        } catch (IOException e) {
            Timber.e(e, "Error data transfer to client");
        } finally {
            Timber.d(LOG_TAG, "finally block");
            if (data != null) {
                data.close();
            }
        }
    }
}

它在 Android <5.0 上完美运行,但在 Android 5.0.+ 上运行完美 我在使用 MediaExtractor(http://developer.android.com/reference/android/media/MediaExtractor.html)

时遇到以下错误
NuCachedSource2﹕ source returned error -1, 10 retries left
NuCachedSource2﹕ source returned error -1, 9 retries left
...

并且在使用 Mediaplayer 时没有错误消息,只需安静地重试几次。

也许 Android 5 有另一种音频流缓存方式?

流代理实际运行良好。我从 MediaExtractor 得到的所有错误都是关于 m4a 容器的。对于大多数以 m4a 格式打包的文件,无法使用 Android SDK 中的组件流式播放。除了三星、LG等一些厂商的部分ROM有此功能。