Creating a Video Component

Hi I’m somewhat new to building visual components in Ignition and am working on a video component utilizing the VLCJ library. Currently I’m running into the issue of the component not serializing onto the root container, it creates a new instance of a frame and I would like to embed it into the root container. Currently I’m using IntelliJ and Maven to create the module for Ignition 8.1. Any insight and help is much appreciated.
Class construction:

public class VLCJComponent extends AbstractVisionComponent {

    private final EmbeddedMediaPlayerComponent myPlayerComponent;

    private JPanel refPanel;
    private MediaPlayerFactory factory;
    private VideoSurface videoSurface;
    private JFrame frame;

    private String VideoPath = null;
    
    public VLCJComponent() {
        // set the perferred size of the component
        setPreferredSize(new Dimension(352, 240));
        
        JFrame frame = new JFrame("Testing VLCj");
        frame.setBounds(10,10,352,240);

        myPlayerComponent = new EmbeddedMediaPlayerComponent() {
            @Override
            protected void onBeforeRelease () {
                super.onBeforeRelease();
            }

            @Override
            protected void onAfterRelease () {
                super.onAfterRelease();
            }
            
            @Override
            public void videoOutput (MediaPlayer mediaPlayer,int newCount){
                super.videoOutput(mediaPlayer, newCount);
            }

            @Override
            public void playing (MediaPlayer mediaPlayer){
                super.playing(mediaPlayer);
            }

            @Override
            public void error (MediaPlayer mediaPlayer){
                super.error(mediaPlayer);
            }
        };
        // display the player
        myPlayerComponent.setVisible(true);
        frame.setContentPane(myPlayerComponent);

        frame.setVisible(true);
        this.add(myPlayerComponent);
    }

    /**
     * This function will play the video. It takes one argument, a String video URL path.
     */
    public void loadVideo (String path){
        myPlayerComponent.mediaPlayer().media().start(path);
    }

    public String getVideoPath(){
        return t(this, VideoPath);
    }

    public void setVideoPath(String VideoPath){
        String old = this.VideoPath;
        this.VideoPath = VideoPath;

        firePropertyChange("VideoPath",old, VideoPath);
        if (VideoPath != null){
            loadVideo(VideoPath);
        }
    }
}

I recently ran into a similar issue in my first attempt.

Things I did differently - I had to extend AbstractVisionPanel instead of AbstractVisionComponent. Also, I put all my components inside a JPanel, not a JFrame. Then I just added my components to my JPanel, and then added my JPanel to my component. You can see how I did mine here.

I’m still new to the module development as well but I did have the same issue as you so hopefully this helps.

Thank you for the reply. I have tried extending the AbstractVisionPanel instead of the ‘AbstractVisionComponent’, and ended up running into some issues. The video component did not appear on the root container when I did that. Also when I used JPanel instead of JFrame, when the component was added to the root container, it was just an empty container. I’m able to add buttons, labels, text fields etc, just cannot get this to display.

Fair warning: VLCJ is not license-compatible with Ignition, unless you pay for a commercial license. You will only be able to use such a module on your own computers.

Thank you for the information I’m just working on this for an internal module. I did come across that, I’m just looking to capture view and capture some video streams on my computers.

You definitely want a JPanel, not a JFrame, to embed this inside the actual window. When you went down that road, are you sure you added all of your components to the stack correctly?

What serialization/other errors are you getting? Full stacktrace is helpful.

Scanning the java doc for EmbeddedMediaPlayer briefly, you want to make sure release is called by your component. If you extend AbstractVisionPanel you’ll get an onShutdown method to easily hook into.

So when I do go with a JPanel and not a JFrame I end up getting this error:

