Added VLC based audio backend.
This commit is contained in:
parent
05cebf59fb
commit
05e0e446a7
6
pom.xml
6
pom.xml
@ -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>
|
||||||
|
@ -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);
|
||||||
|
@ -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(){
|
||||||
|
203
src/main/java/musicplayer/player/VLCPlayer.java
Normal file
203
src/main/java/musicplayer/player/VLCPlayer.java
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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) {
|
||||||
|
@ -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());
|
||||||
|
77
src/test/java/musicplayer/player/VLCPlayerTest.java
Normal file
77
src/test/java/musicplayer/player/VLCPlayerTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user