Added VLC based audio backend.

This commit is contained in:
neviyn 2016-04-01 20:27:17 +01:00
parent 05cebf59fb
commit 05e0e446a7
7 changed files with 296 additions and 4 deletions

View File

@ -85,6 +85,12 @@
<version>1.5</version> <version>1.5</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>uk.co.caprica</groupId>
<artifactId>vlcj</artifactId>
<version>3.10.1</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>

View File

@ -9,6 +9,7 @@ import musicplayer.library.ILibrary;
import musicplayer.library.JTreeLibrary; import musicplayer.library.JTreeLibrary;
import musicplayer.player.GStreamerPlayer; import musicplayer.player.GStreamerPlayer;
import musicplayer.player.IPlayer; import musicplayer.player.IPlayer;
import musicplayer.player.VLCPlayer;
import musicplayer.playlist.IPlaylist; import musicplayer.playlist.IPlaylist;
import musicplayer.playlist.JTablePlaylist; import musicplayer.playlist.JTablePlaylist;
import musicplayer.swingui.PlayerGUI; import musicplayer.swingui.PlayerGUI;
@ -17,7 +18,7 @@ class SwingUIModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
bind(IPlayer.class).to(GStreamerPlayer.class).in(Singleton.class); bind(IPlayer.class).to(VLCPlayer.class).in(Singleton.class);
bind(IDatabase.class).to(HibernateDatabase.class).in(Singleton.class); bind(IDatabase.class).to(HibernateDatabase.class).in(Singleton.class);
bind(IPlaylist.class).to(JTablePlaylist.class).in(Singleton.class); bind(IPlaylist.class).to(JTablePlaylist.class).in(Singleton.class);
bind(ILibrary.class).to(JTreeLibrary.class).in(Singleton.class); bind(ILibrary.class).to(JTreeLibrary.class).in(Singleton.class);

View File

