/** * Copyright 2009 Marc Stogaitis and Mimi Sun * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmote.server.media.vlc; import java.io.File; import java.util.logging.Level; import java.util.logging.Logger; import org.blinkenlights.jid3.MP3File; import org.gmote.common.media.MediaMetaInfo; import org.gmote.server.media.MediaCommandHandler; import org.gmote.server.media.MediaInfoUpdater; import org.gmote.server.media.PlayerUtil; import org.videolan.jvlc.Audio; import org.videolan.jvlc.JVLC; import org.videolan.jvlc.MediaDescriptor; import org.videolan.jvlc.MediaPlayer; import org.videolan.jvlc.Playlist; import org.videolan.jvlc.VLCException; import org.videolan.jvlc.internal.LibVlc.libvlc_meta_t; /** * Handles commands when playing a playlist. We are using the deprecated * Playlist class since there is a bug in the current jvlc's implementation of * MediaList. See JVLC Playlist issue: * http://forum.videolan.org/viewtopic.php?f=2&t=49612&start=0&st=0&sk=t&sd=a * * @author Marc * */ // See class description for explanation of suppression of deprecation warning @SuppressWarnings("deprecation") public class VlcPlaylistCommandHandler extends MediaCommandHandler { private static final int MAX_ITERATION = 8; private static Logger LOGGER = Logger.getLogger(VlcPlaylistCommandHandler.class.getName()); private static Playlist playlist; // Time to fast forward/rewind by (in milliseconds). private static final float POSITION_INCREMENT_TIME = 12 * 1000; private static VlcPlaylistCommandHandler instance = null; private static JVLC jvlc; private static int idOfFirstSong = -1; private String mrlOfLastMediaUpdate = ""; // Private constructor to prevent instantiation private VlcPlaylistCommandHandler() { } public static VlcPlaylistCommandHandler instance(Playlist mediaPlaylist, int idOfOriginal, JVLC jvlcInstance) { playlist = mediaPlaylist; idOfFirstSong = idOfOriginal; jvlc = jvlcInstance; if (instance == null) { instance = new VlcPlaylistCommandHandler(); } return instance; } @Override public void closeMedia() { stopMedia(); } @Override public void pauseMedia() { try { playlist.togglePause(); } catch (VLCException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } @Override public void playMedia() { mrlOfLastMediaUpdate = ""; try { if (idOfFirstSong == -1) { playlist.play(); } else { playlist.play(idOfFirstSong, new String[] {}); idOfFirstSong = -1; } } catch (VLCException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } @Override public void stopMedia() { try { // Note: We DON'T do a playlist.stop() here since other operations (such // as rewind) will crash the jvm. if (!mediaIsPaused) { playlist.getMediaInstance().pause(); } playlist.getMediaInstance().setPosition(0); for (int i = 0; playlist.isRunning() && i < 5; i++) { sleep(100); } } catch (VLCException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } public static Playlist getPlaylist(JVLC jvlc) { if (playlist == null) { playlist = new Playlist(jvlc); } return playlist; } @Override public void fastForward() { MediaPlayer player = playlist.getMediaInstance(); float newPosition = player.getPosition() + (POSITION_INCREMENT_TIME / player.getLength()); player.setPosition(newPosition); } @Override public void rewind() { MediaPlayer player = playlist.getMediaInstance(); float newPosition = player.getPosition() - (POSITION_INCREMENT_TIME / player.getLength()); if (newPosition < 0) { newPosition = 0; } player.setPosition(newPosition); } @Override public void fastForwardLong() { try { int currentIndex = playlist.getCurrentIndex(); playlist.next(); waitForNextSong(currentIndex); MediaInfoUpdater.instance().sendMediaUpdate(getNewMediaInfo()); } catch (VLCException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } private void waitForNextSong(int currentIndex) throws VLCException { if (playlist.itemsCount() > 1) { for (int i = 0; (playlist.getCurrentIndex() == currentIndex) && i < MAX_ITERATION; i++) { sleep(100); } } } @Override public void rewindLong() { try { int currentIndex = playlist.getCurrentIndex(); playlist.prev(); waitForNextSong(currentIndex); MediaInfoUpdater.instance().sendMediaUpdate(getNewMediaInfo()); } catch (VLCException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } @Override protected void setVolume(int volume) { Audio audio = new Audio(jvlc); audio.setVolume(volume); } @Override protected int getVolume() { Audio audio = new Audio(jvlc); return audio.getVolume(); } @Override protected void toggleMute() { Audio audio = new Audio(jvlc); audio.toggleMute(); } private void sleep(long time) { try { Thread.sleep(time); } catch (InterruptedException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } @Override public MediaMetaInfo getNewMediaInfo() { try { if (!playlist.isRunning()) { return null; } } catch (VLCException e) { LOGGER.log(Level.SEVERE, e.getMessage(),e); return null; } MediaDescriptor media = playlist.getMediaInstance().getMedia(); if (media == null || media.getMrl() == null) { return null; } String mediaMrl = media.getMrl(); if (mediaMrl.equals(mrlOfLastMediaUpdate)) { return null; } String artworkUrl = media.getMeta(libvlc_meta_t.libvlc_meta_ArtworkURL); MP3File mp3 = new MP3File(new File(mediaMrl)); MediaMetaInfo fileInfo = PlayerUtil.getSongMetaInfo(mp3); useVlcMetaInfoIfNull(fileInfo, media); byte[] imageData = PlayerUtil.extractEmbeddedImageData(mp3); if (imageData == null && artworkUrl != null && artworkUrl.startsWith("file://")) { // Try to get the image from file. imageData = PlayerUtil.extractImageArtworkFromFile(artworkUrl); } if (imageData == null) { // In windows, a folder.jpg file often contains the album art imageData = PlayerUtil.extractImageFromFolder(mediaMrl); } fileInfo.setImage(imageData); if (imageData == null) { LOGGER.info("Image data is null"); } mrlOfLastMediaUpdate = mediaMrl; return fileInfo; } private void useVlcMetaInfoIfNull(MediaMetaInfo fileInfo, MediaDescriptor media) { // Try to use the vlc data if we wern't able to get the data any other way. // We use vlc's data as a backup only since it takes a little while for vlc // to update this data, resulting in the user first being presented with // partial data (for example, no album art, no album name, and a song name // that = file name.mp3), and then receives the correct info a few seconds // later. if (fileInfo.getTitle() == null) { fileInfo.setTitle(media.getMeta(libvlc_meta_t.libvlc_meta_Title)); } if (fileInfo.getArtist() == null) { fileInfo.setTitle(media.getMeta(libvlc_meta_t.libvlc_meta_Artist)); } if (fileInfo.getAlbum() == null) { fileInfo.setTitle(media.getMeta(libvlc_meta_t.libvlc_meta_Album)); } } }