2012-12-08 8 views
5

Ho un ServerSocket e un Socket impostato in modo che ServerSocket invii un flusso di immagini utilizzando ImageIO.write (....) e il socket tenta di leggerli e aggiornare un JFrame con loro. Quindi mi sono chiesto se ImageIO potesse rilevare la fine di un'immagine. (Non ho assolutamente alcuna conoscenza del formato JPEG, quindi l'ho provato invece)Invia un flusso di immagini usando ImageIO?

Apparentemente no.

Sul lato server, ho inviato le immagini in modo continuo utilizzando ImageIO.write (...) in loop con un po 'di sonno in mezzo. Sul lato client, ImageIO leggeva la prima immagine senza problemi, ma sulla successiva restituiva null. Questo è confusionario. Mi aspettavo che bloccasse la lettura della prima immagine (perché pensa che l'immagine successiva faccia ancora parte della stessa immagine), o riesca a leggerla tutte (perché funziona). Cosa sta succedendo? Sembra che ImageIO rilevi la fine della prima immagine, ma non la seconda. (Le immagini, a proposito, sono simili l'una all'altra) C'è un modo semplice per eseguire lo streaming di immagini come questa o devo creare il mio meccanismo che legge i byte in un buffer finché non raggiunge un determinato byte o una sequenza di byte, a che punto legge l'immagine fuori dal buffer?

Questa è la parte utile del mio codice server:

 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"); 
     } 

E il mio codice cliente:

 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 
     } 
+0

Ancora più strano: ho impostato il formato per ImageIO.write su png, e ha funzionato (beh, in una certa misura: metà delle immagini ricevute erano nulle) – DankMemes

+0

Sembra che dovrò implementare il sistema di buffering dopo tutto. :( – DankMemes

+1

* "un flusso di immagini utilizzando ImageIO.write (....) e il socket tenta di leggerle e aggiornare un JFrame con loro." * Se questo è per la chat Web o condivisione dello schermo o simili, nota che richiede flussi video o immagini intelligentemente ridotte al minimo (ad es. un ritaglio solo dell'area modificata, per uno screen cast). L'invio di singole immagini farà sprintare il video risultante come un ghiacciaio (ignorando i cambiamenti climatici globali). –

risposta

4

ImageIO.read(InputStream) crea un ImageInputStream e chiede read(ImageInputStream) internamente. Quest'ultimo metodo è documentato per chiudere il flusso quando ha finito di leggere l'immagine.

Quindi, in teoria, si può solo ottenere il ImageReader, creare un ImageInputStream te stesso, e hanno la ImageReader lettura dal ImageInputStream ripetutamente.

Tranne, sembra che uno ImageInputStream sia progettato per funzionare con una sola immagine (che può contenere o meno cornici multiple). Se chiami ImageReader.read(0) più di una volta, si riavvolgerà all'inizio dei dati del flusso (memorizzati nella cache) ogni volta, restituendo la stessa immagine più e più volte. ImageReader.read(1) cercherà un secondo fotogramma in un'immagine a più fotogrammi, che ovviamente non ha senso con un JPEG.

Quindi, forse possiamo creare un ImageInputStream, leggere ImageReader da esso e quindi creare un nuovo ImageInputStream per gestire i dati di immagine successivi nello stream, giusto? Tranne che sembra che ImageInputStream faccia tutti i tipi di memorizzazione nella cache, read-ahead e pushback, il che rende piuttosto difficile conoscere la posizione di lettura dell'inputstream integrato. Il prossimo ImageInputStream inizierà a leggere i dati da qualche parte, ma non è alla fine dei dati della prima immagine come ci aspetteremmo.

L'unico modo per essere certi della posizione del flusso sottostante è con mark e reset. Poiché le immagini possono essere grandi, probabilmente avrai bisogno di un BufferedInputStream per consentire un grande readLimit.

questo ha funzionato per me:

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); 
    } 
} 
0

Anche se forse non è il modo ottimale per fare questo il seguente codice dovrebbe farti passato il problema vostro avere. Come una risposta precedente ha notato che ImageIO non sta lasciando il flusso alla fine dell'immagine, questo troverà la via per l'immagine successiva.

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 
} 
Problemi correlati