android寻求代理流和stagefright播放器的问题

时间:2011-01-12 19:52:14

标签: android media-player

我想从网址播放一个只有一段时间才有效的音频流。由于缓冲机制(在第二次填充缓冲区时url将会死亡),因此使用内置的stagefright流功能不能很好地发挥作用,所以我实现了一个代理流,类似于在npr app中做了什么

http://code.google.com/p/npr-android-app/source/browse/trunk/Npr/src/org/npr/android/news/StreamProxy.java

这实际上非常好用,但有一个例外,任何搜索调用都会有效地破坏代理流。我很难确定搜寻在stagefright中的确切运作方式。每当我寻求时,我都会收到

的消息
01-12 13:35:57.201: ERROR/(4870): Connection reset by peer
01-12 13:35:57.201: ERROR/(4870): java.net.SocketException: Connection reset by peer
01-12 13:35:57.201: ERROR/(4870): at org.apache.harmony.luni.platform.OSNetworkSystem.writeSocketImpl(Native Method)
01-12 13:35:57.201: ERROR/(4870):     at org.apache.harmony.luni.platform.OSNetworkSystem.write(OSNetworkSystem.java:723)
01-12 13:35:57.201: ERROR/(4870):     at org.apache.harmony.luni.net.PlainSocketImpl.write(PlainSocketImpl.java:578)
01-12 13:35:57.201: ERROR/(4870):     at org.apache.harmony.luni.net.SocketOutputStream.write(SocketOutputStream.java:59)
01-12 13:35:57.201: ERROR/(4870):     at com.soundcloud.utils.StreamProxy.processRequest(StreamProxy.java:209)

然后暂停几秒钟,之后哪个stagefright重试连接到同一个url,并且通常会抛出一个错误(我会想象因为代理流没有被重置)。另一个潜在的问题是,似乎代理流将始终线性地读取数据源:

while (isRunning && (readBytes = data.read(buff, 0, buff.length)) != -1) 

我只是猜测,但我认为为了支持搜索,代理必须能够在从缓冲区读取时提供偏移量。有没有办法找出套接字客户端请求的偏移量(预期的搜索位置)?

我对套接字的体验有限。有没有人对此有实施建议?

4 个答案:

答案 0 :(得分:2)

当您寻找或跳过或连接丢失且MediaPlayer不断重新连接到代理服务器时,您必须在从客户端获取请求和范围(int)后将此响应发送到状态206。

String headers += "HTTP/1.1 206 OK\r\n";
headers += "Content-Type: audio/mpeg\r\n";
headers += "Accept-Ranges: bytes\r\n";
headers += "Content-Length: " + (fileSize-range) + "\r\n";
headers += "Content-Range: bytes "+range + "-" + fileSize + "/*\r\n";
headers += "\r\n";

当您收到来自MediaPlayer的请求时,HTTP标头中不包含Range,那么它正在请求新的流文件,在这种情况下,您的响应标头应如下所示:

String headers = "HTTP/1.1 200 OK\r\n";
headers += "Content-Type: audio/mpeg\r\n";
headers += "Accept-Ranges: bytes\r\n";
headers += "Content-Length: " + fileSize + "\r\n";
headers += "\r\n";

享受!

答案 1 :(得分:1)

MediaPlayer正在发出HTTP范围请求,指定播放器期望的字节偏移量。您可以从提交给代理服务器的请求的标头中读取这些范围值。然后,您可以打开将这些范围发送回MediaPlayer。

答案 2 :(得分:1)

不确定你是否想过这个,但我需要做的就是将原始请求中的标题传递给代理请求:

  private HttpResponse download(String url, Header[] headers) {
DefaultHttpClient seed = new DefaultHttpClient();
SchemeRegistry registry = new SchemeRegistry();
registry.register(
        new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
SingleClientConnManager mgr = new MyClientConnManager(seed.getParams(),
    registry);
DefaultHttpClient http = new DefaultHttpClient(mgr, seed.getParams());
HttpGet method = new HttpGet(url);
for (Header header : headers) {
    method.addHeader(header);
}
HttpResponse response = null;
try {
  Log.d(getClass().getName(), "starting download");
  response = http.execute(method);
  Log.d(getClass().getName(), "downloaded");

}catch(java.net.UnknownHostException e)
{
    Intent i=new Intent("org.prx.errorInStream");
    mContext.sendBroadcast(i);
}
catch (ClientProtocolException e) {
  Log.e(getClass().getName(), "Error downloading", e);
} catch (IOException e) {
  Log.e(getClass().getName(), "Error downloading", e);
}
return response;

}

private void processRequest(HttpRequest request, Socket client)
  throws IllegalStateException, IOException {
if (request == null) {
  return;
}
Log.d(getClass().getName(), "processing");
String url = request.getRequestLine().getUri();

HttpResponse realResponse = download(url, request.getAllHeaders());

if (realResponse == null) {
  return;
}

...

}

答案 3 :(得分:0)

如@Roberto所述,您必须修改方法downloadprocessRequest才能绕过HTTP请求标头。但实际上要完成它,或者至少在我的情况下,我必须进一步修改方法private HttpRequest readRequest(Socket client)来解析socket中的标题,如下所示。

private HttpRequest readRequest(Socket client) {
        HttpRequest request = null;
        InputStream is;
        String firstLine;
        List<Header> headers = new ArrayList<Header>();
        try {
            is = client.getInputStream();
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(is), 8192);
            firstLine = reader.readLine();
            /**
             * read HTTP request headers from socket
             * */
            String line;
            while (((line = reader.readLine()) != null) && !(line.equals(""))) { // HTTP header ends at ""
                try {
                    StringTokenizer st = new StringTokenizer(line, ":");
                    String h = st.nextToken();
                    String v = st.nextToken();
                    Header header = new BasicHeader(h, v);
                    headers.add(header);
                } catch (NoSuchElementException e) {
                }
            }
        } catch (IOException e) {
            Log.e(LOG_TAG, "Error parsing request", e);
            return request;
        }

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

        /**
         * retrieve REAL url
         * */
        StringTokenizer st = new StringTokenizer(firstLine);
        String method = st.nextToken();
        String uri = st.nextToken();
        Log.d(LOG_TAG, uri);
        String realUri = uri.substring(1);
        Log.d(LOG_TAG, realUri);
        request = new BasicHttpRequest(method, realUri);
        /**
         * add headers back to HTTP request
         * */
        for (Header header : headers) {
            request.addHeader(header);
        }
        return request;
    }