Playing Media

   

JMF player programs play audio and video media by routing it to speakers and monitors . These programs present VCR-like controls to give users the opportunity to control stream playback. In many cases, a user can fast forward and rewind media streams (in addition to starting and stopping).

Playing media streams requires players , which are objects created from classes that implement the Player interface. You can obtain a player by calling one of Manager 's createPlayer methods . Each player receives a media stream from a data source and renders it at precise moments in time. The rendering destination (such as speakers or monitor) for a specific track depends on the track's format.

A player can be in any one of six states: Unrealized, Realizing , Realized, Prefetching , Prefetched, and Started. When a player has been created but does not know anything about the media it will be playing, the player is said to be in the Unrealized state .

When a player's realize method is called, the player moves from the Unrealized state into a Realizing state . A realizing player determines its resource requirements. During realization, it acquires all its non-exclusive resources. (An exclusive resource is a limited resource ”such as a specific hardware device ”that can be used by one and only one player at a time.) A realizing player might download its resources over a network. As a result, realizing can be a time- intensive task.

When realizing is finished, the player moves to the Realized state . In this state, a player knows what resources it needs and information about the type of media that will be presented. Because a realized player knows how to render its data, it can provide visual components and controls (objects ”such as audio gain ”that control various aspects of a media stream). The player's connections to other objects in the system are in place, but it does not own any resources that would prevent another player from starting.

When the player's prefetch method is called, it moves from the Realized state into the Prefetching state . A prefetching player is preparing to present its media. During this phase, the player preloads its media data, obtains exclusive resources, and does whatever else is necessary to prepare itself to play. Prefetching might have to recur if a player's media presentation is repositioned, or if a change in the player's rate requires that additional buffers be acquired or alternate processing take place.

When a player finishes prefetching, it moves into the Prefetched state . A prefetched player is ready to be started.

A player enters the Started state , after its start method is called. A started player's time base time and media time are mapped and its clock is running, although the player might be waiting for a particular time to begin presenting its media data. This time is referred to as latency .

Figure 20.2 illustrates the various states of a player. At any point in time, the player is in one and only one of these states.

Figure 20.2. A player transitions through six states as it operates.

graphics/20fig02.gif

Method calls that transition a player from one state to another are shown in bold. Furthermore, various events are fired to indicate state transitions. Prior to the Started state, the player is said to be stopped . After the Started state is reached, the player is actively playing media.

To learn more about state transitions, and to see what a simple player looks like, take a look at Listing 20.2 . This listing presents source code for a PlayerDemo1 application, which can play audio and video media streams.

Listing 20.2 The PlayerDemo1 Application Source Code
 // PlayerDemo1.java import javax.media.*; import java.awt.*; import java.awt.event.*; class PlayerDemo1 extends Frame implements ControllerListener {    Component vc;    Player player;    PlayerDemo1 (String title, String mediaURL)    {       super (title);       addWindowListener (new WindowAdapter ()                          {                              public void windowClosing (WindowEvent e)                              {                                 player.close ();                                 System.exit (0);                              }                          } );       try       {          player = Manager.createPlayer (new MediaLocator (mediaURL));       }       catch (java.io.IOException e)       {          terminate (e.toString ());       }       catch (NoPlayerException e)       {          terminate ("Could not find a player.");       }       if (player == null)           terminate ("Trouble creating player.");       player.addControllerListener (this);       player.prefetch ();    }    public void controllerUpdate (ControllerEvent e)    {       if (e instanceof ControllerClosedEvent)           System.exit (0);       if (e instanceof EndOfMediaEvent)       {           player.setMediaTime (new Time (0));           player.start ();           return;       }       if (e instanceof PrefetchCompleteEvent)       {           player.start ();           return;       }      if (e instanceof RealizeCompleteEvent)       {           vc = player.getVisualComponent ();           if (vc != null)               add (vc);           pack ();           setResizable (false);           setVisible (true);       }    }    void terminate (String s)    {       System.out.println (s);       System.exit (-1);    }    public static void main (String [] args)    {       if (args.length != 1)       {           System.out.println ("usage: java PlayerDemo1 url");           return;       }       new PlayerDemo1 ("Player Demo1", args [0]);    } } 

