/* * 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-2016 Caprica Software Limited. */ package uk.co.caprica.vlcj.test.basic; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Canvas; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.AWTEventListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.SwingUtilities; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.co.caprica.vlcj.binding.LibVlc; import uk.co.caprica.vlcj.binding.LibVlcFactory; import uk.co.caprica.vlcj.binding.internal.libvlc_media_t; import uk.co.caprica.vlcj.player.AudioOutput; import uk.co.caprica.vlcj.player.Equalizer; import uk.co.caprica.vlcj.player.MediaDetails; import uk.co.caprica.vlcj.player.MediaMeta; import uk.co.caprica.vlcj.player.MediaPlayer; import uk.co.caprica.vlcj.player.MediaPlayerEventAdapter; import uk.co.caprica.vlcj.player.MediaPlayerFactory; import uk.co.caprica.vlcj.player.embedded.DefaultFullScreenStrategy; import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer; import uk.co.caprica.vlcj.player.embedded.FullScreenStrategy; import uk.co.caprica.vlcj.test.VlcjTest; import com.sun.awt.AWTUtilities; import com.sun.jna.platform.WindowUtils; /** * Simple test harness creates an AWT Window and plays a video. * <p> * This is <strong>very</strong> basic but should give you an idea of how to build a media player. * <p> * In case you didn't realise, you can press F12 to toggle the visibility of the player controls. * <p> * Java7 provides -Dsun.java2d.xrender=True or -Dsun.java2d.xrender=true, might give some general * performance improvements in graphics rendering. */ public class TestPlayer extends VlcjTest { /** * Log. */ private static final Logger logger = LoggerFactory.getLogger(TestPlayer.class); private final JFrame mainFrame; private final Canvas videoSurface; private final JPanel controlsPanel; private final JPanel videoAdjustPanel; private final JFrame equalizerFrame; private MediaPlayerFactory mediaPlayerFactory; private EmbeddedMediaPlayer mediaPlayer; private Equalizer equalizer; public static void main(final String[] args) throws Exception { LibVlc libVlc = LibVlcFactory.factory().create(); logger.info(" version: {}", libVlc.libvlc_get_version()); logger.info(" compiler: {}", libVlc.libvlc_get_compiler()); logger.info("changeset: {}", libVlc.libvlc_get_changeset()); setLookAndFeel(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new TestPlayer(args); } }); } public TestPlayer(String[] args) { videoSurface = new Canvas(); videoSurface.setBackground(Color.black); videoSurface.setSize(800, 600); // Only for initial layout // Since we're mixing lightweight Swing components and heavyweight AWT // components this is probably a good idea JPopupMenu.setDefaultLightWeightPopupEnabled(false); TestPlayerMouseListener mouseListener = new TestPlayerMouseListener(); videoSurface.addMouseListener(mouseListener); videoSurface.addMouseMotionListener(mouseListener); videoSurface.addMouseWheelListener(mouseListener); videoSurface.addKeyListener(new TestPlayerKeyListener()); List<String> vlcArgs = new ArrayList<String>(); vlcArgs.add("--no-snapshot-preview"); vlcArgs.add("--quiet"); vlcArgs.add("--quiet-synchro"); vlcArgs.add("--intf"); vlcArgs.add("dummy"); // Special case to help out users on Windows (supposedly this is not actually needed)... // if(RuntimeUtil.isWindows()) { // vlcArgs.add("--plugin-path=" + WindowsRuntimeUtil.getVlcInstallDir() + "\\plugins"); // } // else { // vlcArgs.add("--plugin-path=/home/linux/vlc/lib"); // } // vlcArgs.add("--plugin-path=" + System.getProperty("user.home") + "/.vlcj"); logger.debug("vlcArgs={}", vlcArgs); mainFrame = new JFrame("VLCJ Test Player"); mainFrame.setIconImage(new ImageIcon(getClass().getResource("/icons/vlcj-logo.png")).getImage()); FullScreenStrategy fullScreenStrategy = new DefaultFullScreenStrategy(mainFrame); mediaPlayerFactory = new MediaPlayerFactory(vlcArgs.toArray(new String[vlcArgs.size()])); mediaPlayerFactory.setUserAgent("vlcj test player"); List<AudioOutput> audioOutputs = mediaPlayerFactory.getAudioOutputs(); logger.debug("audioOutputs={}", audioOutputs); mediaPlayer = mediaPlayerFactory.newEmbeddedMediaPlayer(fullScreenStrategy); mediaPlayer.setVideoSurface(mediaPlayerFactory.newVideoSurface(videoSurface)); mediaPlayer.setPlaySubItems(true); mediaPlayer.setEnableKeyInputHandling(false); mediaPlayer.setEnableMouseInputHandling(false); controlsPanel = new PlayerControlsPanel(mediaPlayer); videoAdjustPanel = new PlayerVideoAdjustPanel(mediaPlayer); mainFrame.setLayout(new BorderLayout()); mainFrame.setBackground(Color.black); mainFrame.add(videoSurface, BorderLayout.CENTER); mainFrame.add(controlsPanel, BorderLayout.SOUTH); mainFrame.add(videoAdjustPanel, BorderLayout.EAST); mainFrame.setJMenuBar(buildMenuBar()); mainFrame.pack(); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mainFrame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent evt) { logger.debug("windowClosing(evt={})", evt); if(mediaPlayer != null) { mediaPlayer.release(); mediaPlayer = null; } if(mediaPlayerFactory != null) { mediaPlayerFactory.release(); mediaPlayerFactory = null; } } }); if(mediaPlayerFactory.isEqualizerAvailable()) { equalizer = mediaPlayerFactory.newEqualizer(); equalizerFrame = new EqualizerFrame(mediaPlayerFactory.getEqualizerBandFrequencies(), mediaPlayerFactory.getEqualizerPresetNames(), mediaPlayerFactory, mediaPlayer, equalizer); } else { equalizerFrame = null; } // Global AWT key handler, you're better off using Swing's InputMap and // ActionMap with a JFrame - that would solve all sorts of focus issues too Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { @Override public void eventDispatched(AWTEvent event) { if(event instanceof KeyEvent) { KeyEvent keyEvent = (KeyEvent)event; if(keyEvent.getID() == KeyEvent.KEY_PRESSED) { if(keyEvent.getKeyCode() == KeyEvent.VK_F12) { controlsPanel.setVisible(!controlsPanel.isVisible()); videoAdjustPanel.setVisible(!videoAdjustPanel.isVisible()); mainFrame.getJMenuBar().setVisible(!mainFrame.getJMenuBar().isVisible()); mainFrame.invalidate(); mainFrame.validate(); } else if(keyEvent.getKeyCode() == KeyEvent.VK_A) { mediaPlayer.setAudioDelay(mediaPlayer.getAudioDelay() - 50000); } else if(keyEvent.getKeyCode() == KeyEvent.VK_S) { mediaPlayer.setAudioDelay(mediaPlayer.getAudioDelay() + 50000); } // else if(keyEvent.getKeyCode() == KeyEvent.VK_N) { // mediaPlayer.nextFrame(); // } else if(keyEvent.getKeyCode() == KeyEvent.VK_1) { mediaPlayer.setTime(60000 * 1); } else if(keyEvent.getKeyCode() == KeyEvent.VK_2) { mediaPlayer.setTime(60000 * 2); } else if(keyEvent.getKeyCode() == KeyEvent.VK_3) { mediaPlayer.setTime(60000 * 3); } else if(keyEvent.getKeyCode() == KeyEvent.VK_4) { mediaPlayer.setTime(60000 * 4); } else if(keyEvent.getKeyCode() == KeyEvent.VK_5) { mediaPlayer.setTime(60000 * 5); } else if(keyEvent.getKeyCode() == KeyEvent.VK_6) { mediaPlayer.setTime(60000 * 6); } else if(keyEvent.getKeyCode() == KeyEvent.VK_7) { mediaPlayer.setTime(60000 * 7); } else if(keyEvent.getKeyCode() == KeyEvent.VK_8) { mediaPlayer.setTime(60000 * 8); } else if(keyEvent.getKeyCode() == KeyEvent.VK_9) { mediaPlayer.setTime(60000 * 9); } } } } }, AWTEvent.KEY_EVENT_MASK); mainFrame.setVisible(true); if(mediaPlayerFactory.isEqualizerAvailable()) { equalizerFrame.pack(); equalizerFrame.setVisible(true); } mediaPlayer.addMediaPlayerEventListener(new TestPlayerMediaPlayerEventListener()); // Won't work with OpenJDK or JDK1.7, requires a Sun/Oracle JVM (currently) boolean transparentWindowsSupport = true; try { Class.forName("com.sun.awt.AWTUtilities"); } catch(Exception e) { transparentWindowsSupport = false; } logger.debug("transparentWindowsSupport={}", transparentWindowsSupport); if(transparentWindowsSupport) { final Window test = new Window(null, WindowUtils.getAlphaCompatibleGraphicsConfiguration()) { private static final long serialVersionUID = 1L; @Override public void paint(Graphics g) { Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); g.setColor(Color.white); g.fillRoundRect(100, 150, 100, 100, 32, 32); g.setFont(new Font("Sans", Font.BOLD, 32)); g.drawString("Heavyweight overlay test", 100, 300); } }; AWTUtilities.setWindowOpaque(test, false); // Doesn't work in full-screen exclusive // mode, you would have to use 'simulated' // full-screen - requires Sun/Oracle JDK test.setBackground(new Color(0, 0, 0, 0)); // This is what you do in JDK7 // mediaPlayer.setOverlay(test); // mediaPlayer.enableOverlay(true); } // This might be useful // enableMousePointer(false); } private JMenuBar buildMenuBar() { // Menus are just added as an example of overlapping the video - they are // non-functional in this demo player JMenuBar menuBar = new JMenuBar(); JMenu mediaMenu = new JMenu("Media"); mediaMenu.setMnemonic('m'); JMenuItem mediaPlayFileMenuItem = new JMenuItem("Play File..."); mediaPlayFileMenuItem.setMnemonic('f'); mediaMenu.add(mediaPlayFileMenuItem); JMenuItem mediaPlayStreamMenuItem = new JMenuItem("Play Stream..."); mediaPlayFileMenuItem.setMnemonic('s'); mediaMenu.add(mediaPlayStreamMenuItem); mediaMenu.add(new JSeparator()); JMenuItem mediaExitMenuItem = new JMenuItem("Exit"); mediaExitMenuItem.setMnemonic('x'); mediaMenu.add(mediaExitMenuItem); menuBar.add(mediaMenu); JMenu playbackMenu = new JMenu("Playback"); playbackMenu.setMnemonic('p'); JMenu playbackChapterMenu = new JMenu("Chapter"); playbackChapterMenu.setMnemonic('c'); for(int i = 1; i <= 25; i ++ ) { JMenuItem chapterMenuItem = new JMenuItem("Chapter " + i); playbackChapterMenu.add(chapterMenuItem); } playbackMenu.add(playbackChapterMenu); JMenu subtitlesMenu = new JMenu("Subtitles"); playbackChapterMenu.setMnemonic('s'); String[] subs = {"01 English (en)", "02 English Commentary (en)", "03 French (fr)", "04 Spanish (es)", "05 German (de)", "06 Italian (it)"}; for(int i = 0; i < subs.length; i ++ ) { JMenuItem subtitlesMenuItem = new JMenuItem(subs[i]); subtitlesMenu.add(subtitlesMenuItem); } playbackMenu.add(subtitlesMenu); menuBar.add(playbackMenu); JMenu toolsMenu = new JMenu("Tools"); toolsMenu.setMnemonic('t'); JMenuItem toolsPreferencesMenuItem = new JMenuItem("Preferences..."); toolsPreferencesMenuItem.setMnemonic('p'); toolsMenu.add(toolsPreferencesMenuItem); menuBar.add(toolsMenu); JMenu helpMenu = new JMenu("Help"); helpMenu.setMnemonic('h'); JMenuItem helpAboutMenuItem = new JMenuItem("About..."); helpAboutMenuItem.setMnemonic('a'); helpMenu.add(helpAboutMenuItem); menuBar.add(helpMenu); return menuBar; } private final class TestPlayerMediaPlayerEventListener extends MediaPlayerEventAdapter { @Override public void mediaChanged(MediaPlayer mediaPlayer, libvlc_media_t media, String mrl) { logger.debug("mediaChanged(mediaPlayer={},media={},mrl={})", mediaPlayer, media, mrl); } @Override public void finished(MediaPlayer mediaPlayer) { logger.debug("finished(mediaPlayer={})", mediaPlayer); } @Override public void paused(MediaPlayer mediaPlayer) { logger.debug("paused(mediaPlayer={})", mediaPlayer); } @Override public void playing(MediaPlayer mediaPlayer) { logger.debug("playing(mediaPlayer={})", mediaPlayer); MediaDetails mediaDetails = mediaPlayer.getMediaDetails(); logger.info("mediaDetails={}", mediaDetails); } @Override public void stopped(MediaPlayer mediaPlayer) { logger.debug("stopped(mediaPlayer={})", mediaPlayer); } @Override public void videoOutput(MediaPlayer mediaPlayer, int newCount) { logger.debug("videoOutput(mediaPlayer={},newCount={})", mediaPlayer, newCount); if(newCount == 0) { return; } MediaDetails mediaDetails = mediaPlayer.getMediaDetails(); logger.info("mediaDetails={}", mediaDetails); MediaMeta mediaMeta = mediaPlayer.getMediaMeta(); logger.info("mediaMeta={}", mediaMeta); final Dimension dimension = mediaPlayer.getVideoDimension(); logger.debug("dimension={}", dimension); if(dimension != null) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { videoSurface.setSize(dimension); mainFrame.pack(); } }); } // You can set a logo like this if you like... File logoFile = new File("./etc/vlcj-logo.png"); if(logoFile.exists()) { mediaPlayer.setLogoFile(logoFile.getAbsolutePath()); mediaPlayer.setLogoOpacity(0.5f); mediaPlayer.setLogoLocation(10, 10); mediaPlayer.enableLogo(true); } // Demo the marquee mediaPlayer.setMarqueeText("vlcj java bindings for vlc"); mediaPlayer.setMarqueeSize(40); mediaPlayer.setMarqueeOpacity(95); mediaPlayer.setMarqueeColour(Color.white); mediaPlayer.setMarqueeTimeout(5000); mediaPlayer.setMarqueeLocation(50, 120); mediaPlayer.enableMarquee(true); // Not quite sure how crop geometry is supposed to work... // // Assertions in libvlc code: // // top + height must be less than visible height // left + width must be less than visible width // // With DVD source material: // // Reported size is 1024x576 - this is what libvlc reports when you call // get video size // // mpeg size is 720x576 - this is what is reported in the native log // // The crop geometry relates to the mpeg size, not the size reported // through the API // // For 720x576, attempting to set geometry to anything bigger than // 719x575 results in the assertion failures above (seems like it should // allow 720x576) to me // mediaPlayer.setCropGeometry("4:3"); } @Override public void error(MediaPlayer mediaPlayer) { logger.debug("error(mediaPlayer={})", mediaPlayer); } @Override public void mediaSubItemAdded(MediaPlayer mediaPlayer, libvlc_media_t subItem) { logger.debug("mediaSubItemAdded(mediaPlayer={},subItem={})", mediaPlayer, subItem); } @Override public void mediaDurationChanged(MediaPlayer mediaPlayer, long newDuration) { logger.debug("mediaDurationChanged(mediaPlayer={},newDuration={})", mediaPlayer, newDuration); } @Override public void mediaParsedChanged(MediaPlayer mediaPlayer, int newStatus) { logger.debug("mediaParsedChanged(mediaPlayer={},newStatus={})", mediaPlayer, newStatus); } @Override public void mediaFreed(MediaPlayer mediaPlayer) { logger.debug("mediaFreed(mediaPlayer={})", mediaPlayer); } @Override public void mediaStateChanged(MediaPlayer mediaPlayer, int newState) { logger.debug("mediaStateChanged(mediaPlayer={},newState={})", mediaPlayer, newState); } @Override public void mediaMetaChanged(MediaPlayer mediaPlayer, int metaType) { logger.debug("mediaMetaChanged(mediaPlayer={},metaType={})", mediaPlayer, metaType); } } /** * * * @param enable */ @SuppressWarnings("unused") private void enableMousePointer(boolean enable) { logger.debug("enableMousePointer(enable={})", enable); if(enable) { videoSurface.setCursor(null); } else { Image blankImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); videoSurface.setCursor(Toolkit.getDefaultToolkit().createCustomCursor(blankImage, new Point(0, 0), "")); } } /** * */ private final class TestPlayerMouseListener extends MouseAdapter { @Override public void mouseMoved(MouseEvent e) { logger.trace("mouseMoved(e={})", e); } @Override public void mousePressed(MouseEvent e) { logger.debug("mousePressed(e={})", e); } @Override public void mouseReleased(MouseEvent e) { logger.debug("mouseReleased(e={})", e); } @Override public void mouseClicked(MouseEvent e) { logger.debug("mouseClicked(e={})", e); } @Override public void mouseWheelMoved(MouseWheelEvent e) { logger.debug("mouseWheelMoved(e={})", e); } @Override public void mouseEntered(MouseEvent e) { logger.debug("mouseEntered(e={})", e); } @Override public void mouseExited(MouseEvent e) { logger.debug("mouseExited(e={})", e); } } /** * */ private final class TestPlayerKeyListener extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { logger.debug("keyPressed(e={})", e); } @Override public void keyReleased(KeyEvent e) { logger.debug("keyReleased(e={})", e); } @Override public void keyTyped(KeyEvent e) { logger.debug("keyTyped(e={})", e); } } }