TOC Prev Next
The transitions between states are controlled with five methods:
realize
prefetch
start
deallocate
stop
close
By controlling when these methods are called, you can manage the state of a
Player. For example, you might want to minimize start-latency by preparing the
Player to start before you actually start it.
You can implement the ControllerListener interface to manage these control
methods in response to changes in the Player's state. Listening for a Player's
state transitions is also important in other cases. For example, you cannot get a
Player's components until the Player has been Realized. By listening for a
RealizeCompleteEvent you can get the components as soon as the Player is
Realized.
5.1 Preparing a Player to Start
Most media Players cannot be started instantly. Before the Player can start,
certain hardware and software conditions must be met. For example, if the Player
has never been started, it might be necessary to allocate buffers in memory to
store the media data. Or, if the media data resides on a network device, the Player
might have to establish a network connection before it can download the data.
Even if the Player has been started before, the buffers might contain data that is
not valid for the current media position.
5.1.1 Realizing and Prefetching the Player
JMF breaks the process of preparing a Player to start into two phases, Realizing
and Prefetching. Realizing and Prefetching a Player before you start it minimizes
the time it takes the Player to begin presenting media when start is called and
helps create a highly-responsive interactive experience for the user. Implementing
the ControllerListener interface allows you to control when these operations
occur.
You call realize to move the Player into the Realizing state and begin the realization
process. You call prefetch to move the Player into the Prefetching state
and initiate the prefetching process. The realize and prefetch methods are
asynchronous and return immediately. When the Player completes the requested
operation, it posts a RealizeCompleteEvent or PrefetchCompleteEvent.
"Player States" on page 6 describes the operations that a Player performs in each
of these states.
A Player in the Prefetched state is prepared to start and its start-up latency cannot
be further reduced. However, setting the media time through setMediaTime might
return the Player to the Realized state, increasing its start-up latency.
Keep in mind that a Prefetched Player ties up system resources. Because some
resources, such as sound cards, might only be usable by one program at a time,
this might prevent other Players from starting.
5.1.2 Blocking until a Player is Realized
Many of the methods that can be called on a Player require that the Player be in
the Realized state. One way to guarantee that a Player is Realized when you call
these methods is to implement a method that calls realize and blocks until the
Player posts a RealizeCompleteEvent.
Note: Be aware that blocking on realize can produce unsatisfactory results. For
example, if an applet blocks while a Player is realizing, Applet.start and
Applet.stop will not be able to interrupt the process.
To block until a Player is Realized, you could implement a method called blockingRealize
that calls realize on your Player and returns when the Player
posts a RealizeCompleteEvent and your controllerUpdate method is called.
This requires that you implement the ControllerListener interface and register
as a listener with the Player. If you register as a listener with multiple Players,
your controllerUpdate method needs to determine which Player posted the
RealizeCompleteEvent.1
boolean realized = false;
public synchronized void blockingRealize()
{
myPlayer.realize();
while (!realized) {
try {
wait();
}
catch (java.lang.InterruptedException e) {
status.setText("Interrupted while waiting on
realize...exiting.");
System.exit(1);
}
}
}
public synchronized void controllerUpdate (ControllerEvent
event)
{
if (event instanceof RealizeCompleteEvent) {
realized = true;
notify();
}
else if (event instanceof EndOfMediaEvent) {
eomReached = true;
}
}
|
5.1.3 Determining a Player's Start-up Latency
To determine how much time is required to start a Player, you can call getStartLatency
. For Players that have a variable start latency, the return value of
getStartLatency represents the maximum possible start latency. For some
media types, getStartLatency might return LATENCY_UNKNOWN.
The start-up latency reported by getStartLatency might differ depending on the
Player's current state. For example, after a prefetch operation, the value
returned by getStartLatency is typically smaller. A Controller that can be
added to a Player will return a useful value once it is Prefetched. (For more information
about added Controllers, see "Using a Player to Manage and Synchronize
other Controllers" on page 29.)
5.2 Starting and Stopping a Player
Calling start moves a Player into the Started state. As soon as start is called,
methods that are only legal for stopped Players cannot be called until the Player
has been stopped.
If start is called and the Player has not been prefetched, start performs the
realize and prefetch operations as needed to move the Player into the Prefetched
state. The Player posts transition events as it moves through each state.
When stop is called on a Player, the Player is considered to be stopped immediately;
stop is synchronous. However, a Player can also stop asynchronously
when:
- The end of the media stream is reached.
- The stop time previously set with
setStopTime is reached.
- The
Player is data starved.
When a Player stops, it posts a StopEvent. To determine why the Player
stopped, you must listen for the specific stop events: DeallocateEvent, EndOfMediaEvent
, RestartingEvent, StopAtTimeEvent, StopByRequestEvent, and
DataStarvedEvent.
5.3 Releasing Player Resources
The deallocate method tells a Player to release any exclusive resources and
minimize its use of non-exclusive resources. Although buffering and memory
management requirements for Players are not specified, most Java Media Players
allocate buffers that are large by the standards of Java objects. A well-implemented
Player releases as much internal memory as possible when deallocate
is called.
The deallocate method can only be called on a Stopped Player. To avoid
ClockStartedErrors, you should call stop before you call deallocate. Calling
deallocate on a Player in the Prefetching or Prefetched state returns it to the
Realized state. If deallocate is called while the Player is realizing, the Player
posts a DeallocateEvent and returns to the Unrealized state. (Once a Player has
been realized, it can never return to the Unrealized state.)
You generally call deallocate when the Player is not being used. For example,
an applet should call deallocate as part of its stop method. By calling deallocate
, the program can maintain references to the Player, while freeing other
resources for use by the system as a whole. (JMF does not prevent a Realized
Player that has formerly been Prefetched or Started from maintaining information
that would allow it to be started up more quickly in the future.)
When you are finished with a Player (or other Controller) and are not going to
use it anymore, you should call close. The close method indicates that the Controller
will no longer be used and can shut itself down. Calling close releases
all of the resources that the Controller was using and causes the it to cease all
activity. When a Controller is closed, it posts a ControllerClosedEvent. A
closed Controller cannot be reopened and invoking methods on a closed Controller
might generate errors.
5.4 Implementing the ControllerListener Interface
ControllerListener is an asynchronous interface for handling events generated
by Controller objects. Using the ControllerListener interface enables you to
manage the timing of potentially time-consuming Player operations such as
prefetching.
To implement the ControllerListener interface, you need to:
- Implement the
ControllerListener interface in a class.
- Register that class as a listener by calling
addControllerListener on the
Controller that you want to receive events from.
When a Controller posts an event, it calls controllerUpdate on each registered
listener. Typically, controllerUpdate is implemented as a series of if-else
statements of the form:
if(event instanceof EventType){
...
} else if(event instanceof OtherEventType){
...
}
|
This filters out the events that you are not interested in. If you have registered as a
listener with multiple Controllers, you also need to determine which Controller
posted the event. ControllerEvents come "stamped" with a reference to
their source that you can access by calling getSource.
"Appendix D: ControllerAdapter" on page 73 provides the source for an implementation
of ControllerListener that can be easily extended to respond to particular
Events.
When you receive events from a Controller, you might need to do some additional
processing to ensure that the Controller is in the proper state before calling
a control method. For example, before calling any of the methods that are
restricted to Stopped Players, you should check the Player's target state by
calling getTargetState. If start has been called, the Player is considered to be
in the Started state, though it might be posting transition events as it prepares the
Player to present media.
Some types of ControllerEvents are stamped with additional state information.
For example, the StartEvent and StopEvent classes each define a method that
allows you to retrieve the media time at which the event occurred.
1
This example code is used with the permission of Bill Day and JavaWorld magazine. The
blockingRealize example code was first published by Bill Day in "Java Media Framework
Player API: Multimedia Comes to Java" in JavaWorld magazine, an online publication of Web
Publishing Inc., April 1997. Please see http://www.javaworld.com/javaworld/jw-04-1997/jw-
04-jmf.html for the complete article, example code listing, and demonstration applets.
TOC Prev Next
|