コンテンツ | | | 目次 JMF 2.0 API ガイド



3

Presenting Time-Based Media with JMF

To present time-based media such as audio or video with JMF, you use a Player. Playback can be controlled programmatically, or you can display a control-panel component that enables the user to control playback interactively. If you have several media streams that you want to play, you need to use a separate Player for each one. to play them in sync, you can use one of the Player objects to control the operation of the others.

A Processor is a special type of Player that can provide control over how the media data is processed before it is presented. Whether you're using a basic Player or a more advanced Processor to present media content, you use the same methods to manage playback. For information about how to control what processing is performed by a Processor, see Processing Time-Based Media with JMF.

The MediaPlayer bean is a Java Bean that encapsulates a JMF player to provide an easy way to present media from an applet or application. The MediaPlayer bean automatically constructs a new Player when a different media stream is selected, which makes it easier to play a series of media clips or allow the user to select which media clip to play. For information about using the MediaPlayer bean, see Presenting Media with the MediaPlayer Bean

Controlling a Player

To play a media stream, you need to construct a Player for the stream, configure the Player and prepare it to run, and then start the Player to begin playback.

Creating a Player

You create a Player indirectly through the media Manager. To display the Player, you get the Player object's components and add them to your applet's presentation space or application window.

When you need to create a new Player, you request it from the Manager by calling createPlayer or createProcessor. The Manager uses the media URL or MediaLocator that you specify to create an appropriate Player. A URL can only be successfully constructed if the appropriate corresponding URLStreamHandler is installed. MediaLocator doesn't have this restriction.

Blocking Until a Player is Realized

Many of the methods that can be called on a Player require the Player to be in the Realized state. One way to guarantee that a Player is Realized when you call these methods is to use the Manager createRealizedPlayer method to construct the Player. This method provides a convenient way to create and realize a Player in a single step. When this method is called, it blocks until the Player is Realized. Manager provides an equivalent createRealizeProcessor method for constructing a Realized Processor.

Note: Be aware that blocking until a Player or Processor is Realized can produce unsatisfactory results. For example, if createRealizedPlayer is called in an applet, Applet.start and Applet.stop will not be able to interrupt the construction process.

Using a ProcessorModel to Create a Processor

A Processor can also be created using a ProcessorModel. The ProcessorModel defines the input and output requirements for the Processor and the Manager does its best to create a Processor that meets these requirements. To create a Processor using a ProcessorModel, you call the Manager.createRealizedProcessor method. Example 3-1 creates a Realized Processor that can produce IMA4-encoded stereo audio tracks with a 44.1 kHz sample rate and a 16-bit sample size.

Example 3-1: Constructing a Processor with a ProcessorModel.
 AudioFormat afs[] = new AudioFormat[1]; 
 afs[0] = new AudioFormat("ima4", 44100, 16, 2); 
 Manager.createRealizedProcessor(new ProcessorModel(afs, null)); 

Since the ProcessorModel does not specify a source URL in this example, Manager implicitly finds a capture device that can capture audio and then creates a Processor that can encode that into IMA4.

Note that when you create a Realized Processor with a ProcessorModel you will not be able to specify processing options through the Processor object's TrackControls. For more information about specifying processing options for a Processor, see Processing Time-Based Media with JMF.

Displaying Media Interface Components

A Player generally has two types of user interface components, a visual component and a control-panel component. Some Player implementations can display additional components, such as volume controls and download-progress bars.

Displaying a Visual Component

A visual component is where a Player presents the visual representation of its media, if it has one. Even an audio Player might have a visual component, such as a waveform display or animated character.

To display a Player object's visual component, you:

  1. Get the component by calling getVisualComponent.
  2. Add it to the applet's presentation space or application window.

You can access the Player object's display properties, such as its x and y coordinates, through its visual component. The layout of the Player components is controlled through the AWT layout manager.

Displaying a Control Panel Component

A Player often has a control panel that allows the user to control the media presentation. For example, a Player might be associated with a set of buttons to start, stop, and pause the media stream, and with a slider control to adjust the volume.

Every Player provides a default control panel. To display the default control panel:

  1. Call getControlPanelComponent to get the Component.
  2. Add the returned Component to your applet's presentation space or application window.

