5

ServerSocketとSocketを設定しているので、ServerSocketはImageIO.write(....)を使用して画像のストリームを送信し、Socketはそれらを読み取ってJFrameを更新しようとします。そこで、ImageIOが画像の終わりを検出できるかどうか疑問に思いました。(私はJPEG形式の知識がまったくないので、代わりにテストしました)

どうやら、そうではありません。

サーバー側では、ImageIO.write(...)をループで使用し、その間にいくつかのスリープを設定して、画像を継続的に送信しました。クライアント側では、ImageIOは最初の画像を問題なく読み取りましたが、次の画像ではnullを返しました。これは紛らわしいです。最初の画像の読み取りをブロックするか(次の画像がまだ同じ画像の一部であると見なすため)、またはすべての画像の読み取りに成功する(機能するため)ことを期待していました。何が起こっている?ImageIOは最初の画像の終わりを検出しているように見えますが、2番目の画像は検出していません。(ちなみに、画像は大まかに似ています)このような画像をストリーミングする簡単な方法はありますか、それとも指定されたバイトまたはシーケンスに達するまでバイトをバッファに読み込む独自のメカニズムを作成する必要がありますか?バイト、どの時点でバッファから画像を読み取りますか?

これは私のサーバーコードの便利な部分です:

        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作成し、から繰り返し読み取ることができます。ImageInputStreamImageReaderImageInputStream

ただし、ImageInputStreamは 1 つだけの画像 (複数のフレームを含む場合と含まない場合があります) で動作するように設計されているようです。複数回呼び出すとImageReader.read(0)、毎回 (キャッシュされた) ストリーム データの先頭に巻き戻され、同じ画像が何度も表示されます。 ImageReader.read(1)マルチフレーム画像で 2 番目のフレームを探しますが、もちろん JPEG では意味がありません。

では、ImageInputStream を作成し、そこから ImageReader を読み取らせてから、新しい ImageInputStream を作成して、ストリーム内の後続の画像データを処理できるのではないでしょうか? ただし、ImageInputStreamあらゆる種類のキャッシング、先読み、およびプッシュバックを実行しているように見えるため、ラップされた InputStream の読み取り位置を知ることは非常に困難です。次の ImageInputStream はどこかからデータの読み取りを開始しますが、最初のイメージのデータの最後ではありません。

基になるストリームの位置を確認する唯一の方法は、 と を使用することmarkですresetBufferedInputStream画像は大きくなる可能性があるため、大きな を許可するにはおそらく が必要です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 に答える