【问题标题】:Video streaming using NanoHttpd, Error: java.net.SocketException: sendto failed: EPIPE (Broken pipe)使用 NanoHttpd 进行视频流式传输,错误:java.net.SocketException: sendto failed: EPIPE (Broken pipe)
【发布时间】:2016-01-22 09:24:30
【问题描述】:

我需要流式传输视频并在网页中连续播放。我正在使用 Nanohttpd 库在我的 Android 应用程序中运行服务器。我可以在页面加载时流式传输和播放视频。视频播放完毕后,我无法第二次播放该视频。每次我需要刷新页面来播放视频。尝试第二次玩时出现以下错误。

java.net.SocketException: sendto failed: EPIPE (Broken pipe)
    at libcore.io.IoBridge.maybeThrowAfterSendto(IoBridge.java:546)
    at libcore.io.IoBridge.sendto(IoBridge.java:515)
    at java.net.PlainSocketImpl.write(PlainSocketImpl.java:504)
    at java.net.PlainSocketImpl.access$100(PlainSocketImpl.java:37)
    at java.net.PlainSocketImpl$PlainSocketOutputStream.write(PlainSocketImpl.java:266)
    at server.http.android.androidhttpserver.server.NanoHTTPD$Response.sendBody(NanoHTTPD.java:1386)
    at server.http.android.androidhttpserver.server.NanoHTTPD$Response.sendBodyWithCorrectEncoding(NanoHTTPD.java:1359)
    at server.http.android.androidhttpserver.server.NanoHTTPD$Response.sendBodyWithCorrectTransferAndEncoding(NanoHTTPD.java:1349)
    at server.http.android.androidhttpserver.server.NanoHTTPD$Response.send(NanoHTTPD.java:1335)
    at server.http.android.androidhttpserver.server.NanoHTTPD$HTTPSession.execute(NanoHTTPD.java:769)
    at server.http.android.androidhttpserver.server.NanoHTTPD$ClientHandler.run(NanoHTTPD.java:186)
    at java.lang.Thread.run(Thread.java:818)
Caused by: android.system.ErrnoException: sendto failed: EPIPE (Broken pipe)
    at libcore.io.Posix.sendtoBytes(Native Method)
    at libcore.io.Posix.sendto(Posix.java:176)
    at libcore.io.BlockGuardOs.sendto(BlockGuardOs.java:278)
    at libcore.io.IoBridge.sendto(IoBridge.java:513)
    at java.net.PlainSocketImpl.write(PlainSocketImpl.java:504)
    at java.net.PlainSocketImpl.access$100(PlainSocketImpl.java:37)
    at java.net.PlainSocketImpl$PlainSocketOutputStream.write(PlainSocketImpl.java:266)
    at server.http.android.androidhttpserver.server.NanoHTTPD$Response.sendBody(NanoHTTPD.java:1386)
    at server.http.android.androidhttpserver.server.NanoHTTPD$Response.sendBodyWithCorrectEncoding(NanoHTTPD.java:1359)
    at server.http.android.androidhttpserver.server.NanoHTTPD$Response.sendBodyWithCorrectTransferAndEncoding(NanoHTTPD.java:1349)
    at server.http.android.androidhttpserver.server.NanoHTTPD$Response.send(NanoHTTPD.java:1335)
    at server.http.android.androidhttpserver.server.NanoHTTPD$HTTPSession.execute(NanoHTTPD.java:769)
    at server.http.android.androidhttpserver.server.NanoHTTPD$ClientHandler.run(NanoHTTPD.java:186)
    at java.lang.Thread.run(Thread.java:818)

我正在发送如下响应。

@Override
public Response serve(IHTTPSession session) {
   FileInputStream fis = null;
    File file = new File("/storage/emulated/0/DCIM/Camera/VIDEO.mp4");
    try{
        if(file.exists())
        {
            fis = new FileInputStream(file);
        }
        else
         Log.d("FOF :", "File Not exists:");
    }catch (FileNotFoundException e)
    {
        e.printStackTrace();
    }
    return new NanoHTTPD.Response(Response.Status.OK,"video/mp4",fis, file.length() );
}

访问视频的 HTML 文件是

<html><body>
<video id="video_id" width="420" autoplay loop>
    <source src=http://192.168.2.6:8080/ type="video/mp4">
  </video>
</body></html>

请提供一个解决方案,让我在不刷新网页的情况下连续播放视频。

