de.fu_berlin.inf.dpp.ui.views.VideoPlayerView.java Source code

Java tutorial

Introduction

Here is the source code for de.fu_berlin.inf.dpp.ui.views.VideoPlayerView.java

Source

/*
 * DPP - Serious Distributed Pair Programming
 * (c) Freie Universitt Berlin - Fachbereich Mathematik und Informatik - 2010
 * (c) Stephan Lau - 2010
 * 
 * This program 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 1, or (at your option)
 * any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package de.fu_berlin.inf.dpp.ui.views;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import de.fu_berlin.inf.dpp.SarosPluginContext;
import org.apache.log4j.Logger;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.CoolBar;
import org.eclipse.swt.widgets.CoolItem;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.picocontainer.annotations.Inject;

import de.fu_berlin.inf.dpp.Saros;
import de.fu_berlin.inf.dpp.annotations.Component;
import de.fu_berlin.inf.dpp.observables.VideoSessionObservable;
import de.fu_berlin.inf.dpp.preferences.PreferenceConstants;
import de.fu_berlin.inf.dpp.ui.ImageManager;
import de.fu_berlin.inf.dpp.ui.actions.VideoSharingAction;
import de.fu_berlin.inf.dpp.ui.widgets.explanation.ListExplanationComposite.ListExplanation;
import de.fu_berlin.inf.dpp.ui.widgets.explanation.explanatory.ListExplanatoryViewPart;
import de.fu_berlin.inf.dpp.util.Utils;
import de.fu_berlin.inf.dpp.util.ValueChangeListener;
import de.fu_berlin.inf.dpp.videosharing.VideoSharing;
import de.fu_berlin.inf.dpp.videosharing.VideoSharing.Mode;
import de.fu_berlin.inf.dpp.videosharing.VideoSharing.VideoSharingSession;
import de.fu_berlin.inf.dpp.videosharing.activities.KeyPressedVideoActivity;
import de.fu_berlin.inf.dpp.videosharing.activities.MouseClickedVideoActivity;
import de.fu_berlin.inf.dpp.videosharing.activities.MouseClickedVideoActivity.Button;
import de.fu_berlin.inf.dpp.videosharing.activities.MouseWheeledVideoActivity;
import de.fu_berlin.inf.dpp.videosharing.activities.VideoActivity;
import de.fu_berlin.inf.dpp.videosharing.decode.Decoder;
import de.fu_berlin.inf.dpp.videosharing.decode.DecodingStatisticPacket;
import de.fu_berlin.inf.dpp.videosharing.player.VideoDisplay;

/**
 * @author s-lau
 * @author bkahlert (ExplanatoryViewPart)
 */
@Component(module = "ui")
public class VideoPlayerView extends ListExplanatoryViewPart implements VideoDisplay {
    private static Logger log = Logger.getLogger(VideoPlayerView.class);

    public static final String ID = "de.fu_berlin.inf.dpp.ui.views.VideoPlayerView";

    @Inject
    protected VideoSharing videoSharing;
    @Inject
    protected Saros saros;
    @Inject
    protected VideoSessionObservable videoSharingSessionObservable;

    protected IPreferenceStore preferences;
    protected boolean resample = false;
    protected boolean keepAspectRatio = true;

    /* howto */
    protected ListExplanation howTo = new ListExplanation(SWT.ICON_INFORMATION,
            "In order to share your screen with a session participant please do the following steps:",
            "Open Saros session", "Select a participant with whom you want to share your screen",
            "Click on the \"" + VideoSharingAction.TOOLTIP_START_SESSION + "\" button in the view's toolbar");

    /* content */
    protected Composite parent;
    protected Canvas canvas = null;
    protected VideoCanvas videoCanvas = null;

    protected CoolBar statusBar = null;
    protected DecodingStatisticPacket lastShownStatus = null;

    /* used labels */
    protected Label fps;
    protected Label bitrate;
    protected Label delay;

    protected Rectangle imageSize;
    protected BufferedImage nextImage = null;

    protected BlockingQueue<BufferedImage> images = new ArrayBlockingQueue<BufferedImage>(1);

    protected ObjectOutputStream activityOutput;

    protected Rectangle clientArea;

