How to effectively render video in Java (AWT??)
In my project I've got a class FrameProducer
firing events each time a video frame has been created. Each frame is an image returned as an java.awt.Image.BufferedImage
object.
I've got 2 FrameProducer
objects and would like to render BufferedImage's
produced by them simultaneously on the screen. I would like the picture rendered on the screen to be scalable (that is, when I drag the application-window's corner the rendered video gets smaller or bigger).
How do you think this is best to be achieved?
I've considered using java.awt.Graphics2D
embedded in an java.awt.Frame
, but I don't know how such a thing can be done, or if this is the best choice. I just need this for algorithm visualisation, it doesn't need to be nice and shiny, just fast and easy. What would some suggestions or some ready code be that I could use?
Edit: OK, I implemented the solution as Rekin suggested, and it works. But as I'm not an Java expert and definitely not a Swing expert, I'd like to ask you for kind remarks on my code - I'm sure many will benefit from that in the future.
As I said, there is a FrameProducer
(never mind the implementation):
public abstract class FrameProducer extends Observable {
public abstract BufferedImage getFrame();
public void fireEvent() {
setChanged();
notifyObservers();
}
}
Then there is also a FrameRenderer
waiting for events from the FrameProducer
(a simple observer-pattern implementation using java.util
):
public class FrameRenderer extends JPanel implements Observer {
private BufferedImage frame;
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(frame, null, 0, 0);
g2d.dispose();
}
@Override
public void update(Observable observable, Object arg) {
System.out.println("Cought an event from " + observable.getClass());
if (observable instanceof FrameProducer) {
frame = ((FrameProducer) observable).getFrame();
paint(getGraphics());
}
}
}
And then there is also the thing that needs rework: the MainFrame
.
public class MainFrame extends JFrame {
FrameProducer[] producers;
public MainFrame(FrameProducer[] producers) {
this.setTitle("Playing what you feed.");
this.producers = producers;
initContents();
setVisible(true);
}
private void initContents() {
FrameRenderer renderer = new FrameRenderer();
renderer.setLocation(0, 0);
this.add(renderer);
producers[0].addObserver(renderer);
}
}
It all gets initialised in the main method:
public class FrameAccessTest {
public static void main(String[] args) {
final FrameProducer frameGrabber开发者_StackOverflow = new CameraFrameGrabber("vfw://0");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
MainFrame mainFrame =
new MainFrame(new FrameProducer[] {frameGrabber});
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
How do I implement initContents() in such a way, that all the video streams provided in MainFrame's
constructor as FrameProducer[]
get rendered in a row and that they are scalable?
I'd go with JPanel and overriding paintComponent
method. In it I'd use Graphics2D.drawImage()
giving width and height of container as a parameters to it.
The AWT approach seems closer to bare metal, but since JDK 6 brought a lot of improvements in Swing rendering pipeline, I would go the Swing & Java2D approach. It's accelerated by available hardware (using DirectDraw on Windows or OpenGL wherever possible) and hides a lot of low level details behind a nice API. For example You get double buffering for free.
Override the paintComponent
method and use Graphics2D to draw your buffered image using drawImage
. This method is overloaded and can be used to scale the image as well.
精彩评论