package ejmf.toolkit.media;
import javax.media.Clock;
import javax.media.ClockStoppedException;
import javax.media.Controller;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.DeallocateEvent;
import javax.media.MediaTimeSetEvent;
import javax.media.RateChangeEvent;
import javax.media.StartEvent;
import javax.media.StopEvent;
import javax.media.StopTimeChangeEvent;
import javax.media.Time;
/**
* This class provides a thread to stop an AbstractController when
* its stop time is reached.
*
* @see AbstractController
*
* @author Steve Talley & Rob Gordon
*/
public class StopTimeMonitor extends Thread implements ControllerListener {
private boolean wokenUp;
private AbstractController controller;
/**
* Constructs a StopTimeMonitor for the given
* AbstractController.
*
* @param controller
* The AbstractController to whose stop time to
* monitor.
*/
public StopTimeMonitor(AbstractController controller) {
super();
this.controller = controller;
controller.addControllerListener(this);
setDaemon(true);
start();
}
/**
* Listen for RateChangeEvents or MediaTimeSetEvents and
* notify the StopTimeMonitor thread to recalculate its wait
* time. Also listen for StartEvents and StopEvents so that
* the monitor will know whether the controller is playing.
*
* @param e
* The ControllerEvent
*/
public synchronized void controllerUpdate(ControllerEvent e) {
if( e instanceof StopTimeChangeEvent ||
e instanceof RateChangeEvent ||
e instanceof MediaTimeSetEvent ||
e instanceof StartEvent ||
(e instanceof StopEvent && ! (e instanceof DeallocateEvent) ) )
{
wokenUp = true;
notifyAll();
}
}
/**
* Continuously monitor the controller, it's state, and it's
* stop time. Wait until a. the controller is started, and
* b. a stop time has been set on the AbstractController.
* Then calculate the how long to wait before stopping the
* controller, based on the current media time, rate, and
* stop time. If we are woken up by an event, then
* recalculate and begin again. Otherwise, stop the
* controller when we wake up.
*/
private synchronized void monitorStopTime() {
Time stopTime;
long waittime;
while(true) {
// Wait until the controller is started and the
// stop time has been set
while( controller.getState() != Controller.Started ||
(stopTime = controller.getStopTime()) == Clock.RESET )
{
try {
wait();
} catch(InterruptedException e) {}
}
wokenUp = false;
// Get the time until the AbstractController should
// be stopped. If the Clock has stopped since we
// last checked, go back to sleep
try {
waittime = getWaitTime(stopTime);
} catch(ClockStoppedException e) {
continue;
}
// Now wait until it's time to stop the controller
if( waittime > 0 ) {
try {
wait( waittime );
} catch(InterruptedException e) {}
}
// Did we wake up naturally?
if(! wokenUp) {
// Reset the stop time
controller.stopAtTime();
controller.setStopTime(Clock.RESET);
}
}
}
/**
* Calculates the time that the monitor should sleep (in
* milliseconds) before stopping the controller.
*/
private long getWaitTime(Time stopTime)
throws ClockStoppedException
{
long stop =
controller.mapToTimeBase(stopTime).getNanoseconds();
long now =
controller.getTimeBase().getNanoseconds();
return (stop - now) / 1000000;
}
/**
* Continuously monitor the controller, it's state, and it's stop
* time.
*/
public void run() {
monitorStopTime();
}
}