    public VideoPlayerView() {
        SarosPluginContext.initComponent(this);

        preferences = saros.getPreferenceStore();
        updateResample();
        updateKeepAspectRatio();
        preferences.addPropertyChangeListener(new IPropertyChangeListener() {

            public void propertyChange(PropertyChangeEvent event) {
                if (event.getProperty().equals(PreferenceConstants.PLAYER_RESAMPLE))
                    updateResample();
                if (event.getProperty().equals(PreferenceConstants.PLAYER_KEEP_ASPECT_RATIO))
                    updateKeepAspectRatio();
            }
        });

        this.videoSharingSessionObservable.add(new ValueChangeListener<VideoSharingSession>() {

            public void setValue(final VideoSharingSession newValue) {
                Utils.runSafeSWTSync(log, new Runnable() {
                    public void run() {
                        if (newValue == null)
                            reset();
                        else if (newValue.getMode().equals(Mode.CLIENT) || newValue.getMode().equals(Mode.LOCAL))
                            initialize();
                    }
                });
            }
        });
    }

    protected void updateResample() {
        resample = preferences.getBoolean(PreferenceConstants.PLAYER_RESAMPLE);
    }

    protected void updateKeepAspectRatio() {
        keepAspectRatio = preferences.getBoolean(PreferenceConstants.PLAYER_KEEP_ASPECT_RATIO);
    }

    @Override
    public void createContentPartControl(Composite parent) {
        this.showExplanation(this.howTo);

        this.parent = parent;
        GridLayout layout = new GridLayout();
        parent.setLayout(layout);

        createCanvas(parent);
        createStatusBar(parent);
        createActionBar();
    }

    private void createCanvas(Composite parent) {
        canvas = new Canvas(parent, SWT.EMBEDDED);
        GridData align = new GridData(SWT.FILL, SWT.FILL, true, true);
        canvas.setLayoutData(align);

        canvas.addListener(SWT.Resize, new Listener() {

            public void handleEvent(Event event) {
                clientArea = canvas.getClientArea();
            }
        });
        clientArea = canvas.getClientArea();
    }

    private void createStatusBar(Composite parent) {
        this.statusBar = new CoolBar(parent, SWT.FLAT | SWT.HORIZONTAL);
        this.statusBar.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
        createStatisticBar(statusBar);
        this.statusBar.pack();
    }

    private void createStatisticBar(CoolBar coolBar) {
        this.fps = createLabel(coolBar, "FPS  ", "rendered frames per second");
        this.bitrate = createLabel(coolBar, "BITRATE  ", "average video bitrate");
        this.delay = createLabel(coolBar, "DLY", "average delay of rendered frames to original video time");
    }

    protected void createActionBar() {
        final IActionBars actionBars = getViewSite().getActionBars();
        final IToolBarManager toolBarManager = actionBars.getToolBarManager();

        toolBarManager.add(new ChangeSourceModeAction());
        toolBarManager.add(new StopVideoSessionAction());
        toolBarManager.add(new PauseVideoSessionAction());
    }

    protected Label createLabel(CoolBar coolBar, String text, String toolTip) {
        CoolItem coolItem = new CoolItem(coolBar, SWT.NONE);
        Label label = new Label(coolBar, SWT.NONE);

        label.setText(text);
        label.setToolTipText(toolTip);
        label.pack();

        Point size = label.getSize();
        size = label.computeSize(size.x, size.y);
        coolItem.setSize(size);
        coolItem.setMinimumSize(size);
        coolItem.setPreferredSize(size);

        coolItem.setControl(label);

        return label;
    }

    @Override
    public void setFocus() {
        if (videoCanvas != null)
            videoCanvas.requestFocus();
    }

    public Dimension getImageDimension(Dimension dimension) {
        if (!resample)
            return null;
        int iWidth = clientArea.width, iHeight = clientArea.height;
        if (keepAspectRatio) {
            double ratioInput = (double) dimension.width / dimension.height;
            double ratioPlayer = (double) clientArea.width / clientArea.height;
            if (ratioInput < ratioPlayer) {
                iHeight = clientArea.height;
                iWidth = (int) (ratioInput * clientArea.height);
            } else {
                iWidth = clientArea.width;
                iHeight = (int) ((1 / ratioInput) * clientArea.width);
            }
        }
        return new Dimension(iWidth, iHeight);
    }

    public void setActivityOutput(ObjectOutputStream out) {
        this.activityOutput = out;
    }

    public void updateImage(BufferedImage image) {
        if (image == null)
            return;
        if (videoCanvas != null)
            videoCanvas.updateImage(image);
        else {
            // NOP, skip until videoCanvas is initialized
        }
    }