If you prefer to define a custom user-interface, you can implement custom GUI Components and call the appropriate Player methods in response to user actions. If you register the custom components as ControllerListeners, you can also update them when the state of the Player changes.

Displaying a Gain-Control Component

Player implementations that support audio gain adjustments implement the GainControl interface. GainControl provides methods for adjusting the audio volume, such as setLevel and setMute. To display a GainControl Component if the Player provides one, you:

  1. Call getGainControl to get the GainControl from the Player. If the Player returns null, it does not support the GainControl interface.
  2. Call getControlComponent on the returned GainControl.
  3. Add the returned Component to your applet's presentation space or application window.

Note that getControls does not return a Player object's GainControl. You can only access the GainControl by calling getGainControl.

Displaying Custom Control Components

Many Players have other properties that can be managed by the user. For example, a video Player might allow the user to adjust brightness and contrast, which are not managed through the Player interface. You can find out what custom controls a Player supports by calling the getControls method.

For example, you can call getControls to determine if a Player supports the CachingControl interface.

Example 3-2: Using getControls to find out what Controls are supported.
 Control[] controls = player.getControls();
    for (int i = 0; i < controls.length; i++) {
       if (controls[i] instanceof CachingControl) {
          cachingControl = (CachingControl) controls[i];
       }
    } 

Displaying a Download-Progress Component

The CachingControl interface is a special type of Control implemented by Players that can report their download progress. A CachingControl provides a default progress-bar component that is automatically updated as the download progresses. To use the default progress bar in an applet:

  1. Implement the ControllerListener interface and listen for
    CachingControlEvents in controllerUpdate.
  2. The first time you receive a CachingControlEvent:
    1. Call getCachingControl on the event to get the caching control.
    2. Call getProgressBar on the CachingControl to get the default progress bar component.
    3. Add the progress bar component to your applet's presentation space.
  3. Each time you receive a CachingControlEvent, check to see if the download is complete. When getContentProgress returns the same value as getContentLength, remove the progress bar.

The Player posts a CachingControlEvent whenever the progress bar needs to be updated. If you implement your own progress bar component, you can listen for this event and update the download progress whenever CachingControlEvent is posted.

Setting the Playback Rate

The Player object's rate determines how media time changes with respect to time-base time; it defines how many units a Player object's media time advances for every unit of time-base time. The Player object's rate can be thought of as a temporal scale factor. For example, a rate of 2.0 indicates that media time passes twice as fast as the time-base time when the Player is started.

In theory, a Player object's rate could be set to any real number, with negative rates interpreted as playing the media in reverse. However, some media formats have dependencies between frames that make it impossible or impractical to play them in reverse or at non-standard rates.

To set the rate, you call setRate and pass in the temporal scale factor as a float value. When setRate is called, the method returns the rate that is actually set, even if it has not changed. Players are only guaranteed to support a rate of 1.0.

Setting the Start Position

Setting a Player object's media time is equivalent to setting a read position within a media stream. For a media data source such as a file, the media time is bounded; the maximum media time is defined by the end of the media stream.

To set the media time you call setMediaTime and pass in a Time object that represents the time you want to set.

Frame Positioning

Some Players allow you to seek to a particular frame of a video. This enables you to easily set the start position to the beginning of particular frame without having to specify the exact media time that corresponds to that position. Players that support frame positioning implement the FramePositioningControl.

To set the frame position, you call the FramePositioningControl seek method. When you seek to a frame, the Player object's media time is set to the value that corresponds to the beginning of that frame and a MediaTimeSetEvent is posted.

Some Players can convert between media times and frame positions. You can use the FramePositioningControl mapFrameToTime and mapTimeToFrame methods to access this information, if it's available. (Players that support FramePositioningControl are not required to export this information.) Note that there is not a one-to-one correspondence between media times and frames --a frame has a duration, so several different media times might map to the same frame. (See Getting the Media Time for more information.) -

Preparing 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.

Realizing and Prefetching a 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.

Note: Processor introduces a third phase to the preparation process called Configuring. During this phase, Processor options can be selected to control how the Processor manipulates the media data. For more information, see Selecting Track Processing Options.

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 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 and increase 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, having a Player in the Prefetched state might prevent other Players from starting.

