/*
* Copyright (c) 2007 Wayne Meissner
*
* This file is part of gstreamer-java.
*
* This code is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3 only, as
* published by the Free Software Foundation.
*
* This code 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 Lesser General Public License
* version 3 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gstreamer.swing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import javax.swing.AbstractAction;
import javax.swing.BoundedRangeModel;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.gstreamer.State;
import org.gstreamer.media.PlayBinMediaPlayer;
import org.gstreamer.media.MediaPlayer;
import org.gstreamer.media.event.EndOfMediaEvent;
import org.gstreamer.media.event.MediaAdapter;
import org.gstreamer.media.event.MediaListener;
import org.gstreamer.media.event.StartEvent;
import org.gstreamer.media.event.StopEvent;
import com.sun.jna.Platform;
/**
*
*/
public class VideoPlayer extends javax.swing.JPanel {
private static final long serialVersionUID = 1964694975227372646L;
private static final SwingExecutorService swingExec = new SwingExecutorService();
public VideoPlayer(MediaPlayer player) {
mediaPlayer = player;
videoComponent = new VideoComponent();
mediaPlayer.setVideoSink(videoComponent.getElement());
setLayout(new BorderLayout());
add(videoComponent, BorderLayout.CENTER);
controls = new JPanel();
controls.setLayout(new BoxLayout(controls, BoxLayout.X_AXIS));
add(controls, BorderLayout.SOUTH);
controls.add(new PopupVolumeButton(volumeModel));
controls.add(new JButton(rewAction));
controls.add(new JButton(togglePlayAction));
controls.add(new JButton(fwdAction));
playAction.setEnabled(true);
pauseAction.setEnabled(false);
stopAction.setEnabled(false);
Object paintValue = UIManager.put("Slider.paintValue", Boolean.FALSE);
controls.add(new JSlider(positionModel = new PipelinePositionModel(mediaPlayer.getPipeline())));
UIManager.put("Slider.paintValue", paintValue);
controls.add(positionLabel = new JLabel("00:00:00"));
//
// On MacOSX, need a spacer so the window resize control is visible
//
if (Platform.isMac()) {
Box spacer = Box.createHorizontalBox();
spacer.add(Box.createHorizontalStrut(16));
controls.add(spacer);
}
controls.setVisible(false);
//
// Add a listener to update the media position label
//
positionModel.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
BoundedRangeModel m = (BoundedRangeModel)e.getSource();
int value = m.getValue();
String text = String.format("%02d:%02d:%02d", value / 3600, (value / 60) % 60, value % 60);
positionLabel.setText(text);
}
});
mediaPlayer.addMediaListener(swingExec.wrap(MediaListener.class, mediaListener));
// Propate key events to the video component without giving it focus
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent evt) {
for (KeyListener l : videoComponent.getKeyListeners()) {
l.keyPressed(evt);
}
}
@Override
public void keyReleased(KeyEvent evt) {
for (KeyListener l : videoComponent.getKeyListeners()) {
l.keyReleased(evt);
}
}
});
}
public VideoPlayer(File file) {
this(file.toURI());
}
public VideoPlayer(String uri) {
this(parseURI(uri));
}
public VideoPlayer(URI uri) {
this(new PlayBinMediaPlayer("swing player", swingExec));
mediaPlayer.setURI(uri);
}
private static URI parseURI(String uri) {
try {
URI u = new URI(uri);
if (u.getScheme() == null) {
throw new URISyntaxException(uri, "Invalid URI scheme");
}
return u;
} catch (URISyntaxException e) {
File f = new File(uri);
if (!f.exists()) {
throw new IllegalArgumentException("Invalid URI/file " + uri, e);
}
return f.toURI();
}
}
public VideoComponent getVideoComponent() {
return videoComponent;
}
public void setOpacity(float alpha) {
float[] c = getBackground().getColorComponents(new float[3]);
setBackground(new Color(c[0], c[1], c[2], alpha));
videoComponent.setOpacity(alpha);
}
public float getOpacity() {
return videoComponent.getOpacity();
}
@Override
public void setOpaque(boolean isOpaque) {
super.setOpaque(isOpaque);
if (videoComponent != null) {
videoComponent.setOpaque(isOpaque);
}
}
public void setKeepAspect(boolean keepAspect) {
videoComponent.setKeepAspect(keepAspect);
}
public void setControlsVisible(boolean visible) {
controls.setVisible(visible);
revalidate();
}
public void setURI(URI uri) {
mediaPlayer.setURI(uri);
}
public void setInputFile(File file) {
mediaPlayer.setURI(file.toURI());
}
/**
* Obtain the MediaPlayer instance used by this VideoPlayer
* @return The MediaPlayer used
*/
public MediaPlayer getMediaPlayer() {
return mediaPlayer;
}
ImageIcon playIcon = loadIcon("actions/media-playback-start");
ImageIcon pauseIcon = loadIcon("actions/media-playback-pause");
ImageIcon stopIcon = loadIcon("actions/media-playback-stop");
ImageIcon fwdIcon = loadIcon("actions/media-seek-forward");
ImageIcon rewIcon = loadIcon("actions/media-seek-backward");
private AbstractAction playAction = new AbstractAction("", playIcon) {
private static final long serialVersionUID = 1714683114563367993L;
public void actionPerformed(ActionEvent e) {
mediaPlayer.play();
}
};
private AbstractAction pauseAction = new AbstractAction("", pauseIcon) {
private static final long serialVersionUID = 1045795748586088103L;
public void actionPerformed(ActionEvent e) {
mediaPlayer.pause();
}
};
private AbstractAction stopAction = new AbstractAction("", stopIcon) {
private static final long serialVersionUID = 881154743003615413L;
public void actionPerformed(ActionEvent e) {
mediaPlayer.stop();
}
};
private AbstractAction fwdAction = new AbstractAction("", fwdIcon) {
private static final long serialVersionUID = -6661129153122209564L;
public void actionPerformed(ActionEvent e) {
positionModel.setValue(positionModel.getValue() + 60);
}
};
private AbstractAction rewAction = new AbstractAction("", rewIcon) {
private static final long serialVersionUID = 4450598460336990513L;
public void actionPerformed(ActionEvent e) {
positionModel.setValue(positionModel.getValue() - 60);
}
};
private class TogglePlayAction extends AbstractAction {
private static final long serialVersionUID = -333774735255804279L;
public TogglePlayAction() {
super("", pauseIcon);
}
public void setState(State state) {
switch (state) {
case PAUSED:
case NULL:
putValue(SMALL_ICON, playIcon);
break;
case PLAYING:
default:
putValue(SMALL_ICON, pauseIcon);
break;
}
}
public void actionPerformed(ActionEvent e) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
setState(State.PAUSED);
} else {
mediaPlayer.play();
setState(State.PLAYING);
}
}
}
private MediaListener mediaListener = new MediaAdapter() {
@Override
public void endOfMedia(EndOfMediaEvent evt) {
this.stop(evt);
}
@Override
public void pause(StopEvent evt) {
pauseAction.setEnabled(false);
playAction.setEnabled(true);
togglePlayAction.setState(State.PAUSED);
}
@Override
public void start(StartEvent evt) {
playAction.setEnabled(false);
pauseAction.setEnabled(true);
stopAction.setEnabled(true);
togglePlayAction.setState(State.PLAYING);
}
@Override
public void stop(StopEvent evt) {
playAction.setEnabled(true);
pauseAction.setEnabled(false);
stopAction.setEnabled(false);
togglePlayAction.setState(State.NULL);
}
};
private TogglePlayAction togglePlayAction = new TogglePlayAction();
private static ImageIcon loadIcon(String name) {
return loadIcon(16, name);
}
private static ImageIcon loadIcon(int size, String name) {
String path = "/org/freedesktop/tango/" + size + "x" + size + "/" + name + ".png";
URL url = VideoPlayer.class.getResource(path);
if (url != null) {
return new ImageIcon(url);
} else {
throw new RuntimeException("Cannot locate icon for " + name);
}
}
private BoundedRangeModel volumeModel = new DefaultBoundedRangeModel() {
private static final long serialVersionUID = -4728390119456427400L;
@Override
public int getMaximum() {
return 100;
}
@Override
public int getMinimum() {
return 0;
}
@Override
public int getValue() {
return Math.min(100, (int) ((mediaPlayer.getVolume() * 100d) + 0.5));
}
@Override
public void setValue(int percent) {
double volume = Math.max(Math.min((double) percent, 100d), 0d) / 100.0d;
mediaPlayer.setVolume(volume);
}
};
PipelinePositionModel positionModel;
private MediaPlayer mediaPlayer;
private JComponent controls;
private JLabel positionLabel;
private VideoComponent videoComponent;
}