PlayerDemo1 's constructor calls Manager 's createPlayer method with a MediaLocator argument to create and return a reference to a player. The MediaLocator argument identifies the protocol and location of the source of a media stream. Although similar to uniform resource locators, media locators do not require protocol handlers to be installed. If a player cannot be found, a NoPlayerException object is thrown.

The return value should be a player. Although the documentation does not suggest that createPlayer returns null , a check for null is made, just to be safe. Assuming that a player has been created, its addControllerListener method is called to register PlayerDemo1 as a listener for various controller events. (The player is the controller.) These events are fired for a number of reasons, including player transitions from one state to another. Finally, the player's prefetch method is called to put the player into its Prefetching state.

All the good stuff happens in the controllerUpdate method: If something should go wrong and the controller is closed (as indicated by a ControllerClosedEvent object), PlayerDemo1 exits. This might happen if the player does not "understand" a certain format of media. When the end of a media stream is reached (as indicated by an EndOfMediaEvent object), the stream is rewound to the beginning, by calling the player's setMediaTime method. This is followed by a call to the player's start method to restart the stream, which results in a continuous loop. (You don't have to set your player into a continuous loop.) After the player is in its Prefetched state, the only way it can reach its Started state is for its start method to be called. This occurs when controllerUpdate receives a PrefetchCompleteEvent object. When the player has reached its Realized state, a RealizeCompleteEvent object is sent to controllerUpdate . At this point, the player's getVisualComponent method is called to return its visual component. For video streams, this component is a window that contains image frames . For audio streams, null will most likely be returned to indicate that there is no visual component. The pack method is called to ensure a proper size and layout, setResizable is called with a false argument to prevent the user from resizing the window, and setVisible ensures that both the frame window and visual component are visible.

Figure 20.3 shows an image of the Orion Nebula. This image is part of an MPEG movie that's stored in the OrionMosaic.mpg file. To run this movie, you would type the following line in a Microsoft Windows DOS command window (assuming that Microsoft Windows is your operating system):

Figure 20.3. PlayerDemo1 's output shows the Orion Nebula in all its glory .

graphics/20fig03.gif

 java PlayerDemo1 file:OrionMosaic.mpg 

Wouldn't it be nice if there was some way to replay this movie, without having to stop and restart PlayerDemo1 ? There actually is a way to accomplish this task. Listing 20.3 presents source code for the PlayerDemo2 application that demonstrates this technique.

Listing 20.3 The PlayerDemo2 Application Source Code
 // PlayerDemo2.java import javax.media.*; import java.awt.*; import java.awt.event.*; class PlayerDemo2 extends Frame implements ControllerListener {    Component cc, vc;    Player player;    PlayerDemo2 (String title, String mediaURL)    {       super (title);       addWindowListener (new WindowAdapter ()                          {                              public void windowClosing (WindowEvent e)                              {                                 player.close ();                                 System.exit (0);                              }                          });       try       {          player = Manager.createPlayer (new MediaLocator (mediaURL));       }       catch (java.io.IOException e)       {          terminate (e.toString ());       }       catch (NoPlayerException e)       {          terminate ("Could not find a player.");       }       if (player == null)           terminate ("Trouble creating player.");       player.addControllerListener (this);       player.prefetch ();    }    public void controllerUpdate (ControllerEvent e)    {       if (e instanceof ControllerClosedEvent)           System.exit (0);       if (e instanceof EndOfMediaEvent)       {           player.setMediaTime (new Time (0));           return;       }       if (e instanceof RealizeCompleteEvent)       {           vc = player.getVisualComponent ();           if (vc != null)               add (vc, BorderLayout.CENTER);           cc = player.getControlPanelComponent ();           if (cc != null)               add (cc, (vc != null) ? BorderLayout.SOUTH                                     : BorderLayout.CENTER);           pack ();           setResizable (false);           setVisible (true);       }    }    void terminate (String s)    {       System.out.println (s);       System.exit (-1);    }    public static void main (String [] args)    {       if (args.length != 1)       {           System.out.println ("usage: java PlayerDemo2 url");           return;       }       new PlayerDemo2 ("Player Demo2", args [0]);    } } 

The difference between PlayerDemo1 and PlayerDemo2 is that the player's getControlPanelComponent method is called to retrieve a play bar control panel by which you can start, stop, and control playback. This play bar is shown in Figure 20.4, together with a frame (a still image) from an MPEG movie of the Cassini spacecraft approaching Saturn.

Figure 20.4. PlayerDemo2 's output includes a play bar control panel across the bottom.

graphics/20fig04.gif

Tip

The getVisualComponent and getControlPanelComponent methods return heavyweight AWT components. Although these components don't cause problems in some Swing programs, they will cause problems in others. (See Chapter 15, "And Then There Was Swing," for examples of various problems that occur when heavyweight AWT components are intermixed with lightweight Swing components.) You can still use these components in Swing programs, if you first call Manager 's setHint method with LIGHTWEIGHT_RENDERER and Boolean (true) arguments. This argument causes Manager to try and create a player with a renderer that interoperates with lightweight components. As an example, you can set this hint, after creating a player, as follows :

 Manager.setHint (Manager.LIGHTWEIGHT_RENDERER,                  new Boolean (true)); 

One big problem with downloading media over the Internet is that it can take quite a while ” especially if you are using a modem. Some kind of progress indicator would help users know what's happening. Fortunately, JMF includes a special component, called a progress bar, for indicating the current state of a download. This component is included in Listing 20.4 's PlayerDemo3 source code.

Listing 20.4 The PlayerDemo3 Application Source Code
 // PlayerDemo3.java import javax.media.*; import java.awt.*; import java.awt.event.*; class PlayerDemo3 extends Frame implements ControllerListener {    Component cc, pbar, vc;    Player player;    PlayerDemo3 (String title, String mediaURL)    {       super (title);       addWindowListener (new WindowAdapter ()                          {                              public void windowClosing (WindowEvent e)                              {                                 player.close ();                                 System.exit (0);                              }                          });       try       {          player = Manager.createPlayer (new MediaLocator (mediaURL));       }       catch (java.io.IOException e)       {          terminate (e.toString ());       }       catch (NoPlayerException e)       {          terminate ("Could not find a player.");       }       if (player == null)           terminate ("Trouble creating player.");       player.addControllerListener (this);       player.prefetch ();    }    public void controllerUpdate (ControllerEvent e)    {       if (e instanceof CachingControlEvent)       {           CachingControlEvent cce = (CachingControlEvent) e;           CachingControl cc = cce.getCachingControl ();           long cc_progress = cce.getContentProgress ();           long cc_length = cc.getContentLength ();           if (pbar == null)               if ((pbar = cc.getControlComponent ()) != null)               {                   add (pbar, BorderLayout.NORTH);                   pack ();               }           if (cc_progress == cc_length && pbar != null)           {               remove (pbar);               pbar = null;               pack ();           }           return;       }       if (e instanceof ControllerClosedEvent)           System.exit (0);       if (e instanceof EndOfMediaEvent)       {           player.setMediaTime (new Time (0));           return;       }       if (e instanceof RealizeCompleteEvent)       {           vc = player.getVisualComponent ();           if (vc != null)               add (vc, BorderLayout.CENTER);           cc = player.getControlPanelComponent ();           if (cc != null)               add (cc, (vc != null) ? BorderLayout.SOUTH                                     : BorderLayout.CENTER);           pack ();           setResizable (false);           setVisible (true);       }    }    void terminate (String s)    {       System.out.println (s);       System.exit (-1);    }    public static void main (String [] args)    {       if (args.length != 1)       {           System.out.println ("usage: java PlayerDemo3 url");           return;       }       new PlayerDemo3 ("Player Demo3", args [0]);    } } 

Each time an image is downloaded off the Internet, the player fires a CachingControlEvent object to the listener. This event's getCachingControl method returns a caching control object that describes the current state of the download. Its getContentLength method returns the length of the download, whereas the event's getContentProgress method returns the current location in the download. Finally, the caching control's getProgressBarComponent retrieves an AWT progress bar component. This progress bar is shown in Figure 20.5 .

Figure 20.5. PlayerDemo3 's output includes a progress bar ”shown as the "Loading media" indicator at the top of the window.

graphics/20fig05.gif

Caution

Although you can click the progress bar's pause and resume button to pause and resume the download, you should not click the play button when the download is paused . If you do, the program will most likely lock up. (This is an example of JMF's unpredictable behavior.) Furthermore, you should also avoid pressing the play button during download. (Experiments have shown that pressing Play while downloading can result in unpredictable behavior.) It's okay to press Play once download is complete.


   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net