Determining the Start 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 object'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, see Using a Player to Synchronize Controllers.)

Starting and Stopping the Presentation

The Clock and Player interfaces define the methods for starting and stopping presentation.

Starting the Presentation

You typically start the presentation of media data by calling start. The start method tells the Player to begin presenting media data as soon as possible. If necessary, start prepares the Player to start by performing the realize and prefetch operations. If start is called on a Started Player, the only effect is that a StartEvent is posted in acknowledgment of the method call.

Clock defines a syncStart method that can be used for synchronization. See Synchronizing Multiple Media Streams for more information.

To start a Player at a specific point in a media stream:

  1. Specify the point in the media stream at which you want to start by calling setMediaTime.
  2. Call start on the Player.
Stopping the Presentation

There are four situations in which the presentation will stop:

When a Player is stopped, its media time is frozen if the source of the media can be controlled. If the Player is presenting streamed media, it might not be possible to freeze the media time. In this case, only the receipt of the media data is stopped--the data continues to be streamed and the media time continues to advance.

When a Stopped Player is restarted, if the media time was frozen, presentation resumes from the stop time. If media time could not be frozen when the Player was stopped, reception of the stream resumes and playback begins with the newly-received data.

To stop a Player immediately, you call the stop method. If you call stop on a Stopped Player, the only effect is that a StopByRequestEvent is posted in acknowledgment of the method call.

Stopping the Presentation at a Specified Time

You can call setStopTime to indicate when a Player should stop. The Player stops when its media time passes the specified stop time. If the Player object's rate is positive, the Player stops when the media time becomes greater than or equal to the stop time. If the Player object's rate is negative, the Player stops when the media time becomes less than or equal to the stop time. The Player stops immediately if its current media time is already beyond the specified stop time.

For example, assume that a Player object's media time is 5.0 and setStopTime is called to set the stop time to 6.0. If the Player object's rate is positive, media time is increasing and the Player will stop when the media time becomes greater than or equal to 6.0. However, if the Player object's rate is negative, it is playing in reverse and the Player will stop immediately because the media time is already beyond the stop time. (For more information about Player rates, see Setting the Playback Rate.)

You can always call setStopTime on a Stopped Player. However, you can only set the stop time on a Started Player if the stop time is not currently set. If the Started Player already has a stop time, setStopTime throws an error.

You can call getStopTime to get the currently scheduled stop time. If the clock has no scheduled stop time, getStopTime returns Clock.RESET. To remove the stop time so that the Player continues until it reaches end-of-media, call setStopTime(Clock.RESET).

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 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 any 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 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.

Querying a Player

A Player can provide information about its current parameters, including its rate, media time, and duration.

Getting the Playback Rate

To get a Player object's current rate, you call getRate. Calling getRate returns the playback rate as a float value.

Getting the Media Time

To get a Player object's current media time, you call getMediaTime. Calling getMediaTime returns the current media time as a Time object. If the Player is not presenting media data, this is the point from which media presentation will commence.

Note that there is not a one-to-one correspondence between media times and frames. Each frame is presented for a certain period of time, and the media time continues to advance during that period.

For example, imagine you have a slide show Player that displays each slide for 5 seconds--the Player essentially has a frame rate of 0.2 frames per second.


Figure 3-1: Frame duration and media time.

If you start the Player at time 0.0, while the first frame is displayed, the media time advances from 0.0 to 5.0. If you start at time 2.0, the first frame is displayed for 3 seconds, until time 5.0 is reached.

Getting the Time-Base Time

You can get a Player object's current time-base time by getting the Player object's TimeBase and calling getTime:

 myCurrentTBTime = player1.getTimeBase().getTime();

When a Player is running, you can get the time-base time that corresponds to a particular media time by calling mapToTimeBase.

Getting the Duration of the Media Stream

Since programs often need to know how long a particular media stream will run, all Controllers implement the Duration interface. This interface defines a single method, getDuration. The duration represents the length of time that a media object would run, if played at the default rate of 1.0. A media stream's duration is only accessible through a Player.