15:28:22.830 [AWT-EventQueue-0] ERROR com.inductiveautomation.ignition.client.util.gui.ErrorUtil - null
com.inductiveautomation.ignition.common.xmlserialization.SerializationException: Error invoking [VLCJComponent]org.IgnitionMods.client.VLCJComponent[VLCj Video Player,150,610,350x240,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.synth.SynthBorder@f86cfeb,flags=9,maximumSize=,minimumSize=,preferredSize=java.awt.Dimension[width=352,height=240]].setVideoPath(http://XX.XX.XX.XX:9914/video4.mjpeg)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.handlers.ObjectDeserializationHandler.endSubElement(ObjectDeserializationHandler.java:82)
	at com.inductiveautomation.factorypmi.application.xmlserialization.ComponentDeserializationHandler.endSubElement(ComponentDeserializationHandler.java:102)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.XMLDeserializer$ParseContext.onElementEnd(XMLDeserializer.java:923)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:165)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.parse(BinaryParser.java:51)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.XMLDeserializer.deserializeBinary(XMLDeserializer.java:358)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.XMLDeserializer.deserialize(XMLDeserializer.java:268)
	at com.inductiveautomation.factorypmi.application.model.WindowCache._deserializeWindow(WindowCache.java:381)
	at com.inductiveautomation.factorypmi.application.model.WindowCache._loadWindow(WindowCache.java:324)
	at com.inductiveautomation.factorypmi.application.model.WindowCache.openWindow(WindowCache.java:298)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace$DesigntimeWindowOpener.openWindow(WindowWorkspace.java:3730)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace.openWindow(WindowWorkspace.java:1482)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace$4.lambda$createPanels$2(WindowWorkspace.java:623)
	at com.inductiveautomation.ignition.designer.workspacewelcome.RecentlyModifiedTilePanel$Tile.lambda$new$1(RecentlyModifiedTilePanel.java:206)
	at com.inductiveautomation.ignition.client.util.gui.Listen$1.mousePressed(Listen.java:52)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(Unknown Source)
	at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
	at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)
	at java.desktop/java.awt.Component.processEvent(Unknown Source)
	at java.desktop/java.awt.Container.processEvent(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
	at com.jidesoft.docking.DockingUtils.a(Unknown Source)
	at com.jidesoft.docking.DefaultDockingManager.handleEvent(Unknown Source)
	at com.jidesoft.docking.DefaultDockingManager$56.eventDispatched(Unknown Source)
	at java.desktop/java.awt.Toolkit$SelectiveAWTEventListener.eventDispatched(Unknown Source)
	at java.desktop/java.awt.Toolkit$ToolkitEventMulticaster.eventDispatched(Unknown Source)
	at java.desktop/java.awt.Toolkit$ToolkitEventMulticaster.eventDispatched(Unknown Source)
	at java.desktop/java.awt.Toolkit$ToolkitEventMulticaster.eventDispatched(Unknown Source)
	at java.desktop/java.awt.Toolkit$ToolkitEventMulticaster.eventDispatched(Unknown Source)
	at java.desktop/java.awt.Toolkit.notifyAWTEventListeners(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source)
	at java.desktop/java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source)
	at java.desktop/java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.reflect.InvocationTargetException: null
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
	at com.inductiveautomation.ignition.common.xmlserialization.deserialization.handlers.ObjectDeserializationHandler.endSubElement(ObjectDeserializationHandler.java:69)
	... 63 common frames omitted
Caused by: java.lang.IllegalStateException: The video surface component must be displayable
	at uk.co.caprica.vlcj.player.embedded.videosurface.ComponentVideoSurface.attach(ComponentVideoSurface.java:66)
	at uk.co.caprica.vlcj.player.embedded.VideoSurfaceApi.attachVideoSurface(VideoSurfaceApi.java:80)
	at uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer.onBeforePlay(EmbeddedMediaPlayer.java:125)
	at uk.co.caprica.vlcj.player.base.ControlsApi.play(ControlsApi.java:51)
	at uk.co.caprica.vlcj.player.base.MediaPlayerLatch.play(MediaPlayerLatch.java:66)
	at uk.co.caprica.vlcj.player.base.ControlsApi.start(ControlsApi.java:65)
	at uk.co.caprica.vlcj.player.base.MediaApi.start(MediaApi.java:419)
	at uk.co.caprica.vlcj.player.base.MediaApi.start(MediaApi.java:114)
	at org.IgnitionMods.client.VLCJComponent.loadVideo(VLCJComponent.java:89)
	at org.IgnitionMods.client.VLCJComponent.setVideoPath(VLCJComponent.java:102)
	... 68 common frames omitted

I am not sure that all the components are added to the stack correctly. I’ve been trying to find some information regarding that but have not come across anything yet.
I’ve added the onShutdown release and tried it and when I add the component to the root container I get this:

Look deeper into the stack of the error you posted:
Caused by: java.lang.IllegalStateException: The video surface component must be displayable

That’s coming from the media player component, not Ignition. You need to match Ignition’s lifecycle to your component. Something to try: Leave any setVideoPath/loadVideo calls out of the constructor (maybe put them in onStartup?).

I will dig deeper into the stack and look into Ignition’s component lifecycle. I appreciate the feedback. Thank you.

Thank you for the help and pointing me in the right direction. I was able to get this to work. The reason I was getting the empty container was because I did not set the size on the media player. I put the loadVideo on a button and it’s working. Again, thank you so much for the feedback.

1 Like