/*
* 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.player;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class implements a mechanism to play a media item and wait for it to start (or wait for it
* to raise an error instead of starting).
* <p>
* Ordinarily a call to play new media returns immediately and the native media player attempts to
* open and start playing the media asynchronously. This can make it a little difficult for
* application code to know if the media successfully started or failed to start because of an
* error.
* <p>
* It is possible for application code to respond to media player events to determine whether the
* media started successfully or failed because of an error but this class serves as a convenient
* encapsulation of that functionality.
* <p>
* The strategy is simply to block the play call until a media player "playing" or "error" event is
* received.
* <p>
* Example usage:
*
* <pre>
* mediaPlayer.prepareMedia(mrl, options);
* boolean definitelyStarted = new MediaPlayerLatch(mediaPlayer).play();
* </pre>
*
* The {@link DefaultMediaPlayer} uses this class for the "play and wait..." implementation, see
* {@link MediaPlayer#startMedia(String, String...)}.
* <p>
* Most applications are not expected to need this class and use the "start media" functionality on
* the media player instead.
*/
public class MediaPlayerLatch {
/**
* Log.
*/
private final Logger logger = LoggerFactory.getLogger(MediaPlayerLatch.class);
/**
* Media player instance.
*/
private final MediaPlayer mediaPlayer;
/**
* Create a new media player latch.
*
* @param mediaPlayer media player instance
*/
public MediaPlayerLatch(MediaPlayer mediaPlayer) {
this.mediaPlayer = mediaPlayer;
}
/**
* Play the media and wait for it to either start playing or error.
*
* @return true if the media definitely started playing and false if it did not or the thread
* was interrupted while waiting (unlikely, but in which case the media player
* <em>might</em> still start)
*/
public boolean play() {
logger.debug("play()");
// If the media player is already playing, then the latch will wait for an event that
// will never arrive and so will block incorrectly
if(!mediaPlayer.isPlaying()) {
CountDownLatch latch = new CountDownLatch(1);
LatchListener listener = new LatchListener(latch);
mediaPlayer.addMediaPlayerEventListener(listener);
mediaPlayer.play();
try {
logger.debug("Waiting for media playing or error...");
latch.await();
logger.debug("Finished waiting.");
boolean started = listener.playing.get();
logger.debug("started={}", started);
return started;
}
catch(InterruptedException e) {
logger.debug("Interrupted while waiting for media player", e);
return false;
}
finally {
mediaPlayer.removeMediaPlayerEventListener(listener);
}
}
else {
return true;
}
}
/**
* Short-lived listener to wait for playing/error events.
*/
private static final class LatchListener extends MediaPlayerEventAdapter {
/**
* Synchronisation latch.
*/
private final CountDownLatch latch;
/**
* True if the media started, otherwise false.
*/
private final AtomicBoolean playing = new AtomicBoolean();
/**
* Create a new listener.
*
* @param latch synchronisation latch.
*/
private LatchListener(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void playing(MediaPlayer mediaPlayer) {
playing.set(true);
latch.countDown();
}
@Override
public void error(MediaPlayer mediaPlayer) {
playing.set(false);
latch.countDown();
}
}
}