    /**
     * @swt
     */
    public void initialize() {
        if (videoCanvas == null && !canvas.isDisposed())
            videoCanvas = new VideoCanvas(SWT_AWT.new_Frame(canvas));
        canvas.update();
        this.hideExplanation();
    }

    /**
     * @swt
     */
    public void reset() {
        this.showExplanation(this.howTo);
        if (videoCanvas != null)
            videoCanvas.parentFrame.dispose();
        videoCanvas = null;
        canvas.update();
        fps.setText("");
        bitrate.setText("");
        delay.setText("");
    }

    protected void updateStatusbar() {
        VideoSharingSession session = videoSharingSessionObservable.getValue();
        if (session == null)
            return;

        Decoder decoder = session.getDecoder();
        if (decoder == null)
            return;

        DecodingStatisticPacket statistic = decoder.getLastDecodingStatistic();
        if (statistic == null || statistic == lastShownStatus)
            return;

        lastShownStatus = statistic;

        Utils.runSafeSWTAsync(log, new Runnable() {
            public void run() {
                if (!parent.isDisposed()) {
                    fps.setText(String.valueOf(lastShownStatus.getFps()));
                    bitrate.setText(Utils.formatByte(lastShownStatus.getBytes()));
                    delay.setText(String.valueOf(lastShownStatus.getDelay()));
                }
            }
        });

    }

    class ChangeSourceModeAction extends Action {

        public ChangeSourceModeAction() {
            setToolTipText("Change mode of image source");
            setImageDescriptor(ImageManager.getImageDescriptor("icons/elcl16/chvdsrc.png"));

            videoSharingSessionObservable.addAndNotify(new ValueChangeListener<VideoSharingSession>() {

                public void setValue(VideoSharingSession newValue) {
                    setEnabled(newValue != null
                            && (newValue.getMode().equals(Mode.CLIENT) || newValue.getMode().equals(Mode.LOCAL)));
                }
            });
        }

        @Override
        public void run() {
            Utils.runSafeAsync(log, new Runnable() {
                public void run() {
                    VideoSharingSession session = VideoPlayerView.this.videoSharingSessionObservable.getValue();
                    if (session != null) {
                        session.requestChangeImageSourceMode();
                    }
                }
            });
        }
    }

    class StopVideoSessionAction extends Action {

        public StopVideoSessionAction() {
            setToolTipText("Stop running session");
            setImageDescriptor(
                    PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_ELCL_STOP));

            videoSharingSessionObservable.addAndNotify(new ValueChangeListener<VideoSharingSession>() {

                public void setValue(VideoSharingSession newValue) {
                    setEnabled(newValue != null
                            && (newValue.getMode().equals(Mode.CLIENT) || newValue.getMode().equals(Mode.LOCAL)));
                }
            });
        }

