Package uk.co.caprica.vlcj.player.embedded

Source Code of uk.co.caprica.vlcj.player.embedded.DefaultEmbeddedMediaPlayer$OverlayWindowAdapter

/*
* This file is part of VLCJ.
*
* VLCJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VLCJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VLCJ.  If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2009, 2010, 2011, 2012, 2013, 2014 Caprica Software Limited.
*/

package uk.co.caprica.vlcj.player.embedded;

import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Window;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;

import javax.swing.SwingUtilities;

import uk.co.caprica.vlcj.binding.LibVlc;
import uk.co.caprica.vlcj.binding.internal.libvlc_instance_t;
import uk.co.caprica.vlcj.logger.Logger;
import uk.co.caprica.vlcj.player.DefaultMediaPlayer;
import uk.co.caprica.vlcj.player.embedded.videosurface.CanvasVideoSurface;

/**
* Implementation of a media player that renders video to an embedded Canvas component.
* <p>
* This implementation supports the use of an 'overlay' window that will track the video surface
* position and size. Such an overlay could be used to paint custom graphics over the top of the
* video.
* <p>
* The overlay window should be non-opaque - support for this depends on the JVM, desktop window
* manager and graphics device hardware and software.
* <p>
* The overlay also has some significant limitations, it is a component that covers the video
* surface component and will prevent mouse and keyboard events from being processed by the video
* surface. Workarounds to delegate the mouse and keyboard events to the underlying Canvas may be
* possible but that is a responsibility of the overlay component itself and not these bindings.
* <p>
* The overlay will also 'lag' the main application frame when the frame is dragged - the event used
* to track the frame position does not fire until after the window drag operation has completed
* (i.e. the mouse pointer is released).
* <p>
* A further limitation is that the overlay will not appear when full-screen exclusive mode is used
* - if an overlay is required in full-screen mode then the full-screen mode must be simulated (by
* re-sizing the main window, removing decorations and so on).
* <p>
* If an overlay is used, then because the window is required to be non-opaque then it will appear
* in front of <strong>all</strong> other desktop windows, including application dialog windows. For
* this reason, it may be necessary to disable the overlay while displaying dialog boxes, or when
* the window is deactivated.
* <p>
* The overlay implementation in this class simply keeps a supplied window in sync with the video
* surface. It is the responsibility of the client application itself to supply an appropriate
* overlay component.
* <p>
* <strong>Finally, the overlay is experimental and support for the overlay may be changed or
* removed.</strong>
*/
public class DefaultEmbeddedMediaPlayer extends DefaultMediaPlayer implements EmbeddedMediaPlayer {

    /**
     * Full-screen strategy implementation, may be <code>null</code>.
     */
    private final FullScreenStrategy fullScreenStrategy;

    /**
     * Listener implementation used to keep the overlay position and size in sync with the video
     * surface.
     */
    private final OverlayComponentAdapter overlayComponentAdapter;

    /**
     * Listener implementation used to keep the overlay visibility state in sync with the video
     * surface.
     */
    private final OverlayWindowAdapter overlayWindowAdapter;

    /**
     * Component to render the video to.
     */
    private CanvasVideoSurface videoSurface;

    /**
     * Optional overlay component.
     */
    private Window overlay;

    /**
     * Track the requested overlay enabled/disabled state so it can be restored when needed.
     */
    private boolean requestedOverlay;

    /**
     * Track whether or not the overlay should be restored when the video surface is shown/hidden.
     */
    private boolean restoreOverlay;

    /**
     * Create a new media player.
     * <p>
     * Full-screen will not be supported.
     *
     * @param libvlc native interface
     * @param instance libvlc instance
     */
    public DefaultEmbeddedMediaPlayer(LibVlc libvlc, libvlc_instance_t instance) {
        this(libvlc, instance, null);
    }

