BufferedImages nacheinander als Video anzeigen

Jakob P

Mitglied
Hallo,
ich versuche mehrere Bilder nacheinander in einem JFrame darzustellen, damit es am Ende wie ein Video aussieht.
Dafür habe ich mir folgende Klasse geschrieben:
Java:
private static class VideoStreamHandler implements Runnable{

        private JFrame streamWindow;
        private JLabel jLabel;
        protected ArrayList<BufferedImage> pendingImages = new ArrayList<>();

        public VideoStreamHandler(){
        }

        @Override
        public void run() {
            createWindow();
            streamWindow.setVisible(true);
            while (true){
                if (pendingImages.size() > 0) {
                    updateImage(pendingImages.remove(0));
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // don't care at first
                    }
                }
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        private void createWindow(){
            Dimension d = new Dimension();
            streamWindow = new JFrame("TabletConnect - View");
            streamWindow.setVisible(true);
            streamWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            String filename = "src/img/waiting_for_picture.jpeg";
            try{
                BufferedImage image = ImageIO.read(new File(filename));
                jLabel = new JLabel(new ImageIcon(image));
                streamWindow.getContentPane().add(jLabel);
                streamWindow.pack();
            } catch (Exception e){
                e.printStackTrace();
            }

        }

        private void updateImage(BufferedImage img){
            jLabel.setIcon(new ImageIcon(img));
            jLabel.repaint();
        }
    }

Wenn der VideoStreamHandler gestartet wird, soll das Bild src/img/waiting_for_picture.jpeg angezeigt werden. Sobald ein Element in pendingImages vorhanden ist, soll das Bild geupdated werden.
Leider sehe ich immer nur das Ladebild, sowie das erste Bild, das ich in Lade. Die nächsten werden nicht angezeigt.

Falls es eine bessere Lösung zum Anzeigen als Video gibt, gerne her damit, kenne mich mit Swing noch nicht wirklich aus.

Sonst meine Frage: Warum werden die Bilder danach nicht mehr angezeigt?

Vielen Dank!
 
K

kneitzel

Gast
Der Code sagt noch nicht so viel aus. Aber bezüglich dem Ansatz;

a) Wenn Du dieses Runnable als eigenen Thread laufen lässt, hast Du das Problem, dass Du in dem Thread UI Dinge machst. Das sollte dem UI Thread vorbehalten sein.

b) Wenn Du es im UI Thread laufen lässt, dann blockierst Du den UI Thread und damit ist die Anwendung blockiert.

Du könntest also ein Fenster erstellen. Dieses erstellt dann einen Thread, das nach neuen Bildern schaut und dieses dann im Hintergrund lädt. Wenn es fertig geladen ist, wird es aber an den UI Thread zur Anzeige gegeben (SwingUtilities.invokeLater wäre da das Stichwort).
In dem Werk Java ist auch eine Insel ist dazu auch etwas zu finden:
 

Jakob P

Mitglied
Der Code sagt noch nicht so viel aus. Aber bezüglich dem Ansatz;

a) Wenn Du dieses Runnable als eigenen Thread laufen lässt, hast Du das Problem, dass Du in dem Thread UI Dinge machst. Das sollte dem UI Thread vorbehalten sein.

b) Wenn Du es im UI Thread laufen lässt, dann blockierst Du den UI Thread und damit ist die Anwendung blockiert.

Du könntest also ein Fenster erstellen. Dieses erstellt dann einen Thread, das nach neuen Bildern schaut und dieses dann im Hintergrund lädt. Wenn es fertig geladen ist, wird es aber an den UI Thread zur Anzeige gegeben (SwingUtilities.invokeLater wäre da das Stichwort).
In dem Werk Java ist auch eine Insel ist dazu auch etwas zu finden:

Habe mir jetzt mal folgendes zusammengeschustert, was leider nicht ganz klappt:
Java:
private static class VideoStreamHandler implements Runnable{
        VideoStream videoStream;

        public VideoStreamHandler(VideoStream videoStream){
            this.videoStream = videoStream;
        }