If the duration can't be determined when getDuration is called, DURATION_UNKNOWN is returned. This can happen if the Player has not yet reached a state where the duration of the media source is available. At a later time, the duration might be available and a call to getDuration would return the duration value. If the media source does not have a defined duration, as in the case of a live broadcast, getDuration returns DURATION_UNBOUNDED.

Responding to Media Events

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.

Implementing the ControllerListener Interface

To implement the ControllerListener interface, you need to:

  1. Implement the ControllerListener interface in a class.
  2. 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.

Example 3-3: Implementing controllerUpdate.
 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.

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 object'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 contain 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.

Using ControllerAdapter

ControllerAdapter is a convenience class that implements ControllerListener and can be easily extended to respond to particular Events. To implement the ControllerListener interface using ControllerAdapter, you need to:

  1. Subclass ControllerAdapter and override the event methods for the events that you're interested in.
  2. Register your ControllerAdapter class as a listener for a particular Controller by calling addControllerListener.

When a Controller posts an event, it calls controllerUpdate on each registered listener. ControllerAdapter automatically dispatches the event to the appropriate event method, filtering out the events that you're not interested in.

For example, the following code extends a ControllerAdapter with a JDK 1.1 anonymous inner-class to create a self-contained Player that is automatically reset to the beginning of the media and deallocated when the Player reaches the end of the media.

Example 3-4: Using ControllerAdapter.
 player.addControllerListener(new ControllerAdapter() {
    public void endOfMedia(EndOfMediaEvent e) {
       Controller controller = e.getSource();
       controller.stop();
       controller.setMediaTime(new Time(0));
       controller.deallocate();
    }
 })

If you register a single ControllerAdapter as a listener for multiple Players, in your event method implementations you need to determine which Player generated the event. You can call getSource to determine where a ControllerEvent originated.

Synchronizing Multiple Media Streams

To synchronize the playback of multiple media streams, you can synchronize the Players by associating them with the same TimeBase. To do this, you use the getTimeBase and setTimeBase methods defined by the Clock interface. For example, you could synchronize player1 with player2 by setting player1 to use player2's time base:

 player1.setTimeBase(player2.getTimeBase());

When you synchronize Players by associating them with the same TimeBase, you must still manage the control of each Player individually. Because managing synchronized Players in this way can be complicated, JMF provides a mechanism that allows a Player to assume control over any other Controller. The Player manages the states of these Controllers automatically, allowing you to interact with the entire group through a single point of control. For more information, see See "Using a Player to Synchronize Controllers".

Using a Player to Synchronize Controllers

Synchronizing Players directly using syncStart requires that you carefully manage the states of all of the synchronized Players. You must control each one individually, listening for events and calling control methods on them as appropriate. Even with only a few Players, this quickly becomes a difficult task. Through the Player interface, JMF provides a simpler solution: a Player can be used to manage the operation of any Controller.

When you interact with a managing Player, your instructions are automatically passed along to the managed Controllers as appropriate. The managing Player takes care of the state management and synchronization for all of the other Controllers.

This mechanism is implemented through the addController and removeController methods. When you call addController on a Player, the Controller you specify is added to the list of Controllers managed by the Player. Conversely, when you call removeController, the specified Controller is removed from the list of managed Controllers.

Typically, when you need to synchronize Players or other Controllers, you should use this addController mechanism. It is simpler, faster, and less error-prone than attempting to manage synchronized Players individually.

When a Player assumes control of a Controller:

A managing Player only posts completion events for asynchronous methods after each of its managed Controllers have posted the event. The managing Player reposts other events generated by the Controllers as appropriate.

Adding a Controller

You use the addController method to add a Controller to the list of Controllers managed by a particular Player. To be added, a Controller must be in the Realized state; otherwise, a NotRealizedError is thrown. Two Players cannot be placed under control of each other. For example, if player1 is placed under the control of player2, player2 cannot be placed under the control of player1 without first removing player1 from player2's control.

Once a Controller has been added to a Player, do not call methods directly on the managed Controller. To control a managed Controller, you interact with the managing Player.

To have player2 assume control of player1, call:

 player2.addController(player1);
Controlling Managed Controllers

To control the operation of a group of Controllers managed by a particular Player, you interact directly with the managing Player.