        @Override
        public void run() {
            Utils.runSafeAsync(log, new Runnable() {
                public void run() {
                    VideoSharingSession session = VideoPlayerView.this.videoSharingSessionObservable.getValue();
                    if (session != null) {
                        session.requestStop();
                        setEnabled(false);
                    }
                }
            });
        }

    }

    class PauseVideoSessionAction extends Action {

        public PauseVideoSessionAction() {
            setImageDescriptor(ImageManager.getImageDescriptor("icons/elcl16/pausevideo.gif"));
            setToolTipText("Pause");
            setEnabled(false);
            videoSharingSessionObservable.add(new ValueChangeListener<VideoSharingSession>() {

                public void setValue(VideoSharingSession newValue) {
                    updateState();
                }
            });
        }

        @Override
        public void run() {
            Utils.runSafeAsync(log, new Runnable() {
                public void run() {
                    VideoSharingSession session = VideoPlayerView.this.videoSharingSessionObservable.getValue();
                    if (session != null) {
                        session.pause();
                        updateState();
                    }
                }
            });
        }

        protected void updateState() {
            VideoSharingSession session = videoSharingSessionObservable.getValue();
            boolean enabled = session != null
                    && (session.getMode().equals(Mode.CLIENT) || session.getMode().equals(Mode.LOCAL));
            setEnabled(enabled);
            if (enabled && session != null) {
                if (session.isPaused()) {
                    setImageDescriptor(ImageManager.getImageDescriptor("icons/elcl16/resumevideo.gif"));
                    setToolTipText("Resume");
                } else {
                    setImageDescriptor(ImageManager.getImageDescriptor("icons/elcl16/pausevideo.gif"));
                    setToolTipText("Pause");
                }
            } else {
                setImageDescriptor(ImageManager.getImageDescriptor("icons/elcl16/pausevideo.gif"));
                setToolTipText(null);
            }
        }
    }

    /**
     * Canvas for displaying video, inspired by <a
     * href="http://www.dreamincode.net/forums/showtopic113451.htm">Java Game
     * (Actually the most efficient way to repaint)</a>
     * 
     * @author s-lau
     */
    protected class VideoCanvas extends java.awt.Canvas implements VideoDisplay {
        private static final long serialVersionUID = 3182304031585212029L;

        protected Dimension lastImageDimension = null;
        protected Frame parentFrame;

        protected boolean paintInProgress = false;

        protected VideoCanvas(Frame parent) {
            this.parentFrame = parent;
            setBackground(Color.BLACK);
            setIgnoreRepaint(true);

            parent.add(this);
            parent.setVisible(true);
            this.createBufferStrategy(2);
            parent.validate();

            addMouseListener(new ActivityMouseClickListener());
            addMouseWheelListener(new ActivityMouseWheelListener());
            addKeyListener(new ActivityKeyboardListener());
        }

        public void setActivityOutput(ObjectOutputStream out) {
            activityOutput = out;
        }

        public Dimension getImageDimension(Dimension dimenesion) {
            return null;
        }

        public void updateImage(BufferedImage image) {
            updateImage(image, 0, 0);
        }

        public void updateImage(BufferedImage tile, int x, int y) {
            if (paintInProgress) {
                log.warn("Rendering seems too slow.");
                return;
            }
            paintInProgress = true;

            lastImageDimension = new Dimension(tile.getWidth(), tile.getHeight());
            Dimension size = new Dimension(clientArea.width, clientArea.height);

            setSize(size);
            parentFrame.setSize(size);

            BufferStrategy strategy = getBufferStrategy();
            Graphics graphics = strategy.getDrawGraphics();

            graphics.fillRect(0, 0, clientArea.width, clientArea.height);
            graphics.drawImage(tile, x, y, null);

            graphics.dispose();

            strategy.show();
            Toolkit.getDefaultToolkit().sync();

            paintInProgress = false;

            updateStatusbar();
        }

        protected void sendActivity(VideoActivity activity) {
            if (activityOutput == null)
                return;

            try {
                activityOutput.writeObject(activity);
            } catch (IOException e) {
                log.warn("Could not send activity: ", e);
            }
        }

        public void reset() {
            // nothing to do
        }

        public void initialize() {
            // nothing to do
        }

        protected class ActivityMouseClickListener implements MouseListener {

            public void mouseClicked(MouseEvent e) {
                if (lastImageDimension == null)
                    return;

                Button pressedButton = null;

                switch (e.getButton()) {
                case MouseEvent.BUTTON1:
                    pressedButton = Button.LEFT;
                    break;
                case MouseEvent.BUTTON2:
                    pressedButton = Button.MIDDLE;
                    break;
                case MouseEvent.BUTTON3:
                    pressedButton = Button.RIGHT;
                    break;
                default:
                    pressedButton = Button.NONE;
                }

                sendActivity(new MouseClickedVideoActivity(e.getX(), e.getY(), lastImageDimension.width,
                        lastImageDimension.height, pressedButton));
            }

            public void mouseEntered(MouseEvent e) {
                // NOP
            }

            public void mouseExited(MouseEvent e) {
                // NOP
            }

            public void mousePressed(MouseEvent e) {
                // NOP
            }

            public void mouseReleased(MouseEvent e) {
                // NOP
            }

        }

        protected class ActivityMouseWheelListener implements MouseWheelListener {

            public void mouseWheelMoved(MouseWheelEvent e) {
                if (lastImageDimension == null)
                    return;

                sendActivity(new MouseWheeledVideoActivity(e.getX(), e.getY(), lastImageDimension.width,
                        lastImageDimension.height, e.getWheelRotation()));
            }

        }

        protected class ActivityKeyboardListener implements KeyListener {

            public void keyPressed(KeyEvent e) {
                // NOP
            }

            public void keyReleased(KeyEvent e) {
                // NOP
            }

            public void keyTyped(KeyEvent e) {
                char c = e.getKeyChar();
                if (c != KeyEvent.CHAR_UNDEFINED)
                    sendActivity(new KeyPressedVideoActivity(c));
            }

        }
    }

}