    /**
     * Create a new media player.
     *
     * @param libvlc native interface
     * @param instance libvlc instance
     * @param fullScreenStrategy
     */
    public DefaultEmbeddedMediaPlayer(LibVlc libvlc, libvlc_instance_t instance, FullScreenStrategy fullScreenStrategy) {
        super(libvlc, instance);

        this.fullScreenStrategy = fullScreenStrategy;
        this.overlayComponentAdapter = new OverlayComponentAdapter();
        this.overlayWindowAdapter = new OverlayWindowAdapter();
    }

    @Override
    public void setVideoSurface(CanvasVideoSurface videoSurface) {
        Logger.debug("setVideoSurface(videoSurface={})", videoSurface);
        // Keep a hard reference to the video surface component
        this.videoSurface = videoSurface;
        // The video surface is not actually attached to the media player until the
        // media is played
    }

    @Override
    public void attachVideoSurface() {
        Logger.debug("attachVideoSurface()");
        if(videoSurface != null) {
            // The canvas component must be visible at this point otherwise the call
            // to the native library will fail
            if(videoSurface.canvas().isVisible()) {
                videoSurface.attach(libvlc, this);
            }
            else {
                // This is an error
                throw new IllegalStateException("The video surface is not visible");
            }
        }
        else {
            // This is not necessarily an error
            Logger.debug("Can't attach video surface since no video surface has been set");
        }
    }

    @Override
    public void toggleFullScreen() {
        Logger.debug("toggleFullScreen()");
        if(fullScreenStrategy != null) {
            setFullScreen(!fullScreenStrategy.isFullScreenMode());
        }
    }

    @Override
    public void setFullScreen(boolean fullScreen) {
        Logger.debug("setFullScreen(fullScreen={})", fullScreen);
        if(fullScreenStrategy != null) {
            if(fullScreen) {
                fullScreenStrategy.enterFullScreenMode();
            }
            else {
                fullScreenStrategy.exitFullScreenMode();
            }
        }
    }

    @Override
    public boolean isFullScreen() {
        Logger.debug("isFullScreen()");
        if(fullScreenStrategy != null) {
            return fullScreenStrategy.isFullScreenMode();
        }
        else {
            return false;
        }
    }

    @Override
    public BufferedImage getVideoSurfaceContents() {
        Logger.debug("getVideoSurfaceContents()");
        try {
            Rectangle bounds = videoSurface.canvas().getBounds();
            bounds.setLocation(videoSurface.canvas().getLocationOnScreen());
            return new Robot().createScreenCapture(bounds);
        }
        catch(Exception e) {
            throw new RuntimeException("Failed to get video surface contents", e);
        }
    }

    @Override
    public Window getOverlay() {
        Logger.debug("getOverlay()");
        return overlay;
    }

    @Override
    public void setOverlay(Window overlay) {
        Logger.debug("setOverlay(overlay={})", overlay);
        if(videoSurface != null) {
            // Disable the current overlay if there is one
            enableOverlay(false);
            // Remove the existing overlay if there is one
            removeOverlay();
            // Add the new overlay, but do not enable it
            addOverlay(overlay);
        }
        else {
            throw new IllegalStateException("Can't set an overlay when there's no video surface");
        }
    }

    @Override
    public void enableOverlay(boolean enable) {
        Logger.debug("enableOverlay(enable={})", enable);
        requestedOverlay = enable;
        if(overlay != null) {
            if(enable) {
                if(!overlay.isVisible()) {
                    overlay.setLocation(videoSurface.canvas().getLocationOnScreen());
                    overlay.setSize(videoSurface.canvas().getSize());
                    Window window = (Window)SwingUtilities.getAncestorOfClass(Window.class, videoSurface.canvas());
                    window.addComponentListener(overlayComponentAdapter);
                    overlay.setVisible(true);
                }
            }
            else {
                if(overlay.isVisible()) {
                    overlay.setVisible(false);
                    Window window = (Window)SwingUtilities.getAncestorOfClass(Window.class, videoSurface.canvas());
                    window.removeComponentListener(overlayComponentAdapter);
                }
            }
        }
    }

