5

我设置了一个 ServerSocket 和一个 Socket,因此 ServerSocket 使用 ImageIO.write(....) 发送图像流,并且 Socket 尝试读取它们并用它们更新 JFrame。所以我想知道 ImageIO 是否可以检测到图像的结尾。(我完全不知道JPEG格式,所以我测试了它)

显然不是。

在服务器端,我通过在循环中使用 ImageIO.write(...) 连续发送图像,中间有一些睡眠。在客户端,ImageIO 读取第一个图像没有问题,但在下一个它返回 null。这令人困惑。我期待它要么阻止读取第一张图像(因为它认为下一张图像仍然是同一图像的一部分),要么成功读取所有图像(因为它有效)。到底是怎么回事?看起来 ImageIO 检测到了第一个图像的结尾,但没有检测到第二个图像的结尾。(顺便说一下,图像彼此大致相似)有没有一种简单的方法可以像这样流式传输图像,还是我必须制作自己的机制,将字节读入缓冲区,直到它达到指定的字节或序列字节,此时它从缓冲区中读取图像?

这是我的服务器代码的有用部分:

        while(true){
            Socket sock=s.accept();
            System.out.println("Connection");
            OutputStream out=sock.getOutputStream();
            while(!socket.isClosed()){
                BufferedImage img=//get image
                ImageIO.write(img, "jpg", out);
                Thread.sleep(100);
            }
            System.out.println("Closed");
        }

我的客户代码:

        Socket s=new Socket(InetAddress.getByName("localhost"), 1998);
        InputStream in=s.getInputStream();
        while(!s.isClosed()){
            BufferedImage img=ImageIO.read(in);
            if(img==null)//this is what happens on the SECOND image
            else // do something useful with the image
        }
4

3 回答 3

4

ImageIO.read(InputStream)在内部创建一个ImageInputStream和调用read(ImageInputStream)。后一种方法被记录为在读取图像完成后关闭流。

因此,理论上,您可以只获取ImageReader,创建ImageInputStream自己,然后反复ImageReader阅读。ImageInputStream

除了,它似乎ImageInputStream被设计为只处理一个图像(可能包含也可能不包含多个帧)。如果您ImageReader.read(0)多次调用,它每次都会倒回到(缓存的)流数据的开头,一遍又一遍地为您提供相同的图像。 ImageReader.read(1)将在多帧图像中寻找第二帧,这对于 JPEG 当然没有意义。

所以,也许我们可以创建一个 ImageInputStream,让 ImageReader 从中读取,然后创建一个新的 ImageInputStream 来处理流中的后续图像数据,对吧?除此之外,它似乎ImageInputStream进行了各种缓存、预读和回送,这使得知道包装的 InputStream 的读取位置非常困难。下一个 ImageInputStream 将从某处开始读取数据,但它不像我们预期的那样位于第一个图像数据的末尾。

确定基础流位置的唯一方法是使用markand reset。由于图像可能很大,您可能需要 aBufferedInputStream来允许较大的readLimit.

这对我有用:

private static final int MAX_IMAGE_SIZE = 50 * 1024 * 1024;

static void readImages(InputStream stream)
throws IOException {
    stream = new BufferedInputStream(stream);

    while (true) {
        stream.mark(MAX_IMAGE_SIZE);

        ImageInputStream imgStream =
            ImageIO.createImageInputStream(stream);

        Iterator<ImageReader> i = 
            ImageIO.getImageReaders(imgStream);
        if (!i.hasNext()) {
            logger.log(Level.FINE, "No ImageReaders found, exiting.");
            break;
        }

        ImageReader reader = i.next();
        reader.setInput(imgStream);

        BufferedImage image = reader.read(0);
        if (image == null) {
            logger.log(Level.FINE, "No more images to read, exiting.");
            break;
        }

        logger.log(Level.INFO,
            "Read {0,number}\u00d7{1,number} image",
            new Object[] { image.getWidth(), image.getHeight() });

        long bytesRead = imgStream.getStreamPosition();

        stream.reset();
        stream.skip(bytesRead);
    }
}
于 2012-12-08T12:18:59.537 回答
0

我遇到了同样的问题并找到了这篇文章。@VGR 的评论启发了我深入研究这个问题,最终我意识到 ImageIO 无法处理同一流中的一组图像。所以我创建了解决方案(在 Scala 中,抱歉)并写了一些详细信息和内部信息的博客文章。

http://blog.animatron.com/post/80779366767/a-fix-for-imageio-making-animated-gifs-from-streaming

也许它也会帮助某人。

于 2014-03-26T19:08:33.430 回答
0

虽然可能不是执行此操作的最佳方法,但以下代码可以帮助您解决遇到的问题。正如先前的回答所指出的,ImageIO 不会在图像末尾离开流,这将找到通往下一个图像的方式。

int imageCount = in.read();
for (int i = 0; i < imageCount; i ++){
    BufferedImage img = ImageIO.read(in);
    while (img == null){img = ImageIO.read(in);}
    //Do what ever with img
}
于 2013-03-14T06:19:55.110 回答