For example, to prepare all of the managed Controllers to start, call prefetch on the managing Player. Similarly, when you want to start them, call start on the managing Player. The managing Player makes sure that all of the Controllers are Prefetched, determines the maximum start latency among the Controllers, and calls syncStart to start them, specifying a time that takes the maximum start latency into account.

When you call a Controller method on the managing Player, the Player propagates the method call to the managed Controllers as appropriate. Before calling a Controller method on a managed Controller, the Player ensures that the Controller is in the proper state. The following table describes what happens to the managed Controllers when you call control methods on the managing Player.

Function Stopped Player Started Player
setMediaTime Invokes setMediaTime on all managed Controllers. Stops all managed Controllers, invokes setMediaTime, and restarts Controllers.
setRate Invokes setRate on all managed Controllers. Returns the actual rate that was supported by all Controllers and set. Stops all managed Controllers, invokes setRate, and restarts Controllers. Returns the actual rate that was supported by all Controllers and set.
start Ensures all managed Controllers are Prefetched and invokes syncStart on each of them, taking into account their start latencies. Depends on the Player implementation. Player might immediately post a StartEvent.
realize The managing Player immediately posts a RealizeCompleteEvent. To be added, a Controller must already be realized. The managing Player immediately posts a RealizeCompleteEvent. To be added, a Controller must already be realized.
prefetch Invokes prefetch on all managed Controllers. The managing Player immediately posts a PrefetchCompleteEvent, indicating that all managed Controllers are Prefetched.
stop No effect. Invokes stop on all managed Controllers.
deallocate Invokes deallocate on all managed Controllers. It is illegal to call deallocate on a Started Player.
setStopTime Invokes setStopTime on all managed Controllers. (Player must be Realized.) Invokes setStopTime on all managed Controllers. (Can only be set once on a Started Player.)
syncStart Invokes syncStart on all managed Controllers. It is illegal to call syncStart on a Started Player.
close Invokes close on all managed Controllers. It is illegal to call close on a Started Player.

Table 3-1: Calling control methods on a managing player.
Removing a Controller

You use the removeController method to remove a Controller from the list of controllers managed by a particular Player.

To have player2 release control of player1, call:

 player2.removeController(player1);

Synchronizing Players Directly

In a few situations, you might want to manage the synchronization of multiple Player objects yourself so that you can control the rates or media times independently. If you do this, you must:

  1. Register as a listener for each synchronized Player.
  2. Determine which Player object's time base is going to be used to drive the other Player objects and set the time base for the synchronized Player objects. Not all Player objects can assume a new time base. For example, if one of the Player objects you want to synchronize has a push data-source, that Player object's time base must be used to drive the other Player objects.
  3. Set the rate for all of the Players. If a Player cannot support the rate you specify, it returns the rate that was used. (There is no mechanism for querying the rates that a Player supports.)
  4. Synchronize the states of all of the Player objects. (For example, stop all of the players.)
  5. Synchronize the operation of the Player objects:
    • Set the media time for each Player.
    • Prefetch each Player.
    • Determine the maximum start latency among the synchronized Player objects.
    • Start the Player objects by calling syncStart with a time that takes into account the maximum latency.

You must listen for transition events for all of the Player objects and keep track of which ones have posted events. For example, when you prefetch the Player objects, you need to keep track of which ones have posted PrefetchComplete events so that you can be sure all of them are Prefetched before calling syncStart. Similarly, when you request that the synchronized Player objects stop at a particular time, you need to listen for the stop event posted by each Player to determine when all of them have actually stopped.

In some situations, you need to be careful about responding to events posted by the synchronized Player objects. To be sure of the state of all of the Player objects, you might need to wait at certain stages for all of them to reach the same state before continuing.

For example, assume that you are using one Player to drive a group of synchronized Player objects. A user interacting with that Player sets the media time to 10, starts the Player, and then changes the media time to 20. You then:

  1. Pass along the first setMediaTime call to all of the synchronized Player objects.
  2. Call prefetch on each Player to prepare them to start.
  3. Call stop on each Player when the second set media time request is received.
  4. Call setMediaTime on each Player with the new time.
  5. Restart the prefetching operation.
  6. When all of the Player objects have been prefetched, start them by calling syncStart, taking into account their start latencies.

