如何在 Java 中有效渲染视频(AWT??)

发布于 2024-09-26 22:35:42 字数 2897 浏览 8 评论 0原文

在我的项目中,我有一个类 FrameProducer ,每次创建视频帧时都会触发事件。每个帧都是作为 java.awt.Image.BufferedImage 对象返回的图像。

我有 2 个 FrameProducer 对象,希望在屏幕上同时渲染它们生成的 BufferedImage。我希望屏幕上渲染的图片可缩放(也就是说,当我拖动应用程序窗口的角落时,渲染的视频会变小或变大)。

您认为如何最好地实现这一目标?

我考虑过使用嵌入在 java.awt.Frame 中的 java.awt.Graphics2D ,但我不知道如何做到这一点,或者如果这是最好的选择。我只需要这个来实现算法可视化,它不需要漂亮和闪亮,只需快速和简单即可。我可以使用哪些建议或一些现成的代码?


编辑: 好的,我按照 Rekin 的建议实施了该解决方案,并且它有效。但由于我不是 Java 专家,也绝对不是 Swing 专家,我我想请您对我的代码提出善意的评论 - 我相信将来很多人都会从中受益。

正如我所说,有一个 FrameProducer (不用介意实现):

public abstract class FrameProducer extends Observable {
    public abstract BufferedImage getFrame();

    public void fireEvent() {
        setChanged();
        notifyObservers();
    }
}

然后还有一个 FrameRenderer 等待来自 FrameProducer 的事件(一个使用 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());
        }
    }
}

然后还有需要返工的部分: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);
    }
}

这一切都在 main 方法中初始化:

public class FrameAccessTest {
    public static void main(String[] args) {
        final FrameProducer frameGrabber = 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();
                }
            }
        });
    }
}

如何以这样的方式实现 initContents(),即 MainFrame's 构造函数中提供的所有视频流作为 FrameProducer[] 连续渲染并且它们是可扩展的?

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 = 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

静若繁花 2024-10-03 22:35:43

我会使用 JPanel 并重写 paintComponent 方法。在其中,我将使用 Graphics2D.drawImage() 将容器的宽度和高度作为其参数。

AWT 方法似乎更接近裸机,但由于 JDK 6 在 Swing 渲染管道方面带来了很多改进,所以我会选择 Swing 和 AWT 方法。 Java2D 方法。它由可用硬件加速(尽可能使用 Windows 上的 DirectDraw 或 OpenGL),并在良好的 API 后面隐藏大量低级细节。例如,您可以免费获得双缓冲。

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.

玉环 2024-10-03 22:35:43

重写 paintComponent 方法并使用 Graphics2D 使用 drawImage 绘制缓冲图像。此方法已重载,也可用于缩放图像。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文