Example E-1: TimeLineController.java (1 of 11)
import javax.media.*;
import com.sun.media.MediaClock;
// This Controller uses two custom classes:
// The base class is EventPostingBase. It has three methods:
// public void addControllerListener (ControllerListener
// observer);
// public void removeControllerListener (ControllerListener
// observer);
// protected void postEvent (ControllerEvent event);
//
// This Controller posts TimeLineEvents. TimeLineEvent has
// two methods:
// public TimeLineEvent (Controller who, int
// segmentEntered);
// public final int getSegment ();
public class TimeLineController extends EventPostingBase
implements Controller, Runnable
{
Clock ourClock;
// This simple controller really only has two states:
// Prefetched and Started.
int ourState;
long timeLine[];
int currentSegment = -1;
long duration;
Thread myThread;
// Create a TimeLineController giving it a sorted time line.
// The TimeLineController will post events indicating when
// it has passed to different parts of the time line.
public TimeLineController (long timeLine[])
{
this.timeLine = timeLine;
ourClock = new MediaClock ();
|
duration = timeLine[timeLine.length-1];
myThread = null;
// We always start off ready to go!
ourState = Controller.Prefetched;
}
// Binary search for which segment we are now in. Segment
// 0 is considered to start at 0 and end at timeLine[0].
// Segment timeLine.length is considered to start at
// timeLine[timeLine.length-1] and end at infinity. At the
// points of 0 and timeLine[timeLine.length-1] the
// Controller will stop (and post an EndOfMedia event).
int computeSegment (long time)
{
int max = timeLine.length;
int min = 0;
for (;;)
{
if (min == max) return min;
int current = min + ((max - min) >> 1);
if (time < timeLine[current])
{
max = current;
}
else
{
min = current + 1;
}
}
}
// These are all simple...
public float setRate (float factor)
{
// We don't support a rate of 0.0. Not worth the extra math
// to handle something the user should do with the stop()
// method!
if (factor == 0.0f)
{
factor = 1.0f;
}
float newRate = ourClock.setRate (factor);
|
postEvent (new RateChangeEvent (this, newRate));
return newRate;
}
public void setTimeBase (TimeBase master)
throws IncompatibleTimeBaseException
{
ourClock.setTimeBase (master);
}
public Time getStopTime ()
{
return ourClock.getStopTime ();
}
public Time getSyncTime ()
{
return ourClock.getSyncTime ();
}
public Time mapToTimeBase (Time t) throws ClockStoppedException
{
return ourClock.mapToTimeBase (t);
}
public Time getMediaTime ()
{
return ourClock.getMediaTime ();
}
public TimeBase getTimeBase ()
{
return ourClock.getTimeBase ();
}
public float getRate ()
{
return ourClock.getRate ();
}
// From Controller
public int getState ()
{
return ourState;
}
|
public int getTargetState ()
{
return ourState;
}
public void realize ()
{
postEvent (new RealizeCompleteEvent (this, ourState,
ourState, ourState));
}
public void prefetch ()
{
postEvent (new PrefetchCompleteEvent (this, ourState,
ourState, ourState));
}
public void deallocate ()
{
postEvent (new DeallocateEvent (this, ourState,
ourState, ourState, ourClock.getMediaTime ()));
}
public Time getStartLatency ()
{
// We can start immediately, of course!
return new Time(0);
}
public Control[] getControls ()
{
return new Control[0];
}
public Time getDuration ()
{
return new Time(duration);
}
// This one takes a little work as we need to compute if we
// changed segments.
public void setMediaTime (Time now)
{
ourClock.setMediaTime (now);
postEvent (new MediaTimeSetEvent (this, now));
|
checkSegmentChange (now.getNanoseconds());
}
// We now need to spin a thread to compute/observe the
// passage of time.
public synchronized void syncStart (Time tbTime)
{
long startTime = ourClock.getMediaTime().getNanoseconds();
// We may actually have to stop immediately with an
// EndOfMediaEvent. We compute that now. If we are already
// past end of media, then we
// first post the StartEvent then we post a EndOfMediaEvent
boolean endOfMedia;
float rate = ourClock.getRate ();
if ((startTime > duration && rate >= 0.0f) ||
(startTime < 0 && rate <= 0.0f))
{
endOfMedia = true;
}
else
{
endOfMedia = false;
}
// We face the same possible problem with being past the stop
// time. If so, we stop immediately.
boolean pastStopTime;
long stopTime = ourClock.getStopTime().getNanoseconds();
if ((stopTime != Long.MAX_VALUE) &&
((startTime >= stopTime && rate >= 0.0f) ||
(startTime <= stopTime && rate <= 0.0f)))
{
pastStopTime = true;
}
else
{
pastStopTime = false;
}
if (!endOfMedia && !pastStopTime)
{
|
ourClock.syncStart (tbTime);
ourState = Controller.Started;
}
postEvent (new StartEvent (this, Controller.Prefetched,
Controller.Started, Controller.Started,
new Time(startTime), tbTime));
if (endOfMedia)
{
postEvent (new EndOfMediaEvent (this,
Controller.Started,
Controller.Prefetched, Controller.Prefetched,
new Time(startTime)));
}
else if (pastStopTime)
{
postEvent (new StopAtTimeEvent (this, Controller.Started,
Controller.Prefetched, Controller.Prefetched,
new Time(startTime)));
}
else
{
myThread = new Thread (this, "TimeLineController");
// Set thread to appopriate priority...
myThread.start ();
}
}
// Nothing really special here except that we need to notify
// the thread that we may have.
public synchronized void setStopTime (Time stopTime)
{
ourClock.setStopTime (stopTime);
postEvent (new StopTimeChangeEvent (this, stopTime));
notifyAll ();
}
// This one is also pretty easy. We stop and tell the running
// thread to exit.
|
public synchronized void stop ()
{
int previousState = ourState;
ourClock.stop ();
ourState = Controller.Prefetched;
postEvent (new StopByRequestEvent (this, previousState,
Controller.Prefetched, Controller.Prefetched,
ourClock.getMediaTime ()));
notifyAll ();
// Wait for thread to shut down.
while (myThread != null)
{
try
{
wait ();
}
catch (InterruptedException e)
{
// NOT REACHED
}
}
}
protected void checkSegmentChange (long timeNow)
{
int segment = computeSegment (timeNow);
if (segment != currentSegment)
{
currentSegment = segment;
postEvent (new TimeLineEvent (this, currentSegment));
}
}
// Most of the real work goes here. We have to decide when
// to post events like EndOfMediaEvent and StopAtTimeEvent
// and TimeLineEvent.
public synchronized void run ()
{
long timeToNextSegment = 0;
long mediaTimeToWait = 0;
float ourRate = 1.0f;
|
for (;;)
{
// First, have we changed segments? If so, post an event!
long timeNow = ourClock.getMediaTime ().getNanoseconds ();
checkSegmentChange (timeNow);
// Second, have we already been stopped? If so, stop
// the thread.
if (ourState == Controller.Prefetched)
{
myThread = null;
// If someone is waiting for the thread to die, let them
// know.
notifyAll ();
break;
}
// Current rate. Our setRate() method prevents the value
// 0 so we don't check for that here.
ourRate = ourClock.getRate ();
// How long in clock time do we need to wait before doing
// something?
long endOfMediaTime;
// Next, are we past end of media?
if (ourRate > 0.0f)
{
mediaTimeToWait = duration - timeNow;
endOfMediaTime = duration;
}
else
{
mediaTimeToWait = timeNow;
endOfMediaTime = 0;
}
// If we are at (or past) time to stop due to EndOfMedia,
// we do that now!
|
if (mediaTimeToWait <= 0)
{
ourClock.stop ();
ourClock.setMediaTime (new Time(endOfMediaTime));
ourState = Controller.Prefetched;
postEvent (new EndOfMediaEvent (this, Controller.Started,
Controller.Prefetched, Controller.Prefetched,
new Time(endOfMediaTime)));
continue;
}
// How long until we hit our stop time?
long stopTime = ourClock.getStopTime ().getNanoseconds();
if (stopTime != Long.MAX_VALUE)
{
long timeToStop;
if (ourRate > 0.0f)
{
timeToStop = stopTime - timeNow;
}
else
{
timeToStop = timeNow - stopTime;
}
// If we are at (or past) time to stop due to the stop
// time, we stop now!
if (timeToStop <= 0)
{
ourClock.stop ();
ourClock.setMediaTime (new Time(stopTime));
ourState = Controller.Prefetched;
postEvent (new StopAtTimeEvent (this,
Controller.Started,
Controller.Prefetched, Controller.Prefetched,
new Time(stopTime)));
continue;
}
else if (timeToStop < mediaTimeToWait)
{
mediaTimeToWait = timeToStop;
}
}
|
// How long until we pass into the next time line segment?
if (ourRate > 0.0f)
{
timeToNextSegment = timeLine[currentSegment] - timeNow;
}
else if (currentSegment == 0)
{
timeToNextSegment = timeNow;
}
else
{
timeToNextSegment = timeNow - timeLine[currentSegment-1];
}
}
if (timeToNextSegment < mediaTimeToWait)
{
mediaTimeToWait = timeToNextSegment;
}
// Do the ugly math to compute what value to pass to
// wait():
long waitTime;
if (ourRate > 0)
{
waitTime = (long) ((float) mediaTimeToWait / ourRate) /
1000000;
}
else
{
waitTime = (long) ((float) mediaTimeToWait / -ourRate) /
1000000;
}
// Add one because we just rounded down and we don't
// really want to waste CPU being woken up early.
waitTime++;
if (waitTime > 0)
{
// Bug in some systems deals poorly with really large
// numbers. We will cap our wait() to 1000 seconds
// which point we will probably decide to wait again.
|
if (waitTime > 1000000) waitTime = 1000000;
try
{
wait (waitTime);
}
catch (InterruptedException e)
{
// NOT REACHED
}
}
}
public void close()
{
}
public Control getControl(String type)
{
return null;
}
public long getMediaNanoseconds()
{
return 0;
}
}
|