使用ImageIO发送图像流?

时间:2012-12-08 06:37:33

标签: java image networking streaming

我有一个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
        }

3 个答案:

答案 0 :(得分: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将开始从某个地方读取数据,但它并不像我们期望的那样在第一个图像数据的末尾。

确定基础流的位置的唯一方法是使用markreset。由于图片可能很大,因此您可能需要BufferedInputStream才能允许大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);
    }
}

答案 1 :(得分: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
}

答案 2 :(得分:0)

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

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

也许它也会对某人有所帮助。