    @Override
    public boolean overlayEnabled() {
        Logger.debug("overlayEnabled()");
        return overlay != null && overlay.isVisible();
    }

    @Override
    public void setEnableMouseInputHandling(boolean enable) {
        Logger.debug("setEnableMouseInputHandling(enable={})", enable);
        libvlc.libvlc_video_set_mouse_input(mediaPlayerInstance(), enable ? 1 : 0);
    }

    @Override
    public void setEnableKeyInputHandling(boolean enable) {
        Logger.debug("setEnableKeyInputHandling(enable={})", enable);
        libvlc.libvlc_video_set_key_input(mediaPlayerInstance(), enable ? 1 : 0);
    }

    /**
     * Install an overlay component.
     *
     * @param overlay overlay window
     */
    private void addOverlay(Window overlay) {
        Logger.debug("addOverlay(overlay={})", overlay);
        if(overlay != null) {
            this.overlay = overlay;
            Window window = (Window)SwingUtilities.getAncestorOfClass(Window.class, videoSurface.canvas());
            if(window != null) {
                window.addWindowListener(overlayWindowAdapter);
            }
            else {
                // This should not be possible
                Logger.warn("Failed to find a Window ancestor for the video surface Canvas");
            }
        }
    }

    /**
     * Remove the overlay component.
     *
     * @param overlay overlay window
     */
    private void removeOverlay() {
        Logger.debug("removeOverlay()");
        if(overlay != null) {
            Window window = (Window)SwingUtilities.getAncestorOfClass(Window.class, videoSurface.canvas());
            window.removeWindowListener(overlayWindowAdapter);
            overlay = null;
        }
    }

    @Override
    protected final void onBeforePlay() {
        Logger.debug("onBeforePlay()");
        attachVideoSurface();
    }

    /**
     * Component event listener to keep the overlay component in sync with the video surface
     * component.
     */
    private final class OverlayComponentAdapter extends ComponentAdapter {

        @Override
        public void componentResized(ComponentEvent e) {
            Logger.trace("componentResized(e={})", e);
            overlay.setSize(videoSurface.canvas().getSize());
        }

        @Override
        public void componentMoved(ComponentEvent e) {
            Logger.trace("componentMoved(e={})", e);
            overlay.setLocation(videoSurface.canvas().getLocationOnScreen());
        }

        @Override
        public void componentShown(ComponentEvent e) {
            Logger.trace("componentShown(e={})", e);
            showOverlay();
        }

        @Override
        public void componentHidden(ComponentEvent e) {
            Logger.trace("componentHidden(e={})", e);
            hideOverlay();
        }
    }

    /**
     * Window event listener to hide the overlay when the video window is hidden, and vice versa.
     */
    private final class OverlayWindowAdapter extends WindowAdapter {

        @Override
        public void windowIconified(WindowEvent e) {
            Logger.trace("windowIconified(e={})", e);
            // Nothing, this is taken care of by "windowDeactivated"
        }

        @Override
        public void windowDeiconified(WindowEvent e) {
            Logger.trace("windowDeiconified(e={})", e);
            showOverlay();
        }

        @Override
        public void windowDeactivated(WindowEvent e) {
            Logger.trace("windowDeactivated(e={})", e);
            hideOverlay();
        }

        @Override
        public void windowActivated(WindowEvent e) {
            Logger.trace("windowActivated(e={})", e);
            showOverlay();
        }
    }

    /**
     * Make the overlay visible.
     */
    private void showOverlay() {
        Logger.trace("showOverlay()");
        if(restoreOverlay) {
            enableOverlay(true);
        }
    }

    /**
     * Hide the overlay.
     */
    private void hideOverlay() {
        Logger.trace("hideOverlay()");
        if(requestedOverlay) {
            restoreOverlay = true;
            enableOverlay(false);
        }
        else {
            restoreOverlay = false;
        }
    }
}
TOP

Related Classes of uk.co.caprica.vlcj.player.embedded.DefaultEmbeddedMediaPlayer$OverlayWindowAdapter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.