In this case, just listening for PrefetchComplete events from all of the Player objects before calling syncStart isn't sufficient. You can't tell whether those events were posted in response to the first or second prefetch operation. To avoid this problem, you can block when you call stop and wait for all of the Player objects to post stop events before continuing. This guarantees that the next PrefetchComplete events you receive are the ones that you are really interested in.

Example: Playing an MPEG Movie in an Applet

The sample program PlayerApplet demonstrates how to create a Player and present an MPEG movie from within a Java applet. This is a general example that could easily be adapted to present other types of media streams.

The Player object's visual presentation and its controls are displayed within the applet's presentation space in the browser window. If you create a Player in a Java application, you are responsible for creating the window to display the Player object's components.

Note: While PlayerApplet illustrates the basic usage of a Player, it does not perform the error handling necessary in a real applet or application. For a more complete sample suitable for use as a template, see JMF Applet.

Overview of PlayerApplet

The APPLET tag is used to invoke PlayerApplet in an HTML file. The WIDTH and HEIGHT fields of the HTML APPLET tag determine the dimensions of the applet's presentation space in the browser window. The PARAM tag identifies the media file to be played.

Example 3-5: Invoking PlayerApplet.
 <APPLET CODE=ExampleMedia.PlayerApplet 
WIDTH=320 HEIGHT=300> 
 <PARAM NAME=FILE VALUE="sample2.mpg">
 </APPLET>

When a user opens a web page containing PlayerApplet, the applet loads automatically and runs in the specified presentation space, which contains the Player object's visual component and default controls. The Player starts and plays the MPEG movie once. The user can use the default Player controls to stop, restart, or replay the movie. If the page containing the applet is closed while the Player is playing the movie, the Player automatically stops and frees the resources it was using.

To accomplish this, PlayerApplet extends Applet and implements the ControllerListener interface. PlayerApplet defines five methods:

Example 3-6: PlayerApplet.
 import java.applet.*;
 import java.awt.*;
 import java.net.*;
 import javax.media.*;
 
 public class PlayerApplet extends Applet implements ControllerListener {
    Player player = null;
    public void init() {
       setLayout(new BorderLayout());
       String mediaFile = getParameter("FILE");
       try {
          URL mediaURL = new URL(getDocumentBase(), mediaFile);
          player = Manager.createPlayer(mediaURL);
          player.addControllerListener(this);
       } 
       catch (Exception e) {
          System.err.println("Got exception "+e);
       }
    }
    public void start() {
       player.start();
    }
    public void stop() {
       player.stop();
       player.deallocate();
    }
    public void destroy() {
       player.close();
    }
    public synchronized void controllerUpdate(ControllerEvent event) {
       if (event instanceof RealizeCompleteEvent) {
          Component comp;
          if ((comp = player.getVisualComponent()) != null)
             add ("Center", comp); 
          if ((comp = player.getControlPanelComponent()) != null)
             add ("South", comp);	     
          validate();
       }
    }
 }

Initializing the Applet

When a Java applet starts, its init method is invoked automatically. You override init to prepare your applet to be started. PlayerApplet performs four tasks in init:

  1. Retrieves the applet's FILE parameter.
  2. Uses the FILE parameter to locate the media file and build a URL object that describes that media file.
  3. Creates a Player for the media file by calling Manager.createPlayer.
  4. Registers the applet as a controller listener with the new Player by calling addControllerListener. Registering as a listener causes the PlayerApplet controllerUpdate method to be called automatically whenever the Player posts a media event. The Player posts media events whenever its state changes. This mechanism allows you to control the Player object's transitions between states and ensure that the Player is in a state in which it can process your requests. (For more information, see Player States.)
Example 3-7: Initializing PlayerApplet.
 public void init() {
    setLayout(new BorderLayout());  
    // 1. Get the FILE parameter.
    String mediaFile = getParameter("FILE");
    try {
       // 2. Create a URL from the FILE parameter. The URL 
       // class is defined in java.net.
       URL mediaURL = new URL(getDocumentBase(), mediaFile);
       // 3. Create a player with the URL object.
       player = Manager.createPlayer(mediaURL);
       // 4. Add PlayerApplet as a listener on the new player.
       player.addControllerListener(this);
    } 
    catch (Exception e) {
       System.err.println("Got exception "+e);
    }
 }