【问题讨论】:

    标签: java android nanohttpd


    【解决方案1】:

    我成功使用以下代码:

    @Override
    public Response serve(IHTTPSession session) {
        Map<String, String> headers = session.getHeaders();
        Map<String, String> parms = session.getParms();
        Method method = session.getMethod();
        String uri = session.getUri();
        Map<String, String> files = new HashMap<>();
    
        if (Method.POST.equals(method) || Method.PUT.equals(method)) {
            try {
                session.parseBody(files);
            }
            catch (IOException e) {
                return getResponse("Internal Error IO Exception: " + e.getMessage());
            }
            catch (ResponseException e) {
                return new Response(e.getStatus(), MIME_PLAINTEXT, e.getMessage());
            }
        }
    
        uri = uri.trim().replace(File.separatorChar, '/');
        if (uri.indexOf('?') >= 0) {
            uri = uri.substring(0, uri.indexOf('?'));
        }
    
        File f = new File(uri);
        return serveFile(uri, header, f);
    }
    
    private Response serveFile(String uri, Map<String, String> header, File file) {
        Response res;
        String mime = getMimeTypeForFile(uri);
        try {
            // Calculate etag
            String etag = Integer.toHexString((file.getAbsolutePath() +
                    file.lastModified() + "" + file.length()).hashCode());
    
            // Support (simple) skipping:
            long startFrom = 0;
            long endAt = -1;
            String range = header.get("range");
            if (range != null) {
                if (range.startsWith("bytes=")) {
                    range = range.substring("bytes=".length());
                    int minus = range.indexOf('-');
                    try {
                        if (minus > 0) {
                            startFrom = Long.parseLong(range.substring(0, minus));
                            endAt = Long.parseLong(range.substring(minus + 1));
                        }
                    } catch (NumberFormatException ignored) {
                    }
                }
            }
    
            // Change return code and add Content-Range header when skipping is requested
            long fileLen = file.length();
            if (range != null && startFrom >= 0) {
                if (startFrom >= fileLen) {
                    res = createResponse(Response.Status.RANGE_NOT_SATISFIABLE, MIME_PLAINTEXT, "");
                    res.addHeader("Content-Range", "bytes 0-0/" + fileLen);
                    res.addHeader("ETag", etag);
                } else {
                    if (endAt < 0) {
                        endAt = fileLen - 1;
                    }
                    long newLen = endAt - startFrom + 1;
                    if (newLen < 0) {
                        newLen = 0;
                    }
    
                    final long dataLen = newLen;
                    FileInputStream fis = new FileInputStream(file) {
                        @Override
                        public int available() throws IOException {
                            return (int) dataLen;
                        }
                    };
                    fis.skip(startFrom);
    
                    res = createResponse(Response.Status.PARTIAL_CONTENT, mime, fis);
                    res.addHeader("Content-Length", "" + dataLen);
                    res.addHeader("Content-Range", "bytes " + startFrom + "-" +
                            endAt + "/" + fileLen);
                    res.addHeader("ETag", etag);
                }
            } else {
                if (etag.equals(header.get("if-none-match")))
                    res = createResponse(Response.Status.NOT_MODIFIED, mime, "");
                else {
                    res = createResponse(Response.Status.OK, mime, new FileInputStream(file));
                    res.addHeader("Content-Length", "" + fileLen);
                    res.addHeader("ETag", etag);
                }
            }
        } catch (IOException ioe) {
            res = getResponse("Forbidden: Reading file failed");
        }
    
        return (res == null) ? getResponse("Error 404: File not found") : res;
    }
    
    // Announce that the file server accepts partial content requests
    private Response createResponse(Response.Status status, String mimeType, InputStream message) {
        Response res = new Response(status, mimeType, message);
        res.addHeader("Accept-Ranges", "bytes");
        return res;
    }
    
    // Announce that the file server accepts partial content requests
    private Response createResponse(Response.Status status, String mimeType, String message) {
        Response res = new Response(status, mimeType, message);
        res.addHeader("Accept-Ranges", "bytes");
        return res;
    }
    
    private Response getResponse(String message) {
        return createResponse(Response.Status.OK, "text/plain", message);
    }
    

    【讨论】:

    • getResponse() 方法定义了什么?
    • 私人响应 getResponse(String message) { return createResponse(Response.Status.OK, "text/plain", message); }
    • 我在 NanoHTTPD.java 'serve(java.lang.String, server.http.android.androidhttpserver.server.NanoHTTPD.Method, java.util.Map, java.util.Map, java.util.Map)' 是deprecated 此检查报告在指定检查范围内使用不推荐使用的代码。
    • 您使用的是哪个版本的 NanoHTTPD?我使用的是 2.1.0 版
    • 我已经通过添加serve()方法更新了上面的代码。
    猜你喜欢
    • 2013-01-15
    • 2014-06-14
    • 1970-01-01
    • 2013-02-07
    • 1970-01-01
    • 1970-01-01
    • 2012-06-14
    • 2011-03-27
    相关资源
    最近更新 更多