        @Override
        public void run() {
            while (true){
                if (videoStream.pendingImages.size() > 0) {
                    videoStream.updateImage(videoStream.pendingImages.remove(0));
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // don't care at first
                    }
                }
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private class VideoStream extends JFrame{
        private JFrame streamWindow;
        private JLabel jLabel;
        protected ArrayList<BufferedImage> pendingImages = new ArrayList<>();

        public void start(){
            BufferedImage image;
            try {
                image = ImageIO.read(getClass().getResource("/img/waiting_for_picture.jpeg"));
                streamWindow = new JFrame("TabletConnect - View");
                streamWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                jLabel = new JLabel(new ImageIcon(image));
                streamWindow.getContentPane().add(jLabel);
                streamWindow.pack();
                streamWindow.setVisible(true);
                EventQueue.invokeLater(new VideoStreamHandler(this));

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private void updateImage(BufferedImage img){
            System.out.println("Update image");
            jLabel.setIcon(new ImageIcon(img));
            jLabel.repaint();
            System.out.println("Label repainted");
        }

    }

Jetzt wird auch das 1 Bild, das in die Liste kommt, nicht angezeigt wird.
Was mache ich falsch?
 

Jakob P

Mitglied
Was die Funktion betrifft:

1. In der Liste ist doch gar kein Bild.
2. Siehe Antwort von @kneitzel, Dein Problem ist b)

Aber es nimmt sich doch in Zeile 12 mit dem Befehl
Java:
videoStream.pendingImages.remove(0)
ein BufferedImage aus der ArrayList, die in der VideoStream Klasse ist. Die Bilder werden von einer externen Funktion hinzugefügt

Hab ich nicht das gelöst, indem ich die Klasse VideoStream hinzugefügt habe und die while in die VideoStreamHandler "ausgelagert" habe?
 

mihe7

Top Contributor
Die Bilder werden von einer externen Funktion hinzugefügt
Kann ich da oben nirgends erkennen.

Hab ich nicht das gelöst, indem ich die Klasse VideoStream hinzugefügt habe und die while in die VideoStreamHandler "ausgelagert" habe?
Was willst Du damit gelöst haben? Das Thread-Problem? Nein, damit hast Du nur das Design verschlimmbessert. Was die Funktion betrifft: Du musst schon einen Thread erstellen und starten, als etwas wie
Java:
Thread animator = new Thread(new VideoThreadHandler(...));
animator.start();
irgendwo im Code stehen haben.
 

Jakob P

Mitglied
Kann ich da oben nirgends erkennen.


Was willst Du damit gelöst haben? Das Thread-Problem? Nein, damit hast Du nur das Design verschlimmbessert. Was die Funktion betrifft: Du musst schon einen Thread erstellen und starten, als etwas wie
Java:
Thread animator = new Thread(new VideoThreadHandler(...));
animator.start();
irgendwo im Code stehen haben.

Das war mein Fehler, ich habe vergessen folgendes zu erwähnen: Die Anzeige ist Teil eines größeren Projektes, in welchem das Gerät Bilder über einen Socket geschickt bekommt. Wenn der Socket Thread eine Nachricht bekommen hat, wird folgende Funktion aufgerufen:
Java:
public void addImgToVideoStreamHandler(BufferedImage img){
    videoStream.pendingImages.add(img);
}
Daher kommen dann auch die bilder.
Der Thread wird folgendermaßen erzeugt:


Java:
public class GUI {

    private final VideoStream videoStream = new VideoStream();
    private final Thread videoStreamThread = new Thread(videoStream);;

    // hier ist die eben genannte addImgToVideoStreamHandler(BufferedImage img)
  
    public void runVideoStreamHandler(){
        videoStreamThread.start();
    }
 
Zuletzt bearbeitet:

mihe7

Top Contributor
Im Prinzip wie Dein erster Code, nur dass Du einen Thread starten musst und die UI-Methoden mit invokeLater() aufrufst.
 

Blender3D

Top Contributor
Wie würde so etwas denn ganz generell aussehen?
Die Bilder in folgender Form im Ordner ablegen.
0000.png, 0001.png .......
Den Ordner mit dem TestProgramm öffnen.
Neben dem Hauptthread werden 2 weitere Threads benutzt.
VideoSource benutzt VideoBuffer, um die Bilder zu catchen.
VideoPlayer benutzt VideoSource, um das nächste Bild bereits auf das gewünscht Format skaliert und bereits geladen zu erhalten.


[CODE lang="java" title="TestVideoPlayer"]import javax.swing.JFrame;

import player.VideoPanel;

public class TestVideoPlayer {

public static void main(String[] args) {
JFrame frame = new JFrame("Videoplayer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new VideoPanel(960, 540));
frame.pack();
frame.setVisible(true);
}
}[/CODE]
[CODE lang="java" title="VideoPanel" highlight="34-45"]import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.filechooser.FileNameExtensionFilter;

@SuppressWarnings("serial")
public class VideoPanel extends JPanel {
private JButton start = new JButton("start");
private VideoPlayer player;
private static final JFileChooser chooser = new JFileChooser();

public VideoPanel(int width, int height) {
chooser.setFileFilter(new FileNameExtensionFilter("JPG & PNG Images", "jpg", "png"));
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
chooser.setDialogTitle("Wähle Bilderordner");
int btnHeight = 30;
setPreferredSize(new Dimension(width, height + btnHeight));
setLayout(new BorderLayout());
player = new VideoPlayer(width, height, 18);
add(start, BorderLayout.NORTH);
add(player, BorderLayout.CENTER);
start.addActionListener(new ActionListener() {
private String dir = null;

@Override
public void actionPerformed(ActionEvent e) {
try {
int ok = chooser.showOpenDialog(VideoPanel.this);
if (ok == JFileChooser.APPROVE_OPTION) {
dir = chooser.getSelectedFile().getPath();
} else if (dir == null)
return;
player.setSource(dir, "");
} catch (FileNotFoundException e1) {
JOptionPane.showMessageDialog(VideoPanel.this, "Ordner enthält keine gültigen Bilder!");
return;
}
player.start();
}
});
}

}[/CODE]
[CODE lang="java" title="VideoPlayer" highlight="37-44"]import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.FileNotFoundException;
import javax.swing.JComponent;

@SuppressWarnings("serial")
public class VideoPlayer extends JComponent implements Runnable {
private VideoSource source;
private Image image = null;
private boolean running = false;
private Thread thread;
private int delay;

/**
* @param width
* Video width
* @param height
* Video height
* @param fps
* Frames per second
*/
public VideoPlayer(int width, int height, int fps) {
setPreferredSize(new Dimension(width, height));
delay = 1000 / fps;
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null)
g.drawImage(image, 0, 0, this);
}

@Override
public void run() {
sleep(500);
while (running && !source.hasEnded()) {
image = source.nextImage();
if (image != null) {
repaint();
sleep(delay);
}
}
}

public void sleep(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public void start() throws IllegalStateException {
if (source == null)
throw new IllegalStateException("No source set!");
thread = new Thread(this);
running = true;
thread.start();
}

/**
* Sets video's source.
*
* @param dir
* Directory containing video's numbered images. E.G name0001.png ,
* name0002.png ....
* @param name
* Image name
* @throws FileNotFoundException
*/
public void setSource(String dir, String name) throws FileNotFoundException {
Dimension dim = getPreferredSize();
if (running)
stop();
source = new VideoSource(dir, name, dim.width, dim.height);
}

public void stop() {
running = false;
while (thread != null && thread.isAlive())
;
thread = null;
if (source != null)
source.stop();
}
}[/CODE]
[CODE lang="java" title="VideoSource" highlight="84-86"]public class VideoSource implements Runnable {
private String dir;
private String name;
private VideoBuffer buffer;
private boolean running = true;
private Thread thread = null;
private int num = 1;
private int numImages = 0;
private String type = "png";
private final int BUFFERSIZE = 10;

public VideoSource(String dir, String name, int width, int height) throws FileNotFoundException {
this.dir = dir;
this.name = name;
buffer = new VideoBuffer(BUFFERSIZE, width, height);
File tmpFile = new File(dir);
if (!tmpFile.exists())
throw new FileNotFoundException("Can't open directory " + tmpFile.getPath());
String imageNameStart = getCurrentImageFileName();
numImages = getNumImages(tmpFile);
tmpFile = new File(imageNameStart);
if (!tmpFile.exists())
throw new FileNotFoundException("Can't find first picture " + imageNameStart);
type = imageNameStart.substring(imageNameStart.lastIndexOf(".") + 1);
start();
}

private void start() {
thread = new Thread(this);
thread.start();
}

private String getCurrentImageFileName() {
return String.format("%s/%s%04d.%s", dir, name, num, type);
}

public int getNumImages(File dir) {
int cnt = 0;
String[] files = dir.list();
for (int i = 0; i < files.length; i++) {
if (files.contains(name))
cnt++;
}
return cnt;
}

public boolean hasEnded() {
return num == numImages;
}

private Image loadNextImage() throws IOException {
if (hasEnded())
return null;
File tmp = new File(getCurrentImageFileName());
if (!tmp.exists())
throw new FileNotFoundException("Can't open " + getCurrentImageFileName());
Image image = ImageIO.read(tmp);
num++;
return image;
}

public Image nextImage() {
Image image = null;
try {
image = buffer.next();
} catch (IllegalAccessError e) {
System.err.println("Buffer empty");
}
return image;
}

@Override
public void run() {
Image img;
while (running && num < numImages) {
while (!buffer.isFull() && !hasEnded()) {
try {
img = loadNextImage();
while (!buffer.add(img))
sleep(5);
} catch (IOException e) {
e.printStackTrace();
}
}
sleep(5);
}
}

private void sleep(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public void stop() {
running = false;
while (thread != null && thread.isAlive())
;
thread = null;
}

}[/CODE]
[CODE lang="java" title="VideoBuffer"]import java.awt.Image;

public class VideoBuffer {
private static final int FULL = -1;
private Image[] buffer;
public final int width;
public final int height;
private int pos = 0;
private int last = 0;

public VideoBuffer(int size, int width, int height) {
this.width = width;
this.height = height;
reset(size);
}

public boolean add(Image img) {
if (isFull())
return false;
last = getNextEmptyPos();
if( last == FULL )
return false;
if (img.getWidth(null) != width || img.getHeight(null) != height)
img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
buffer[last] = img;
return true;
}

private int getNextEmptyPos() {
int pos = last;
for (int i = 0; i < buffer.length; i++) {
if (buffer[pos] == null) {
last = pos;
return pos;
}
pos = (pos + 1) % buffer.length;
}
return FULL;
}

public boolean hasNext() {
return buffer[pos] != null;
}

public boolean isFull() {
return getNextEmptyPos() == FULL;
}

public Image next() throws IllegalAccessError {
if (buffer[pos] == null)
throw new IllegalAccessError("No image available!");
Image tmp = buffer[pos];
buffer[pos] = null;
pos = (pos + 1) % buffer.length;
return tmp;
}

public void reset(int size) {
buffer = new Image[size];
pos = last = 0;
}

public int size() {
return buffer.length;
}

}[/CODE]
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
F BufferedImages serialisieren AWT, Swing, JavaFX & SWT 8
V 2D-Grafik Schnelles ändern von Pixelfarbe in BufferedImages AWT, Swing, JavaFX & SWT 4
GianaSisters 2D-Grafik 2 BufferedImages zusammenfügen wird Schwarz/Weiß AWT, Swing, JavaFX & SWT 10
N BufferedImages vergleichen AWT, Swing, JavaFX & SWT 7
X Video's aus JPG's oder BufferedImages generieren AWT, Swing, JavaFX & SWT 3
U 2 Fragen zu BufferedImages AWT, Swing, JavaFX & SWT 19
U BufferedImages funktionieren nicht AWT, Swing, JavaFX & SWT 36
N Problem mit BufferedImages und ram verbrauch AWT, Swing, JavaFX & SWT 6
M BufferedImages zusammenfügen AWT, Swing, JavaFX & SWT 7
J Bilder auf JPanel (im JScrollPane) nacheinander laden AWT, Swing, JavaFX & SWT 0
Jackii 2 Frames nacheinander aufrufen AWT, Swing, JavaFX & SWT 6
N Mehrere Tasks nacheinander ausführen AWT, Swing, JavaFX & SWT 7
M 2D-Grafik Mehrere Linien (nacheinander) übereinander Zeichnen AWT, Swing, JavaFX & SWT 6
M Mehrere JPanel nacheinander?! AWT, Swing, JavaFX & SWT 11
K Bilder nacheinander einfügen AWT, Swing, JavaFX & SWT 3
C zwei Bilder nacheinander anzeigen AWT, Swing, JavaFX & SWT 2
W Threads nacheinander aufführen AWT, Swing, JavaFX & SWT 5
A 1 JFrame und 3 Panel Nacheinander anzeigenlassen über Button AWT, Swing, JavaFX & SWT 5
R 2 Threads nacheinander. Einer terminiert, der andere nicht. AWT, Swing, JavaFX & SWT 9
G Wie Bilder nacheinander anstatt aufeinmal rastern? AWT, Swing, JavaFX & SWT 6
Z Mehrere Oberflächen nacheinander? AWT, Swing, JavaFX & SWT 3
M Problememit Video Capture AWT, Swing, JavaFX & SWT 5
T JavaFX H.264 AVC Video abspielen AWT, Swing, JavaFX & SWT 4
N JavaFX Video in Jpanel AWT, Swing, JavaFX & SWT 5
BRoll Frame- Video einbinden machbar? AWT, Swing, JavaFX & SWT 6
A (Youtube-) Video in Swing GUI einbinden AWT, Swing, JavaFX & SWT 13
P 2D-Grafik Viele Bilder zu einem Video AWT, Swing, JavaFX & SWT 3
M Swing Video in Swing AWT, Swing, JavaFX & SWT 4
destroflyer *.bik-Video abspielen AWT, Swing, JavaFX & SWT 4
G Transparente Grafik über JMF Video zeichnen AWT, Swing, JavaFX & SWT 2
G Über ein Video zeichnen AWT, Swing, JavaFX & SWT 3
A video-datei in java abspielen (SWING) AWT, Swing, JavaFX & SWT 8

Ähnliche Java Themen

Neue Themen


Oben