@ -5,6 +5,7 @@ import musicplayer.StartPlayingException;
import musicplayer.callbacks.PlayerCallbackInterface; import musicplayer.callbacks.PlayerCallbackInterface;
import musicplayer.model.Song; import musicplayer.model.Song;
import musicplayer.playlist.IPlaylist; import musicplayer.playlist.IPlaylist;
import musicplayer.util.ConfigManager;
import org.gstreamer.ClockTime; import org.gstreamer.ClockTime;
import org.gstreamer.ElementFactory; import org.gstreamer.ElementFactory;
import org.gstreamer.Gst; import org.gstreamer.Gst;
@ -73,7 +74,7 @@ public class GStreamerPlayer implements IPlayer{
}, "seekbar"); }, "seekbar");
seekBarUpdater.start(); seekBarUpdater.start();
} }
currentVolume = getVolume(); setVolume(ConfigManager.getLastVolume());
} }
private void resetSeek(){ private void resetSeek(){

View File

@ -0,0 +1,203 @@
package musicplayer.player;
import com.google.inject.Inject;
import musicplayer.StartPlayingException;
import musicplayer.callbacks.PlayerCallbackInterface;
import musicplayer.model.Song;
import musicplayer.playlist.IPlaylist;
import musicplayer.util.ConfigManager;
import uk.co.caprica.vlcj.discovery.NativeDiscovery;
import uk.co.caprica.vlcj.player.MediaPlayer;
import uk.co.caprica.vlcj.player.MediaPlayerEventAdapter;
import uk.co.caprica.vlcj.player.MediaPlayerFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Optional;
public class VLCPlayer implements IPlayer {
private ArrayList<String> libvlcArgs = new ArrayList<>(Collections.singletonList("--vout=dummy"));
private MediaPlayer mediaPlayer;
private Song currentSong;
private final PlayerCallbackInterface callbackInterface;
private int currentVolume = 100;
private final IPlaylist playlist;
private boolean repeatMode = false; // Repeat the current song?
/**
* Manages VLC based playback operations.
* @param callbackInterface Interface on which UI updates can be called.
* @param playlist Playlist containing music to play.
*/
@Inject
public VLCPlayer(PlayerCallbackInterface callbackInterface, IPlaylist playlist){
this(callbackInterface, playlist, false);
}
/**
* Manages VLC based playback operations.
* @param callbackInterface Interface on which UI updates can be called.
* @param playlist Playlist containing music to play.
*/
VLCPlayer(PlayerCallbackInterface callbackInterface, IPlaylist playlist, boolean testMode){
new NativeDiscovery().discover();
if(testMode)
addTestModeFlags();
this.callbackInterface = callbackInterface;
this.playlist = playlist;
MediaPlayerFactory mediaPlayerFactory = new MediaPlayerFactory(libvlcArgs.toArray(new String[libvlcArgs.size()]));
mediaPlayer = mediaPlayerFactory.newHeadlessMediaPlayer();
mediaPlayer.addMediaPlayerEventListener(new InternalEventAdapter());
Thread hookShutdown = new Thread(() -> {
// Cleanly dispose of the media player instance and any associated native resources
mediaPlayer.stop();
mediaPlayer.release();
// Cleanly dispose of the media player factory and any associated native resources
mediaPlayerFactory.release();
});
Runtime.getRuntime().addShutdownHook(hookShutdown);
setVolume(ConfigManager.getLastVolume());
}
private void addTestModeFlags(){
libvlcArgs.add("--aout=dummy");
}
@Override
public void play() throws StartPlayingException {
playSong(playlist.getActive());
}
@Override
public void playSong(Optional<Song> inputSong) throws StartPlayingException {
if(inputSong.isPresent()) {
playSong(inputSong.get());
}
}
private void playSong(Song inputSong) throws StartPlayingException {
resetSeek();
if(inputSong == currentSong && mediaPlayer.getTime() > 0 && mediaPlayer.getTime() <= mediaPlayer.getLength()) { // Song ~should~ already be loaded, unpause instead?
mediaPlayer.play();
return;
}
currentSong = inputSong;
playlist.setPlayingSong(currentSong);
File songFile = currentSong.getSongFile();
if (!songFile.exists()) {
playlist.delete(currentSong);
next();
return;
}
boolean startedPlaying = mediaPlayer.startMedia(inputSong.getSongFile().getAbsolutePath());
mediaPlayer.setVolume(currentVolume);
if(!startedPlaying)
throw new StartPlayingException(inputSong);
}
private void resetSeek(){
if(callbackInterface != null)
callbackInterface.setSeekBarPosition(0);
}
@Override
public void stop() {
resetSeek();
mediaPlayer.stop();
}
@Override
public void resume() {
mediaPlayer.setPause(false);
}
@Override
public void pause() {
mediaPlayer.setPause(true);
if (isPlaying()) {
//noinspection StatementWithEmptyBody
do { // Wait to be paused
} while (isPlaying());
}
}
@Override
public void next() throws StartPlayingException {
playSong(playlist.getNext(currentSong));
}
@Override
public void previous() throws StartPlayingException {
playSong(playlist.getPrevious(currentSong));
}
@Override
public Song getCurrentSong() {
return currentSong;
}
@Override
public int currentSongPosition() {
return (int)(mediaPlayer.getTime() / 1000);
}
@Override
public void setVolume(int volume) {
currentVolume = volume;
mediaPlayer.setVolume(volume);
}
@Override
public int getVolume() {
return (mediaPlayer.getVolume() >= 0) ? mediaPlayer.getVolume() : currentVolume;
}
@Override
public void seek(int position) {
mediaPlayer.setTime(position * 1000);
}
@Override
public boolean isPlaying() {
return mediaPlayer.isPlaying();
}
@Override
public void setRepeat(boolean repeatMode) {
this.repeatMode = repeatMode;
}
@Override
public boolean isRepeating() {
return repeatMode;
}
private class InternalEventAdapter extends MediaPlayerEventAdapter{
@Override
public void finished(MediaPlayer mediaPlayer) {
try {
if (repeatMode)
playSong(currentSong);
else
playSong(playlist.getNext(currentSong));
} catch (StartPlayingException ex){
ex.printStackTrace();
}
}
@Override
public void playing(MediaPlayer mediaPlayer) {
if(callbackInterface != null)
callbackInterface.setSeekBarDuration((int) (mediaPlayer.getLength() / 1000));
}
@Override
public void timeChanged(MediaPlayer mediaPlayer, long newTime) {
if(callbackInterface != null)
callbackInterface.setSeekBarPosition((int)(newTime / 1000));
}
}
}

View File

@ -85,7 +85,10 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
* @param seconds New length of seekBar. * @param seconds New length of seekBar.
*/ */
public void setSeekBarDuration(int seconds) { public void setSeekBarDuration(int seconds) {
SwingUtilities.invokeLater(() -> seekBar.setMaximum(seconds)); SwingUtilities.invokeLater(() -> {
if (!seeking.get())
seekBar.setMaximum(seconds);
});
} }
private void showError(StartPlayingException ex) { private void showError(StartPlayingException ex) {

View File

@ -4,6 +4,7 @@ import musicplayer.library.ILibrary;
import musicplayer.model.Song; import musicplayer.model.Song;
import musicplayer.playlist.IPlaylist; import musicplayer.playlist.IPlaylist;
import musicplayer.playlist.JTablePlaylist; import musicplayer.playlist.JTablePlaylist;
import musicplayer.util.ConfigManager;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -67,7 +68,7 @@ public class GStreamerPlayerTest {
@Test @Test
public void testSetVolume() throws Exception { public void testSetVolume() throws Exception {
assertEquals(100, player.getVolume()); assertEquals(ConfigManager.getLastVolume(), player.getVolume());
int newVolume = 25; int newVolume = 25;
player.setVolume(25); player.setVolume(25);
assertEquals(newVolume, player.getVolume()); assertEquals(newVolume, player.getVolume());

View File

@ -0,0 +1,77 @@
package musicplayer.player;
import musicplayer.library.ILibrary;
import musicplayer.model.Song;
import musicplayer.playlist.IPlaylist;
import musicplayer.playlist.JTablePlaylist;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.util.Optional;
import static org.junit.Assert.*;
public class VLCPlayerTest {
VLCPlayer player;
IPlaylist playlist;
Song sampleSong = new Song(ILibrary.autoParse(
new File(VLCPlayerTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
@Before
public void setUp() throws Exception {
playlist = new JTablePlaylist();
player = new VLCPlayer(null, playlist, true);
}
@Test
public void testPlayEmptyPlaylist() throws Exception {
player.play();
assertFalse(player.isPlaying());
}
@Test
public void testPlay() throws Exception {
playlist.addSong(sampleSong);
player.play();
assertTrue(player.isPlaying());
}
@Test
public void testPlaySong() throws Exception {
player.playSong(Optional.of(sampleSong));
assertTrue(player.isPlaying());
assertEquals(sampleSong, player.getCurrentSong());
}
@Test
public void testStop() throws Exception {
player.playSong(Optional.of(sampleSong));
assertTrue(player.isPlaying());
player.stop();
assertFalse(player.isPlaying());
}
@Test
public void testResume() throws Exception {
}
@Test
public void testPause() throws Exception {
player.playSong(Optional.of(sampleSong));
assertTrue(player.isPlaying());
player.pause();
assertFalse(player.isPlaying());
}
@Test
public void testSeek() throws Exception {
player.playSong(Optional.of(sampleSong));
assertTrue(player.isPlaying());
player.seek(100);
System.out.println(player.currentSongPosition());
assertTrue(player.currentSongPosition() >= 100);
}
}