/*
This file is part of JFLICKS.
JFLICKS 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.
JFLICKS 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 JFLICKS. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jflicks.player.vlcj;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.Arrays;
import javax.swing.InputMap;
import javax.swing.KeyStroke;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JDialog;
import javax.swing.JLayeredPane;
import javax.swing.Timer;
import uk.co.caprica.vlcj.player.MediaPlayerEventAdapter;
import uk.co.caprica.vlcj.player.MediaPlayer;
import uk.co.caprica.vlcj.player.MediaPlayerFactory;
import uk.co.caprica.vlcj.player.direct.DirectMediaPlayer;
import uk.co.caprica.vlcj.player.direct.RenderCallbackAdapter;
import org.jflicks.player.BasePlayer;
import org.jflicks.player.Bookmark;
import org.jflicks.player.PlayState;
import org.jflicks.util.LogUtil;
import org.jflicks.util.Util;
/**
* This player uses the vlcj library to embed vlc. It current supports
* streaming UDP packets. It can play files but does not do well with
* transport streams.
*
* @author Doug Barnum
* @version 1.0
*/
public class Vlcj extends BasePlayer {
private JDialog dialog;
private MediaPlayerFactory mediaPlayerFactory;
private DirectMediaPlayer directMediaPlayer;
private ImagePanel keyPanel;
private String[] args;
private String[] urls;
private int urlIndex;
private BufferedImage bufferedImage;
private FrameRenderCallback frameRenderCallback;
/**
* Simple constructor.
*/
public Vlcj() {
setType(PLAYER_VIDEO_STREAM_UDP);
setTitle("Vlcj");
ImagePanel pan = new ImagePanel();
pan.setLayout(new BorderLayout());
pan.setFocusable(true);
InputMap map = pan.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
QuitAction quitAction = new QuitAction();
map.put(KeyStroke.getKeyStroke("Q"), "q");
pan.getActionMap().put("q", quitAction);
InfoAction infoAction = new InfoAction();
map.put(KeyStroke.getKeyStroke("I"), "i");
pan.getActionMap().put("i", infoAction);
UpAction upAction = new UpAction();
map.put(KeyStroke.getKeyStroke("UP"), "up");
pan.getActionMap().put("up", upAction);
DownAction downAction = new DownAction();
map.put(KeyStroke.getKeyStroke("DOWN"), "down");
pan.getActionMap().put("down", downAction);
LeftAction leftAction = new LeftAction();
map.put(KeyStroke.getKeyStroke("LEFT"), "left");
pan.getActionMap().put("left", leftAction);
RightAction rightAction = new RightAction();
map.put(KeyStroke.getKeyStroke("RIGHT"), "right");
pan.getActionMap().put("right", rightAction);
EnterAction enterAction = new EnterAction();
map.put(KeyStroke.getKeyStroke("ENTER"), "enter");
pan.getActionMap().put("enter", enterAction);
GuideAction guideAction = new GuideAction();
map.put(KeyStroke.getKeyStroke("G"), "g");
pan.getActionMap().put("g", guideAction);
PauseAction pauseAction = new PauseAction();
map.put(KeyStroke.getKeyStroke("P"), "p");
pan.getActionMap().put("p", pauseAction);
PageUpAction pageupAction = new PageUpAction();
map.put(KeyStroke.getKeyStroke("PAGE_UP"), "pageup");
pan.getActionMap().put("pageup", pageupAction);
PageDownAction pagedownAction = new PageDownAction();
map.put(KeyStroke.getKeyStroke("PAGE_DOWN"), "pagedown");
pan.getActionMap().put("pagedown", pagedownAction);
RewindAction rewindAction = new RewindAction();
map.put(KeyStroke.getKeyStroke("R"), "r");
pan.getActionMap().put("r", rewindAction);
ForwardAction forwardAction = new ForwardAction();
map.put(KeyStroke.getKeyStroke("F"), "f");
pan.getActionMap().put("f", forwardAction);
SkipBackwardAction skipBackwardAction = new SkipBackwardAction();
map.put(KeyStroke.getKeyStroke("Z"), "z");
pan.getActionMap().put("z", skipBackwardAction);
SkipForwardAction skipForwardAction = new SkipForwardAction();
map.put(KeyStroke.getKeyStroke("X"), "x");
pan.getActionMap().put("x", skipForwardAction);
AudioSyncPlusAction audioSyncPlusAction = new AudioSyncPlusAction();
map.put(KeyStroke.getKeyStroke("N"), "n");
pan.getActionMap().put("n", audioSyncPlusAction);
AudioSyncMinusAction audioSyncMinusAction = new AudioSyncMinusAction();
map.put(KeyStroke.getKeyStroke("M"), "m");
pan.getActionMap().put("m", audioSyncMinusAction);
setKeyPanel(pan);
String[] vlcArgs = {
"--no-video-title-show",
"--deinterlace-mode=bob",
};
setArgs(vlcArgs);
}
/**
* We allow custom arguments to the VLC object.
*
* @return The arguments as a String array.
*/
public String[] getArgs() {
String[] result = null;
if (args != null) {
result = Arrays.copyOf(args, args.length);
}
return (result);
}
/**
* We allow custom arguments to the VLC object.
*
* @param array The arguments as a String array.
*/
public void setArgs(String[] array) {
if (array != null) {
args = Arrays.copyOf(array, array.length);
} else {
args = null;
}
}
private int getUrlIndex() {
return (urlIndex);
}
private void setUrlIndex(int i) {
urlIndex = i;
}
private String[] getUrls() {
return (urls);
}
private void setUrls(String[] array) {
urls = array;
}
private JDialog getDialog() {
return (dialog);
}
private void setDialog(JDialog d) {
dialog = d;
}
private ImagePanel getKeyPanel() {
return (keyPanel);
}
private void setKeyPanel(ImagePanel p) {
keyPanel = p;
}
private MediaPlayerFactory getMediaPlayerFactory() {
return (mediaPlayerFactory);
}
private void setMediaPlayerFactory(MediaPlayerFactory f) {
mediaPlayerFactory = f;
}
private DirectMediaPlayer getDirectMediaPlayer() {
return (directMediaPlayer);
}
private void setDirectMediaPlayer(DirectMediaPlayer p) {
directMediaPlayer = p;
}
@Override
public void setSize(Rectangle r) {
if (r != null) {
JDialog d = getDialog();
if (d != null) {
d.setBounds(r);
}
ImagePanel p = getKeyPanel();
if (p != null) {
p.setBounds(0, 0, r.width, r.height);
}
}
}
/**
* {@inheritDoc}
*/
public boolean supportsPause() {
return (true);
}
/**
* {@inheritDoc}
*/
public boolean supportsAutoSkip() {
return (false);
}
/**
* {@inheritDoc}
*/
public boolean supportsMaximize() {
return (false);
}
/**
* {@inheritDoc}
*/
public boolean supportsSeek() {
return (true);
}
/**
* {@inheritDoc}
*/
public void play(String ... urls) {
if ((urls != null) && (urls.length > 0)) {
if (urls.length == 1) {
setUrlIndex(-1);
setUrls(null);
} else {
setUrlIndex(0);
setUrls(urls);
}
play(urls[0], null);
}
}
/**
* {@inheritDoc}
*/
public synchronized void play(String url, Bookmark b) {
if (!isPlaying()) {
setAudioOffset(0);
setPaused(false);
setPlaying(true);
setCompleted(false);
Rectangle r = null;
if (isFullscreen()) {
r = getFullscreenRectangle();
} else {
r = getRectangle();
}
int x = (int) r.getX();
int y = (int) r.getY();
int width = (int) r.getWidth();
int height = (int) r.getHeight();
JDialog win = new JDialog(getFrame());
win.setFocusable(true);
win.setUndecorated(true);
win.setBounds(x, y, width, height);
setDialog(win);
ImagePanel pan = getKeyPanel();
JLayeredPane lpane = new JLayeredPane();
setLayeredPane(lpane);
pan.setBounds(0, 0, width, height);
bufferedImage = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration()
.createCompatibleImage(width, height);
frameRenderCallback = new FrameRenderCallback(bufferedImage);
pan.setBufferedImage(bufferedImage);
lpane.add(pan, Integer.valueOf(100));
win.add(lpane, BorderLayout.CENTER);
Cursor cursor = Util.getNoCursor();
if (cursor != null) {
pan.setCursor(cursor);
}
win.setVisible(true);
LogUtil.log(LogUtil.DEBUG, "vlc url <" + url + ">");
String[] vlcArgs = getArgs();
LogUtil.log(LogUtil.DEBUG, "vlcargs " + (vlcArgs != null));
if (vlcArgs != null) {
for (int i = 0; i < vlcArgs.length; i++) {
LogUtil.log(LogUtil.DEBUG, "vlcargs " + i + " " + vlcArgs[i]);
}
}
MediaPlayerFactory mpf = new MediaPlayerFactory(vlcArgs);
setMediaPlayerFactory(mpf);
DirectMediaPlayer mediaPlayer =
mpf.newDirectMediaPlayer(width, height, frameRenderCallback);
mediaPlayer.addMediaPlayerEventListener(
new MyMediaPlayerEventAdapter());
//mediaPlayer.setPlaySubItems(true);
setDirectMediaPlayer(mediaPlayer);
mediaPlayer.playMedia(url);
if (b != null) {
float ftmp = (float) b.getPosition();
mediaPlayer.setPosition(ftmp / 1000.0f);
}
}
}
/**
* {@inheritDoc}
*/
public void stop() {
LogUtil.log(LogUtil.DEBUG, "stop called!");
setPaused(false);
setPlaying(false);
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
p.stop();
p.release();
setDirectMediaPlayer(null);
}
MediaPlayerFactory f = getMediaPlayerFactory();
if (f != null) {
f.release();
setMediaPlayerFactory(null);
}
JDialog w = getDialog();
if (w != null) {
w.setVisible(false);
w.dispose();
setDialog(null);
}
}
/**
* {@inheritDoc}
*/
public void maximize(boolean b) {
setMaximized(b);
}
/**
* {@inheritDoc}
*/
public void pause(boolean b) {
setPaused(b);
if (getType() != PLAYER_VIDEO_STREAM_UDP) {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
p.setPause(b);
}
}
}
/**
* {@inheritDoc}
*/
public void seek(int seconds) {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
LogUtil.log(LogUtil.DEBUG, "seek length: " + p.getLength());
if (p.getLength() > 0) {
p.skip((long) (seconds * 1000));
} else {
float denom = (float) getLengthHint();
if (denom > 0.0f) {
float worth = (float) seconds / denom;
LogUtil.log(LogUtil.DEBUG, "figuring : " + worth);
p.setPosition(p.getPosition() + worth);
}
}
}
}
/**
* {@inheritDoc}
*/
public void seekPosition(int seconds) {
}
/**
* {@inheritDoc}
*/
public void seekPosition(double percentage) {
}
/**
* {@inheritDoc}
*/
public void guide() {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
p.menuActivate();
}
}
/**
* {@inheritDoc}
*/
public void up() {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
p.menuUp();
}
}
/**
* {@inheritDoc}
*/
public void down() {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
p.menuDown();
}
}
/**
* {@inheritDoc}
*/
public void left() {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
p.menuLeft();
}
}
/**
* {@inheritDoc}
*/
public void right() {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
p.menuRight();
}
}
/**
* {@inheritDoc}
*/
public void enter() {
if (getType() == PLAYER_VIDEO_DVD) {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
p.menuActivate();
}
}
}
/**
* {@inheritDoc}
*/
public void next() {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
p.nextChapter();
}
}
/**
* {@inheritDoc}
*/
public void previous() {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
p.previousChapter();
}
}
/**
* {@inheritDoc}
*/
public void audiosync(double offset) {
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
// Convert to microseconds...
offset *= 1000000;
long loffset = (long) offset;
long current = p.getAudioDelay();
p.setAudioDelay(loffset + current);
LogUtil.log(LogUtil.DEBUG, "set audiodelay to <" + (loffset + current) + ">");
}
}
/**
* {@inheritDoc}
*/
public PlayState getPlayState() {
PlayState result = new PlayState();
DirectMediaPlayer p = getDirectMediaPlayer();
if (p != null) {
if (p.getLength() > 0) {
result.setPosition((long) (p.getPosition() * 1000.0f));
result.setTime((double) (p.getTime() / 1000.0));
result.setPaused(isPaused());
result.setPlaying(isPlaying());
} else {
// Here we only have position to help us...
long hint = getLengthHint();
if (hint > 0L) {
float percent = p.getPosition();
result.setPosition((long) (percent * 1000.0f));
result.setTime((double) (percent * hint));
result.setPaused(isPaused());
result.setPlaying(isPlaying());
}
}
}
return (result);
}
class FrameRenderCallback extends RenderCallbackAdapter {
private BufferedImage bufferedImage;
public FrameRenderCallback(BufferedImage image) {
super(((DataBufferInt) image.getRaster().getDataBuffer()).getData());
bufferedImage = image;
}
@Override
public void onDisplay(DirectMediaPlayer mediaPlayer, int[] data) {
ImagePanel p = getKeyPanel();
if (p != null) {
p.repaint();
}
}
}
class ImagePanel extends JPanel {
private BufferedImage bufferedImage;
public ImagePanel() {
}
public BufferedImage getBufferedImage() {
return (bufferedImage);
}
public void setBufferedImage(BufferedImage bi) {
bufferedImage = bi;
}
@Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
if (bufferedImage != null) {
g2.drawImage(bufferedImage, null, 0, 0);
}
}
}
class MyMediaPlayerEventAdapter extends MediaPlayerEventAdapter {
public MyMediaPlayerEventAdapter() {
}
public void stopped(MediaPlayer mediaPlayer) {
LogUtil.log(LogUtil.DEBUG, "stopped");
}
public void finished(MediaPlayer mediaPlayer) {
LogUtil.log(LogUtil.DEBUG, "finished");
String[] all = getUrls();
if (urls != null) {
int index = getUrlIndex() + 1;
if (index == urls.length) {
setUrlIndex(-1);
setUrls(null);
setPlaying(false);
setCompleted(true);
stop();
} else {
setUrlIndex(index);
mediaPlayer.playMedia(urls[index]);
}
} else {
setPlaying(false);
setCompleted(true);
stop();
}
}
}
}