Controlling the Player

The Applet class defines start and stop methods that are called automatically when the page containing the applet is opened and closed. You override these methods to define what happens each time your applet starts and stops.

PlayerApplet implements start to start the Player whenever the applet is started.

Example 3-8: Starting the Player in PlayerApplet.
 public void start() {
    player.start();
 }

Similarly, PlayerApplet overrides stop to stop and deallocate the Player:

Example 3-9: Stopping the Player in PlayerApplet.
 public void stop() {
    player.stop();
    player.deallocate();
 }

Deallocating the Player releases any resources that would prevent another Player from being started. For example, if the Player uses a hardware device to present its media, deallocate frees that device so that other Players can use it.

When an applet exits, destroy is called to dispose of any resources created by the applet. PlayerApplet overrides destroy to close the Player. Closing a Player releases all of the resources that it's using and shuts it down permanently.

Example 3-10: Destroying the Player in PlayerApplet.
    public void destroy() {
       player.close();
    }

Responding to Media Events

PlayerApplet registers itself as a ControllerListener in its init method so that it receives media events from the Player. To respond to these events, PlayerApplet implements the controllerUpdate method, which is called automatically when the Player posts an event.

PlayerApplet responds to one type of event, RealizeCompleteEvent. When the Player posts a RealizeCompleteEvent, PlayerApplet displays the Player object's components.

Example 3-11: Responding to media events in PlayerApplet.
 public synchronized void controllerUpdate(ControllerEvent event) 
{
    if (event instanceof RealizeCompleteEvent) {
       Component comp;
       if ((comp = player.getVisualComponent()) != null)
          add ("Center", comp);         
       if ((comp = player.getControlPanelComponent()) != null)
          add ("South", comp);
       validate();
    }
 }

A Player object's user-interface components cannot be displayed until the Player is Realized; an Unrealized Player doesn't know enough about its media stream to provide access to its user-interface components. PlayerApplet waits for the Player to post a RealizeCompleteEvent and then displays the Player object's visual component and default control panel by adding them to the applet container. Calling validate triggers the layout manager to update the display to include the new components.

Presenting Media with the MediaPlayer Bean

Using the MediaPlayer Java Bean (javax.media.bean.playerbean.MediaPlayer) is the simplest way to present media streams in your applets and applications. MediaPlayer encapsulates a full-featured JMF Player in a Java Bean. You can either use the MediaPlayer bean's default controls or customize its control Components.

One key advantage to using the MediaPlayer bean is that it automatically constructs a new Player when a different media stream is selected for playback. This makes it easy to play a series of media clips or enable the user to select the media clip that they want to play.

A MediaPlayer bean has several properties that you can set, including the media source:

Property Type Default Description
Show control panel Boolean Yes Controls whether or not the video control panel is visible.
Loop Boolean Yes Controls whether or not the media clip loops continuously.
Media location String N/A The location of the media clip to be played. It can be an URL or a relative address. For example:
file:///e:/video/media/
Sample1.mov

http://webServer/media/Sample1.mov

media/Sample1.mov
Show caching
control
Boolean No Controls whether or not the download-progress bar is displayed.
Fixed Aspect Ratio Boolean Yes Controls whether or not the media's original fixed aspect ratio is maintained.
Volume int 3
Controls the audio volume.

Table 3-2: Media bean properties.

To play a media clip with the MediaPlayer bean:

  1. Construct an instance of MediaPlayer:
