1

我正在开发一个 Java 应用程序,它将从 IP 摄像机流式传输视频。来自 IP 摄像机的 MJPEG 格式的视频流。协议如下...

--ipcamera (\r\n)
Content-Type: image/jpeg (\r\n)
Content-Length: {length of frame} (\r\n)
(\r\n)
{frame}
(\r\n)
--ipcamera (\r\n)
etc.

我尝试使用诸如 BufferedReader 和 Scanner 之类的类来读取直到“\r\n”,但是这些是针对文本而不是二进制数据的,因此它会损坏。有没有办法读取二进制流,直到遇到“\r\n”?这是我当前的(损坏的)代码。

编辑:我已经让它工作了。我更新了下面的代码。但是,这样做真的很慢。我不确定它是否与 ArrayList 有任何关系,但它可能是罪魁祸首。任何加快代码速度的指针?目前单帧需要 500 毫秒到 900 毫秒。

public void run() {
    long startTime = System.currentTimeMillis();
    try {
        URLConnection urlConn = url.openConnection();
        urlConn.setReadTimeout(15000);
        urlConn.connect();
        urlStream = urlConn.getInputStream();
        DataInputStream dis = new DataInputStream(urlStream);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ArrayList<Byte> bytes = new ArrayList<Byte>();
        byte cur;
        int curi;
        byte[] curBytes;
        int length = 0;
        while ((curi = dis.read()) != -1) {
            cur = (byte) curi;
            bytes.add(cur);
            curBytes = getPrimativeArray(bytes);
            String curBytesString = new String(curBytes, "UTF-8");
            if (curBytesString.equals("--ipcamera\r\n")) {
                bytes.clear();
                continue;
            } else if (curBytesString.equals("Content-Type: image/jpeg\r\n")) {
                bytes.clear();
                continue;
            } else if (curBytesString.matches("^Content-Length: ([0-9]+)\r\n$")) {
                length = Integer.parseInt(curBytesString.replace("Content-Length: ", "").trim());
                bytes.clear();
                continue;
            } else if (curBytesString.equals("\r\n")) {
                if (length == 0) {
                    continue;
                }
                byte[] frame = new byte[length];
                dis.readFully(frame, 0, length);
                writeFrame(frame);
                bytes.clear();
                break;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    long curTime = System.currentTimeMillis() - startTime;
    System.out.println(curTime);
}

private byte[] getPrimativeArray(ArrayList<Byte> array) {
    byte[] bytes = new byte[array.size()];
    for (int i = 0; i < array.size(); i++) {
        bytes[i] = array.get(i).byteValue();
    }
    return bytes;
}

private void writeFrame(byte[] bytes) throws IOException {
    File file = new File("C:\\test.jpg");
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(bytes);
    fos.close();
    System.out.println("done");
}
4

4 回答 4

3

目前,您无法处理在框架部分中读取数据的情况。

一个粗略的假设是:

当前版本:

else if (line.equals("") && length != 0)

可能更正确的版本:

else if (!line.equals("") && length != 0)
于 2013-07-31T06:08:37.347 回答
2

除了使用一些不良做法并假设您URLConnection正确交付数据之外,如果您在读取帧数据后将长度重置为零,则您发布的示例似乎有效。

} else if (line.equals("") && length != 0) {
    char[] buf = new char[length];
    reader.read(buf, 0, length);
    baos.write(new String(buf).getBytes());
    //break;
    length = 0;  // <-- reset length
}

请注意,这样所有的帧数据都是ByteArrayOutputStream连续写入的。如果您不希望这样,您应该ByteArrayOutputStream为遇到的每个新框架创建一个新框架。

于 2013-07-31T07:00:20.217 回答
2

您不能使用 BufferedReader 读取二进制文件,它会损坏它。我想保持简单,请使用 DataInputStream.readLine()。虽然不理想,但在您的情况下它可能是最简单的。

于 2013-07-31T06:14:48.510 回答
1
  1. 您不能将 aBufferedReader用于传输的一部分,然后将其他一些流用于其余的传输。它将填充其BufferedReader缓冲区并窃取您想与其他流一起读取的一些数据。请DataInputStream.readLine(),注意它已被弃用,或者使用由URLConnection.

  2. 你确定不用吧?URLConnection为您读取标题。如果您想要内容长度,请使用 API 获取。您要阅读的内容从传输的主体开始。

于 2013-07-31T07:10:36.557 回答