Displaying a Videos Sequence on a Java Component and what’s the deal with SwingUtilities.invokeLater()?
October 21st, 2011I kind of struggled a bit with displaying a video sequence on top of a self-written Java component based on Swing and Graphics2D. Let’s say we have some VideoDecoder with a public interface such as:
public class VideoDecoder {
public VideoDecoder(VideoPanel panel, String videoFile) { ... }
public void run() { ... }
}
So the VideoDecoders run() method happily decodes the video sequence and pushes the frames into the VideoPanel which then simply displays the frame. The most simple version of such a VideoPanel is probably:
public class VideoPanel extends JPanel
{
private Image image;
@Override
public void paint(Graphics g)
{
if(this.image != null) {
g.drawImage(this.image, 0, 0, null);
}
}
public void setImage(Image image) {
this.image = image;
repaint();
}
}
So once it comes to interaction with GUI components you usually do not want to call a method like run() which blocks until it is finished from some Action Handler to avoid freezing your GUI. So I initially came up with some code that I kind of borrowed from my GUI designer:
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
VideoDecoder decoder = new VideoDecoder(videoPanel, filename);
decoder.run();
}
});
So the thing with SwingUtilities.invokeLater() is, that we actually queue a job that will be run by swing on the event dispatch thread. So once our job gets started it hangs the GUI because they share the same thread. Therefore there will be no update on the video panel until the decoder is done.
Simple solution, run the decoder as a separate thread. Change the class definition to:
public class VideoDecoder extends Thread { ... }
and modifiy the Action Handler to:
VideoDecoder decoder = new VideoDecoder(videoPanel, filename); decoder.start();
Of course you have to take care of not creating multiple instances of the decoder or other ways of handling asynchronous data access to the video panel.
Lesson Learned:
Do not use SwingUtilities.invokeLater() if your job either blocks, or takes up a significant amount of time to complete. Instead run it on a separate thread and save yourself a lot of trouble!