MediaPlayer mp1 = new javax.media.bean.playerbean.MediaPlayer();
  1. Set the location of the clip you want to play:
 mp1.setMediaLocation(new java.lang.String("file:///E:/jvideo/media/
Sample1.mov"));
  1. Start the MediaPlayer:
 mp1.start();

You can stop playback by calling stop on the MediaPlayer:

 mp1.stop();

By setting up the MediaPlayer in your Applet's init method and starting the MediaPlayer in your Applet's start method, you can automatically begin media presentation when the Applet is loaded. You should call stop in the Applet's stop method so that playback halts when the Applet is stopped.

Alternatively, you can display the MediaPlayer bean's default control panel or provide custom controls to allow the user to control the media presentation. If you provide custom controls, call the appropriate MediaPlayer control and properties methods when the user interacts with the controls. For example, if you provide a custom Start button in your Applet, listen for the mouse events and call start when the user clicks on the button.

Presenting RTP Media Streams

You can present streaming media with a JMF Player constructed through the Manager using a MediaLocator that has the parameters of an RTP session. For more information about streaming media and RTP, see Working with Real-Time Media Streams.

When you use a MediaLocator to construct a Player for an RTP session, only the first RTP stream that's detected in the session can be presented-- Manager creates a Player for the first stream that's detected in the RTP session. For information about playing multiple RTP streams from the same session, see Receiving and Presenting RTP Media Streams.

Note: JMF-compliant implementations are not required to support the RTP APIs in javax.media.rtp, javax.media.rtp.event, and javax.media.rtp.rtcp. The reference implementations of JMF provided by Sun Microsystems, Inc. and IBM Corporation fully support these APIs.

Example 3-12: Creating a Player for an RTP session.
         String url= "rtp://224.144.251.104:49150/audio/1";
 
         MediaLocator mrl= new MediaLocator(url);
         
         if (mrl == null) {
             System.err.println("Can't build MRL for RTP");
             return false;
         }
         
         // Create a player for this rtp session
         try {
             player = Manager.createPlayer(mrl);
         } catch (NoPlayerException e) {
             System.err.println("Error:" + e);
             return false;
         } catch (MalformedURLException e) {
             System.err.println("Error:" + e);
             return false;
         } catch (IOException e) {
             System.err.println("Error:" + e);
             return false;
         }

When data is detected on the session, the Player posts a RealizeCompleteEvent. By listening for this event, you can determine whether or not any data has arrived and if the Player is capable of presenting any data. Once the Player posts this event, you can retrieve its visual and control components.

Listening for RTP Format Changes

When a Player posts a FormatChangeEvent, it can indicate that a payload change has occurred. Player objects constructed with a MediaLocator automatically process payload changes. In most cases, this processing involves constructing a new Player to handle the new format. Programs that present RTP media streams need to listen for FormatChangeEvents so that they can respond if a new Player is created.

When a FormatChangeEvent is posted, check whether or not the Player object's control and visual components have changed. If they have, a new Player has been constructed and you need to remove references to the old Player object's components and get the new Player object's components.

Example 3-13: Listening for RTP format changes.
     public synchronized void controllerUpdate(ControllerEvent ce) {
         if (ce instanceof FormatChangeEvent) {
             Dimension vSize = new Dimension(320,0);
             Component oldVisualComp = visualComp;
             
             if ((visualComp = player.getVisualComponent()) != null) {
                 if (oldVisualComp != visualComp) {
                     if (oldVisualComp != null) {
                         oldVisualComp.remove(zoomMenu);
                     }                    
                     framePanel.remove(oldVisualComp);
                     
                     vSize = visualComp.getPreferredSize();
                     vSize.width = (int)(vSize.width * defaultScale);
                     vSize.height = (int)(vSize.height * defaultScale);
                     
                     framePanel.add(visualComp);
                     
                     visualComp.setBounds(0, 
                                          0, 
                                          vSize.width, 
                                          vSize.height);
                     addPopupMenu(visualComp);
                 }
             }
             
             Component oldComp = controlComp;
             
             controlComp = player.getControlPanelComponent();
 
             if (controlComp != null) 
             {
                 if (oldComp != controlComp)
                 {
                     framePanel.remove(oldComp);
                     framePanel.add(controlComp);
                                          
                     if (controlComp != null) {
                         int prefHeight = controlComp
                                          .getPreferredSize()
                                          .height;
                         
                         controlComp.setBounds(0, 
                                               vSize.height,
                                               vSize.width,
                                               prefHeight);
                     }
                 }
             }
         }
     }



コンテンツ | | | 目次

著作権 © 1998-1999 Sun Microsystems, Inc. All Rights Reserved.