/*
* @(#)Manager.java 1.76 03/04/30
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved.
*/
package javax.media;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;
import javax.media.protocol.DataSource;
import javax.media.protocol.PullDataSource;
import javax.media.protocol.PushDataSource;
import javax.media.protocol.PullBufferDataSource;
import javax.media.protocol.PushBufferDataSource;
import javax.media.protocol.URLDataSource;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.SourceCloneable;
import javax.media.control.TrackControl;
import javax.media.Format;
import javax.media.format.AudioFormat;
import javax.media.format.VideoFormat;
import javax.media.control.FormatControl;
import javax.media.protocol.CaptureDevice;
import javax.media.rtp.RTPManager;
// For mesages logging.
import com.sun.media.Log;
/**
* <code>Manager</code> is the access point for obtaining
* system dependent resources such as <code>Players</code>,
* <code>DataSources</code>, <code>Processors</code>, <code>DataSinks</code>,
* the system <code>TimeBase</code>, the cloneable and merging utility
* <code>DataSources</code>.
* <p>
*
* A <code>DataSource</code> is an object used to
* deliver time-based multimedia data that is specific
* to a delivery protocol.
* <p>
* A <code>Player</code> is an object used to
* control and render multimedia data that
* is specific to the content type of the data.
* <p>
* A <code>Processor</code> is an object similar to a Player which is
* used to process data and output the processed data.
* <p>
* A <code>DataSink</code> is an object that takes a <code>DataSource</code>
* as input and renders the output to a specified destination.
* <p>
* A <code>DataSource</code> provides
* a <code>Player</code>, <code>Processor</code> or <code>DataSink</code>
* with media data;
* a <CODE>Player</CODE>, <code>Processor</code> or <code>DataSink</code>
* must have a <CODE>DataSource</CODE>.
* <code>Manager</code> provides access to a protocol and media independent
* mechanism for constructing <code>DataSources</code>, <code>Players</code>,
* <code>Processors</code> and <code>DataSinks</code>.
*
* <h2>Creating Players and DataSources</h2>
*
* <code>Manager</code> will create<code>Players</code> from a
* <code>URL</code>, a <CODE>MediaLocator</CODE> or a <code>DataSource</code>.
* Creating a <code>Player</code> requires the following:
* <ul>
* <li> Obtain the connected <code>DataSource</code> for the specified
* protocol
* <li> Obtain the <code>Player</code> for the content-type
* specified by the <code>DataSource</code>
* <li> Attach the <code>DataSource</code> to the <code>Player</code>
* using the <code>setSource</code> method.
* </ul>
*
* <h3>Finding DataSources by Protocol</h3>
*
* A <code>MediaLocator</code> defines a protocol for obtaining
* content.
* <code>DataSources</code> are identified by the protocol
* that they support. <code>Manager</code> uses the protocol
* name to find <code>DataSource</code> classes.
* <p>
*
* To find a <code>DataSource</code> using a <code>MediaLocator</code>,
* <code>Manager</code> constructs a list of class names from the protocol
* package-prefix list and the protocol name obtained
* from the <code>MediaLocator</code>.
* For each class name in the constructed list a new <code>DataSource</code>
* is instantiated, the <code>MediaLocator</code> is attached,
* and the <code>DataSource</code> is connected.
* If no errors have occurred, the procces is considered finished and the
* connected <code>DataSource</code> is used by
* <code>Manager</code> in any following operations.
* If there was an error then the next class name in the list
* is tried.
* The exact details of the search algorithm is described in
* the method documentation below.
*
* <h3>Finding Players by Content Type</h3>
*
* A <code>Player</code> is a <code>MediaHandler</code>.
* A <code>MediaHandler</code> is a an object that reads
* data from a <code>DataSource</code>. There are three types
* of supported <code>MediaHandler</code>: <code>MediaProxy</code>,
* <code>Player</code> and <code>Processor</code>.
* <p>
*
* <code>MediaHandlers</code> are identified by the content type that they
* support. A <code>DataSource</code> identifies the content type
* of the data it produces with the <code>getContentType</code> method.
* <code>Manager</code> uses the content type name to
* find instances of <code>MediaHandler</code>.
* <p>
*
* To find a <code>MediaHandler</code> using a content type name,
* <code>Manager</code> constructs a list of class names from
* the content package-prefix list and the content type name.
* For each class name in the constructed list a new <code>MediaHandler</code>
* is instantiated, and the <code>DataSource</code> is attached to
* the <code>MediaHandler</code> using <coded>MediaHandler.setSource</code>.
* <p>
*
* If the <code>MediaHandler</code> is a <code>Player</code> and the
* <code>setSource</code> was successful the process is finished
* and the <code>Player</code> is returned.
* If the <code>setSource</code> failed, another name in the
* list is tried.
* <p>
*
* If the <code>MediaHandler</code> is a <code>MediaProxy</code>
* then a new <code>DataSource</code> is obtained from the
* <code>MediaProxy</code>, a new list is created for the
* content type the <code>DataSource</code> supports and the
* whole thing is tried again.
* <p>
*
* If a valid <code>Player</code> is not found then the whole
* procedure is repeated with "unknown" substituted
* for the content-type name. The "unknown" content type is supported
* by generic <code>Players</code> that are capable of handling
* a large variety of media types, often in a platform dependent
* way.
*<p>
*
* The detailed creation algorithm is specified in the methods below.
*
* <h3>Creating a Realized Player</h3>
* Versions of <code>createRealizedPlayer</code> calls are provided as an
* acceleration to create a Player. The returned player is in the
* <I>Realized</I> state. In addition to <code>NoPlayerException</code>
* and <code>IOException</code>, <code>CannotRealizeException</code> can
* be thrown if the <code>Manager</code> cannot realize the
* <code>Player</code>.
* <p>
*
* <h2>Creating Processors</h2>
*
* <code>Processors</code> are created in the same way as <code>Players</code>
* as outlined above. <code>Manager</code> also provides an additional way
* to create a Processor via the <code>createRealizedProcessor</code> call. A
* <code>ProcessorModel</code> is used to fully identify the
* input and output requirements of a <code>Processor</code>. The
* <code>createRealizedProcessor</code> call takes a
* <code>ProcessorModel</code> as input and create a <code>Processor</code>
* that adheres to the given
* <code>ProcessorModel</code>. The returned Processor is
* in the <I>Realized</I> state. The method is a blocking call.<p>
* If the <code>Manager</code> fails to find a <code>Processor</code>
* that fits the <code>ProcessorModel</code>, a
* <code>NoProcessorException</code> is thrown. If there is a problem
* creating and realizing a <code>Processor</code>, it will throw an
* <code>IOException</code> or <code>CannotRealizeException</code> depending
* on the circumstances.
*
* <h2>Creating DataSinks</h2>
*
* <code>DataSinks</code> are created from an input <code>DataSource</code>
* <code>MediaLocator</code>. The <code>MediaLocator</code> identifies the
* protocol and content of the <code>DataSink</code> to be used. The
* search for the particular <code>DataSink</code> class to be created
* is similar to the process of creating a <code>DataSource</code>.
* The detail search and creation algorithm is described in the method
* documentation below.
*
* <h2>Player and Processor Threads</h2>
*
* <code>Players</code> and <code>Processors</code> process media data
* asynchronously from the main program flow.
* This implies that a <code>Player</code> or <code>Processor</code> must
* often manage one or more threads.
* The threads managed by the <code>Player</code> or <code>Processor</code>
* are not in the thread group of the application that calls
* <code>createPlayer</code> or <code>createProcessor</code>.
*
* <h2>System Time Base</h2>
*
* All <code>Players</code> need a <code>TimeBase</code>. Many
* use a system-wide <code>TimeBase</code>, often based on
* a time-of-day clock.
* <code>Manager</code> provides access to the system <code>TimeBase</code>
* through <code>getSystemTimeBase</code>.
*
* <h2>Cloning and Merging DataSources</h2>
*
* <code>DataSources</code> can be cloned or merged. If a
* <code>DataSource</code> is cloned, more than one
* <code>MediaHandler</code> can use it as input. Merging more than one
* <code>DataSources</code> will generate one <code>DataSource</code> which
* contains all the <code>SourceStreams</code> of the constituent
* <code>DataSources</code><p>
*
* The <code>Manager</code> provides two methods:
* <code>createCloneableDataSource</code> and
* <code>createMergingDataSource</code> for such purpose.
* <p>
*
* <h2>Manager Hints</h2>
*
* Using the <code>setHint</code> method, the preference for how
* the <code>Manager</code> creates the objects can be specified.
* However, a particular implementation of the <code>Manager</code>
* can choose to ignore the requested hints.
* @see #MAX_SECURITY
* @see #CACHING
* @see #LIGHTWEIGHT_RENDERER
* @see #PLUGIN_PLAYER
* <p>
*
*
* @since 1.0 , new methods added in 2.0
* @see java.net.URL
* @see MediaLocator
* @see PackageManager
* @see javax.media.protocol.DataSource
* @see javax.media.protocol.URLDataSource
* @see MediaHandler
* @see Player
* @see Processor
* @see MediaProxy
* @see TimeBase
*
* @version 2.0, 98/05/18.
*/
public final class Manager {
private static String VERSION = "2.1.1e";
/**
* Boolean hint to turn on/off maximum security.
*/
public static final int MAX_SECURITY = 1;
/**
* Boolean hint to turn on/off the use of playback caching.
*/
public static final int CACHING = 2;
/**
* Boolean hint to turn on/off the use of light weight rendering.
* If on, the <code>Manager</code> will try to create
* <code>Players</code> that use renderers which can interoperate with
* light weight GUI components.
*/
public static final int LIGHTWEIGHT_RENDERER = 3;
/**
* Boolean hint to request the <code>Manager</code> to create
* <code>Players</code> that support
* <code>PlugIns</code>. Such <code>Players</code> use
* <code>PlugIns</code> to demultiplex, decode, render or multiplex
* the data. It will also support <code>TrackControls</code> for
* application level programming.
*/
public static final int PLUGIN_PLAYER = 4;
private static int numberOfHints = 4; // Update this as you add more hints
private static SystemTimeBase sysTimeBase = null;
public final static String UNKNOWN_CONTENT_NAME = "unknown";
private static boolean jdkInit = false;
private static Method forName3ArgsM;
private static Method getSystemClassLoaderM;
private static ClassLoader systemClassLoader;
private static Method getContextClassLoaderM;
private static String fileSeparator = System.getProperty("file.separator");
private static Hashtable hintTable = new Hashtable();
static {
/* Default values for the hints */
hintTable.put(new Integer(MAX_SECURITY), new Boolean(false));
hintTable.put(new Integer(CACHING), new Boolean(true));
hintTable.put(new Integer(LIGHTWEIGHT_RENDERER), new Boolean(false));
hintTable.put(new Integer(PLUGIN_PLAYER), new Boolean(false));
}
/**
* This private constructor keeps anyone from actually
* getting a <CODE>Manager</CODE>.
*/
private Manager() {}
/**
* Returns the version string for this revision of JMF.
*/
public static String getVersion() {
return VERSION;
}
/**
* Create a <CODE>Player</CODE> for the specified media.
* This creates a MediaLocator from the URL and then
* calls <CODE>createPlayer</CODE>.
*
* @param sourceURL The <CODE>URL</CODE> that describes the media data.
* @return A new <CODE>Player</CODE>.
* @exception NoPlayerException Thrown if no <CODE>Player</CODE>
* can be found.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
public static Player createPlayer(URL sourceURL)
throws IOException, NoPlayerException {
return createPlayer(new MediaLocator(sourceURL));
}
/**
* Create a <code>Player</code> for the specified media.
* <p>
* The algorithm for creating a <CODE>Player</CODE> from
* a <code>MediaLocator</code> is:
* <ol>
* <li>Get the protocol from the <code>MediaLocator</code>.
* <li>Get a list of <code>DataSource</code> classes that
* support the protocol, using the protocol package-prefix-list.
* <li> For each source class in the list:
* <ol>
* <li>Instantiate a new <code>DataSource</code>,
* <li>Call the <code>connect</code> method to connect the source.
* <li>Get the media content-type-name (using <code>getContentType</code>)
* from the source.
* <li>Get a list of <code>MediaHandler</code> classes that support the
* media-content-type-name, using the content package-prefix-list.
* <li>For each <code>MediaHandler</code> class in the list:
* <ol>
* <li>Instantiate a new <code>MediaHandler</code>.
* <li>Attach the source to the <code>MediaHandler</code> by calling
* <code>MediaHandler.setSource</code>.
* <li>If there are no failures, determine the type of
* the <code>MediaHandler</code>; otherwise try the next
* <coded>MediaHandler</code> in the list.
* <li>If the <code>MediaHandler</code> is a <code>Player</code>,
* return the new <code>Player</code>.
* <li>If the <code>MediaHandler</code> is a <code>MediaProxy</code>,
* obtain a new <code>DataSource</code> from the <code>MediaProxy</code>,
* obtain the list of <code>MediaHandlers</code> that support the new
* <code>DataSource</code>, and continue searching the new list.
* </ol>
* <li>If no <code>MediaHandler</code> is found for this source,
* try the next source in the list.
* </ol>
* <li>If no <code>Player</code> is found after trying all of the sources,
* reuse the source list.<br>
* This time, for each source class in the list:
* <ol>
* <li>Instantiate the source.
* <li>Call the <code>connect</code> method to connect to the source.
* <li>Use the content package-prefix-list to create a list of
* <code>MediaHandler</code> classes that support the
* "unknown" content-type-name.
* <li>For each <code>MediaHandler</code> class in the list,
* search for a <code>Player</code> as in the previous search.
* <ol>
* <li>If no <code>Player</code> is found after trying all of the sources,
* a <CODE>NoPlayerException</CODE> is thrown.
* </ol>
* </ol>
* @param sourceLocator A <CODE>MediaLocator</CODE> that describes
* the media content.
* @return A <CODE>Player</CODE> for the media described by the source.
* @exception NoPlayerException Thrown if no <CODE>Player</CODE> can
* be found.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
public static Player createPlayer(MediaLocator sourceLocator)
throws IOException, NoPlayerException {
Player newPlayer = null;
Hashtable sources = new Hashtable(10); // A repository of connected sources.
boolean needPluginPlayer = ((Boolean)Manager.getHint(PLUGIN_PLAYER)).booleanValue();
// For RTP, the "non-plugin" player
// (com.sun.media.content.rtp.Handler) actually supports plugin.
String protocol = sourceLocator.getProtocol();
if (protocol != null &&
(protocol.equalsIgnoreCase("rtp") || protocol.equalsIgnoreCase("rtsp")))
needPluginPlayer = false;
try {
newPlayer = createPlayerForContent(sourceLocator, needPluginPlayer, sources);
} catch (NoPlayerException e) {
// ... and if that doesn't work, try finding
// a player for the UNKNOWN_CONTENT_NAME.
if (needPluginPlayer)
throw e;
newPlayer = createPlayerForContent(sourceLocator, true, sources);
}
// Disconnect the unsed sources
if (sources.size() != 0) {
Enumeration enum = sources.elements();
while (enum.hasMoreElements()) {
DataSource ds = (DataSource)enum.nextElement();
ds.disconnect();
}
}
return newPlayer;
}
/**
* Create a <code>Player</code> for the <code>DataSource</code>.
* <p>
* The algorithm for creating a <CODE>Player</CODE> from
* a <code>DataSource</code> is:
* <ol>
* <li>Get the media content-type-name from the source by
* calling <code>getContentType</code>.
* <li>Use the content package-prefix-list to get a list of
* <code>Player</code> classes that support the media content-type name.
* <li>For each <code>Player</code> class in the list:
* <ol>
* <li>Instantiate a new <code>Player</code>.
* <li>Attach the source to the <code>Player</code> by calling
* <code>setSource</code> on the <code>Player</code>.
* <li>If there are no failures, return the new <code>Player</code>;
* otherwise,
* try the next <code>Player</code> in the list.
</ol>
* <li>If no <code>Player</code> is found for this source:
* <ol>
* <li>Use the content package-prefix-list to create a list
* of <code>Player</code> classes that support the
* "unknown" content-type-name.
* <li>For each <code>Player</code> class in the list:
* <ol>
* <li>Instantiate a new <code>Player</code>.
* <li>Attach the source to the <code>Player</code> by
* calling <code>setSource</code>
* on the <code>Player</code>.
* <li>If there are no failures, return the new <code>Player</code>;
* otherwise, try the next <code>Player</code> in the list.
* </ol>
* </ol>
* <li>If no <code>Player</code> can be created,
* a <CODE>NoPlayerException</CODE> is thrown.
* </ol>
* @param DataSource The <CODE>DataSource</CODE> that describes
* the media content.
* @return A new <code>Player</code>.
* @exception NoPlayerException Thrown if a <code>Player</code> can't
* be created.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
public static Player createPlayer(DataSource source)
throws IOException, NoPlayerException {
Player newPlayer;
boolean needPluginPlayer = ((Boolean)Manager.getHint(PLUGIN_PLAYER)).booleanValue();
String contentType = source.getContentType();
// For RTP, the "non-plugin" player
// (com.sun.media.content.rtp.Handler) actually supports plugin.
if (contentType != null &&
(contentType.equalsIgnoreCase("rtp") || contentType.equalsIgnoreCase("rtsp")))
needPluginPlayer = false;
try {
// First try and create one using the source
// as the content identifier ...
if (needPluginPlayer)
contentType = UNKNOWN_CONTENT_NAME;
newPlayer = createPlayerForSource(source, contentType, null);
} catch( NoPlayerException e) {
// ... if that doesn't work use the unknown-content type.
if (needPluginPlayer)
throw e;
newPlayer = createPlayerForSource(source, UNKNOWN_CONTENT_NAME, null);
}
return newPlayer;
}
/**
* Create a Realized <code>Player</code> for the specified media.
* <p>
* This is a blocking method that creates a <code>Player</code>, calls
* realize on it and returns only after the <code>Player</code> has
* been Realized. Realizing a <code>Player</code> could be a time consuming
* operation and one should use caution when using this method as it
* could block the thread for several seconds.
* @param ml The <code>MediaLocator</code> that describes the source of
* the media.
* @return A new <code>Player</code> that is in the <code>Realized</code>
* state.
* @exception NoPlayerException Thrown if a <code>Player</code> can't
* be created.
* @exception IOException Thrown if there was a problem connecting
* with the source.
* @exception CannotRealizeException Thrown if there was a problem
* realizing the Player.
*/
public static Player createRealizedPlayer(URL sourceURL)
throws IOException, NoPlayerException, CannotRealizeException {
Player p = createPlayer(sourceURL);
blockingCall(p, Controller.Realized);
return p;
}
/**
* Create a Realized <code>Player</code> for the specified media.
* <p>
* This is a blocking method that creates a <code>Player</code>, calls
* realize on it and returns only after the <code>Player</code> has
* been Realized. Realizing a <code>Player</code> could be a time consuming
* operation and one should use caution when using this method as it
* could block the thread for several seconds.
* @param ml The <code>MediaLocator</code> that describes the source of
* the media.
* @return A new <code>Player</code> that is in the <code>Realized</code>
* state.
* @exception NoPlayerException Thrown if a <code>Player</code> can't
* be created.
* @exception IOException Thrown if there was a problem connecting
* with the source.
* @exception CannotRealizeException Thrown if there was a problem
* realizing the Player.
*/
public static Player createRealizedPlayer(MediaLocator ml)
throws IOException, NoPlayerException, CannotRealizeException {
Player p = createPlayer(ml);
blockingCall(p, Controller.Realized);
return p;
}
/**
* Create a Realized <code>Player</code> for the specified source.
* <p>
* This is a blocking method that creates a <code>Player</code>, calls
* realize on it and returns only after the <code>Player</code> has
* been Realized. Realizing a <code>Player</code> could be a time consuming
* operation and one should use caution when using this method as it
* could block the thread for several seconds.
* @param source The <code>DataSource</code> that describes the media
* content.
* @return A new <code>Player</code> that is in the <code>Realized</code>
* state.
* @exception NoPlayerException Thrown if a <code>Player</code> can't
* be created.
* @exception IOException Thrown if there was a problem connecting
* with the source.
* @exception CannotRealizeException Thrown if there was a problem
* realizing the Player.
*/
public static Player createRealizedPlayer(DataSource source)
throws IOException, NoPlayerException, CannotRealizeException {
Player p = createPlayer(source);
blockingCall(p, Controller.Realized);
return p;
}
/**
* Create a <code>Processor</code> for the specified media.
* <p>
* The algorithm is similar to that for creating a <code>Player</code>
* from a <code>URL</code>
* @param sourceURL A <CODE>URL</CODE> that describes
* the media content.
* @return A <CODE>Processor</CODE> for the media described by the source.
* @exception NoProcessorException Thrown if no <CODE>Processor</CODE> can
* be found.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
public static Processor createProcessor(URL sourceURL)
throws IOException, NoProcessorException {
return createProcessor(new MediaLocator(sourceURL));
}
/**
* Create a <code>Processor</code> for the specified media.
* <p>
* The algorithm is similar to that for creating a <code>Player</code>
* from a <code>MediaLocator</code>
* @param sourceLocator A <CODE>MediaLocator</CODE> that describes
* the media content.
* @return A <CODE>Processor</CODE> for the media described by the source.
* @exception NoProcessorException Thrown if no <CODE>Processor</CODE> can
* be found.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
public static Processor createProcessor(MediaLocator sourceLocator)
throws IOException, NoProcessorException {
Processor newProcessor = null;
Hashtable sources = new Hashtable(10); // A repository of connected sources.
try {
newProcessor = createProcessorForContent(sourceLocator,
false, sources);
} catch (NoProcessorException e) {
// ... and if that doesn't work, try finding
// a player for the UNKNOWN_CONTENT_NAME.
newProcessor = createProcessorForContent(sourceLocator,
true, sources);
}
// Disconnect the unsed sources
if (sources.size() != 0) {
Enumeration enum = sources.elements();
while (enum.hasMoreElements()) {
DataSource ds = (DataSource)enum.nextElement();
ds.disconnect();
}
}
return newProcessor;
}
/**
* Create a <code>Processor</code> for the <code>DataSource</code>.
* <p>
* The algorithm for creating a <CODE>Processor</CODE> is similar
* to creating a <code>Player</code> from a <code>DataSource</code>.
* @param DataSource The <CODE>DataSource</CODE> that describes
* the media content.
* @return A new <code>Processor</code>.
* @exception NoProcessorException Thrown if a <code>Processor</code> can't
* be created.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
public static Processor createProcessor(DataSource source)
throws IOException, NoProcessorException {
Processor newProcessor;
try {
// First try and create one using the source
// as the content identifier ...
newProcessor = createProcessorForSource(source, source.getContentType(), null);
} catch( NoProcessorException e) {
// ... if that doesn't work use the unknown-content type.
newProcessor = createProcessorForSource(source, UNKNOWN_CONTENT_NAME, null);
}
return newProcessor;
}
/**
* Create a Realized <code>Processor</code> for the specified
* <code>ProcessorModel</code>.
* <P>
* This method accepts a <code>ProcessorModel</code> that describes
* the required input and/or output format of the media data. It is a
* blocking method and returns only after the <code>Processor</code>
* reaches the <code>Realized</code> state.
* @see ProcessorModel
* @param model The <code>ProcessorModel</code> that describes the input and
* output media
* @return A new <code>Processor</code> that is in the <code>Realized</code>
* state.
* @exception NoProcessorException Thrown if a <code>Processor</code> can't
* be created.
* @exception IOException Thrown if there was a problem connecting
* with the source.
* @exception CannotRealizeException Thrown if there was a problem
* realizing the Player.
*/
public static Processor createRealizedProcessor(ProcessorModel model)
throws IOException, NoProcessorException, CannotRealizeException {
DataSource ds = null;
MediaLocator ml = null;
Processor processor = null;
ContentDescriptor [] cds;
ContentDescriptor rcd;
boolean matched = false;
Format [] reqFormats = null;
Format prefFormat;
int reqNumTracks = -1;
int [] procToReqMap;
TrackControl [] procTracks;
int i, j, k;
int nTracksEnabled = 0;
boolean [] enabled;
if (model == null)
throw new NoProcessorException("null ProcessorModel");
// Figure out if we should use a datasource or a media locator as the
// source. DataSource takes precedence over MediaLocator.
ds = model.getInputDataSource();
if (ds != null) {
processor = Manager.createProcessor(ds);
} else if ((ml = model.getInputLocator()) != null) {
processor = Manager.createProcessor(ml);
} else {
// Capture sources...
int nDevices = getNTypesOfCaptureDevices();
Vector dataSourceList = new Vector(1);
reqNumTracks = model.getTrackCount(nDevices);
reqFormats = new Format[reqNumTracks];
for (i = 0; i < reqNumTracks; i++) {
reqFormats[i] = model.getOutputTrackFormat(i);
// Look for a direct match
Vector deviceList = CaptureDeviceManager.getDeviceList(reqFormats[i]);
if (deviceList == null || deviceList.size() == 0) {
// No direct match. Find a similar device and hope to transcode
if (reqFormats[i] instanceof AudioFormat)
deviceList = CaptureDeviceManager.getDeviceList(
new AudioFormat(null));
else if (reqFormats[i] instanceof VideoFormat)
deviceList = CaptureDeviceManager.getDeviceList(
new VideoFormat(null));
}
if (deviceList.size() != 0) {
CaptureDeviceInfo cdi = (CaptureDeviceInfo) deviceList.elementAt(0);
if (cdi != null && cdi.getLocator() != null) {
try {
DataSource crds = Manager.createDataSource(cdi.getLocator());
if (crds instanceof CaptureDevice) {
// Set the format
FormatControl [] fc = ((CaptureDevice)crds).getFormatControls();
if (fc.length > 0) {
Format [] supported = fc[0].getSupportedFormats();
if (supported.length > 0) {
for (int f = 0; f < supported.length; f++) {
if (supported[f].matches(reqFormats[i])) {
Format intersect =
supported[f].intersects(reqFormats[i]);
if (intersect != null) {
if (fc[0].setFormat(intersect) != null)
break;
}
}
}
}
}
}
// ds.connect(); ???
dataSourceList.addElement(crds);
} catch (IOException ioe) {
} catch (NoDataSourceException ndse) {
}
}
}
}
if (dataSourceList.size() == 0) {
throw new NoProcessorException("No suitable capture devices found!");
} else if (dataSourceList.size() > 1) {
// Merge the datasources
DataSource [] dataSourceArray = new DataSource[dataSourceList.size()];
for (k = 0; k < dataSourceList.size(); k++)
dataSourceArray[k] = (DataSource) dataSourceList.elementAt(k);
try {
ds = Manager.createMergingDataSource(dataSourceArray);
} catch (IncompatibleSourceException ise) {
throw new NoProcessorException("Couldn't merge capture devices");
}
} else
ds = (DataSource) dataSourceList.elementAt(0);
processor = Manager.createProcessor(ds);
}
if (processor == null)
throw new NoProcessorException("Couldn't create Processor for source");
// Configure the processor
blockingCall(processor, Processor.Configured);
// Content descriptor stuff
rcd = model.getContentDescriptor();
if (rcd == null) {
processor.setContentDescriptor(null);
} else {
cds = processor.getSupportedContentDescriptors();
if (cds == null || cds.length == 0)
throw new NoProcessorException("Processor doesn't support output");
for (i = 0; i < cds.length; i++) {
if (rcd.matches(cds[i])) {
if (processor.setContentDescriptor(cds[i]) != null) {
matched = true;
break;
}
}
}
if (!matched)
throw new NoProcessorException("Processor doesn't support requested " +
"output ContentDescriptor");
}
procTracks = processor.getTrackControls();
if (procTracks != null && procTracks.length > 0) {
// Format stuff
int nValidTracks = 0;
for (i = 0; i < procTracks.length; i++) {
if (procTracks[i].isEnabled())
nValidTracks++;
}
if (reqNumTracks == -1)
reqNumTracks = model.getTrackCount(nValidTracks);
if (reqNumTracks > 0) {
if (reqFormats == null)
reqFormats = new Format[reqNumTracks];
procToReqMap = new int[reqNumTracks]; // Whats the proc's trackNo for a
// requested track no.
for (i = 0; i < reqNumTracks; i++) {
if (reqFormats[i] == null)
reqFormats[i] = model.getOutputTrackFormat(i);
procToReqMap[i] = -1;
}
enabled = new boolean[procTracks.length];
// First try the default track formats.
for (i = 0; i < procTracks.length; i++) {
enabled[i] = false;
if (!procTracks[i].isEnabled())
continue;
prefFormat = procTracks[i].getFormat();
for (j = 0; j < reqNumTracks; j++) {
if ( procToReqMap[j] == -1 &&
( reqFormats[j] == null ||
prefFormat.matches(reqFormats[j]) ) ) {
if (model.isFormatAcceptable(j, prefFormat)) {
procToReqMap[j] = i;
enabled[i] = true;
nTracksEnabled++;
//procTracks[i].setFormat(prefFormat); // no need to do this
break;
}
}
}
}
for (i = 0; i < procTracks.length && nTracksEnabled < reqNumTracks; i++) {
boolean used = false;
Format [] suppFormats;
// If not enabled by processor, its not to be used
if (!procTracks[i].isEnabled())
continue;
// Check if its already been matched with one of the requested
for (j = 0; j < reqNumTracks; j++)
if (procToReqMap[j] == i)
used = true;
if (used)
continue;
// Get all the supported formats for this track (transcode)
suppFormats = procTracks[i].getSupportedFormats();
if (suppFormats == null || suppFormats.length == 0)
continue;
matched = false;
for (k = 0; k < suppFormats.length && !matched; k++) {
prefFormat = suppFormats[k];
for (j = 0; j < reqNumTracks && !matched; j++) {
//System.err.println("trying " + prefFormat + " && " + reqFormats[j]);
if ( procToReqMap[j] == -1 &&
( reqFormats[j] == null ||
prefFormat.matches(reqFormats[j]) ) ) {
if (model.isFormatAcceptable(j, prefFormat)) {
if (procTracks[i].setFormat(prefFormat) != null) {
procToReqMap[j] = i;
enabled[i] = true;
nTracksEnabled++;
matched = true;
break;
}
}
}
}
}
}
if (nTracksEnabled < reqNumTracks) {
// What should we do if all requested tracks are not satisfied?
// Is failing the right thing to do
throw new CannotRealizeException("Unable to provide all " +
"requested tracks");
}
}
}
blockingCall(processor, Controller.Realized);
return processor;
}
/**
* Create a <code>DataSource</code> for the specified media.
*
* @param sourceURL The <CODE>URL</CODE> that describes the media data.
* @return A new <CODE>DataSource</CODE> for the media.
* @exception NoDataSourceException Thrown if no <code>DataSource</code>
* can be found.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
public static DataSource createDataSource(URL sourceURL)
throws IOException, NoDataSourceException {
return
createDataSource(new MediaLocator(sourceURL));
}
/**
* Create a <code>DataSource</code> for the specified media.
* <p>
*
* Returns a data source for the protocol specified by
* the <CODE>MediaLocator</CODE>. The returned data source
* is <i>connected</i>; <code>DataSource.connect</code> has been
* invoked.
* <p>
*
* The algorithm for creating a <code>DataSource</code> from
* a <code>MediaLocator</code> is:
* <ol>
* <li>Get the protocol from the <code>MediaLocator</code>.
* <li>Use the protocol package-prefix list to get a list of
* <code>DataSource</code> classes that
* support the protocol.
* <li> For each source class in the list:
* <ol>
* <li>Instantiate a new <code>DataSource</code>.
* <li>Call <code>connect</code> to connect the source.
* <li>If there are no errors, return the connected
* source; otherwise, try the next source in the list.
* </ol>
* <li>If no source has been found, obtain a <code>URL</code> from the
* <code>MediaLocator</code> and use it to create
* a <code>URLDataSource</code>
* <li>If no source can be found, a <CODE>NoDataSourceException</CODE>
* is thrown.
* </ol>
*
*
* @param sourceLocator The source protocol for the media data.
* @return A connected <CODE>DataSource</CODE>.
* @exception NoDataSourceException Thrown if no <CODE>DataSource</CODE>
* can be found.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
static public DataSource createDataSource(MediaLocator sourceLocator)
throws IOException, NoDataSourceException {
DataSource source = null;
// For each DataSource that implements the protocol
// that's specified in the source locator ....
Enumeration protoList =
getDataSourceList(sourceLocator.getProtocol()).elements();
while(protoList.hasMoreElements()) {
String protoClassName = (String)protoList.nextElement();
try {
// ... Try and instance a DataSource ....
Class protoClass = getClassForName(protoClassName);
source = (DataSource)protoClass.newInstance();
// ... and get it connected ....
source.setLocator(sourceLocator);
source.connect();
// ... o.k. we've found one, so we're done.
break;
} catch (ClassNotFoundException e) {
// try another data source.
source = null;
} catch (InstantiationException e) {
// try another data source.
source = null;
} catch (IllegalAccessException e) {
// try another data source.
source = null;
} catch (Exception e) {
source = null;
String err = "Error instantiating class: " + protoClassName + " : " + e;
Log.error(e);
throw new NoDataSourceException(err);
} catch (Error e) {
source = null;
String err = "Error instantiating class: " + protoClassName + " : " + e;
Log.error(e);
throw new NoDataSourceException(err);
}
}
// If we haven't found one installed,
// we'll try and create one from a URL/URLDataSource.
// TEMPORARILY COMMENTED OUT URLDataSource: 1/15/99
// if( source == null) {
// try {
// source = new URLDataSource(sourceLocator.getURL());
// source.connect();
// } catch(MalformedURLException me) {
// // Can't get a URL so we're done.
// source = null;
// }
// }
// If we still haven't found one, we're done
// and we don't have a source.
if( source == null) {
throw new NoDataSourceException("Cannot find a DataSource for: " + sourceLocator);
}
Log.comment("DataSource created: " + source + "\n");
return source;
}
/**
* Creates a merged <code>DataSource</code> from an array of sources.
* All sources must be of the same type (i.e <code>PullDataSource</code>,
* <code>PushDataSource</code>, etc.) otherwise an
* IncompatibleSourceException is thrown.
* The returned <code>DataSource</code> is of the same type of
* the given sources. Its content-type is RAW if all sources are of
* type RAW. Otherwise, its content-type is MIXED.
*
* @param sources the <code>DataSources</code> to be merged
* @return a <code>DataSource</code> which contains all the streams
* of the original sources
* @exception IncompatibleSourceException if the sources are not
* of the same type
*/
static public DataSource createMergingDataSource(DataSource[] sources)
throws IncompatibleSourceException {
// check if the sources type matches
if (sources.length == 0)
throw new IncompatibleSourceException("No sources");
if (sources[0] instanceof PullDataSource) {
for (int i = 1; i < sources.length; i ++) {
if (!(sources[i] instanceof PullDataSource))
throw new IncompatibleSourceException("One of the sources isn't matching the others");
}
PullDataSource pds[] = new PullDataSource[sources.length];
for (int i = 0; i < pds.length; i++)
pds[i] = (PullDataSource)sources[i];
return reflectMDS("com.ibm.media.protocol.MergingPullDataSource", pds);
}
if (sources[0] instanceof PushDataSource) {
for (int i = 1; i < sources.length; i ++) {
if (!(sources[i] instanceof PushDataSource))
throw new IncompatibleSourceException("One of the sources isn't matching the others");
}
PushDataSource pds[] = new PushDataSource[sources.length];
for (int i = 0; i < pds.length; i++)
pds[i] = (PushDataSource)sources[i];
return reflectMDS("com.ibm.media.protocol.MergingPushDataSource",pds);
}
if (sources[0] instanceof PullBufferDataSource) {
for (int i = 1; i < sources.length; i ++) {
if (!(sources[i] instanceof PullBufferDataSource))
throw new IncompatibleSourceException("One of the sources isn't matching the others");
}
PullBufferDataSource pds[] = new PullBufferDataSource[sources.length];
for (int i = 0; i < pds.length; i++)
pds[i] = (PullBufferDataSource)sources[i];
return reflectMDS("com.ibm.media.protocol.MergingPullBufferDataSource",pds);
}
if (sources[0] instanceof PushBufferDataSource) {
boolean anyCapture = false;
for (int i = 1; i < sources.length; i ++) {
if (!(sources[i] instanceof PushBufferDataSource))
throw new IncompatibleSourceException("One of the sources isn't matching the others");
if (sources[i] instanceof CaptureDevice)
anyCapture = true;
}
PushBufferDataSource pds[] = new PushBufferDataSource[sources.length];
for (int i = 0; i < pds.length; i++)
pds[i] = (PushBufferDataSource)sources[i];
if (anyCapture)
return reflectMDS("com.ibm.media.protocol.MergingCDPushBDS",pds);
else
return reflectMDS("com.ibm.media.protocol.MergingPushBufferDataSource",pds);
}
return null;
}
/**
* Creates a cloneable <code>DataSource</code>. The returned
* <code>DataSource</code> implements the <code>SourceCloneable</code>
* interface and enables the creation of clones by the createClone method.
* <br>
* If the input <code>DataSource</code> implements
* <code>SourceCloneable</code>, it will be returned right away
* as the result. Otherwise, a "proxy" <code>DataSource</code> is
* created. It implements the <code>SourceCloneable</code> interface and
* can be used to generate other clones.
* <br>
* When <code>createCloneableDataSource</code> is called on a
* <code>DataSource</code>, the returned <code>DataSource</code> should
* be used in place of the original <code>DataSource</code>. Any
* attempt to use the original <code>DataSource</code> may generate
* unpredictable results.
* <br>
* The resulted cloneable <code>DataSource</code> can be used to
* generate clones. The clones generated may or may not has the
* same properties of the original DataSource depending on the
* implementation. Therefore, they should be checked against the
* properties required for the application. If the original
* <code>DataSource</code> is not SourceCloneable and a "proxy"
* <code>DataSource</code> is resulted, the clones generated from
* this "proxy" <code>DataSource</code> is of type
* <code>PushDataSource or PushBufferDataSource</code> depending on
* the type of the original <code>DataSource</code>. In this case,
* each clone pushes data at the same rate that the original
* <code>DataSource</code> is pulled or pushed.
*
* @see SourceCloneable
* @param source the DataSource to be cloned
* @return a cloneable DataSource for the given source
*/
static private DataSource reflectMDS(String cname, Object pds) {
Class cls;
Constructor cc;
Class[] paramTypes = new Class[1];
Object[] arg = new Object[1];
try {
cls = Class.forName(cname);
paramTypes[0] = pds.getClass();
cc = cls.getConstructor(paramTypes);
if ( cname.indexOf("PullDataSource") >= 0 ) {
arg[0] = (PullDataSource[])pds;
} else if (cname.indexOf("PushDataSource") >= 0) {
arg[0] = (PushDataSource[])pds;
} else if (cname.indexOf("PullBufferDataSource") >= 0) {
arg[0] = (PullBufferDataSource[])pds;
} else if (cname.indexOf("PushBufferDataSource") >= 0) {
arg[0] = (PushBufferDataSource[])pds;
} else if ( cname.indexOf("CDPushBDS") >= 0 ) {
arg[0] = (PushBufferDataSource[])pds;
}
return (DataSource)(cc.newInstance(arg));
} catch (Exception ex) {
}
return null;
}
static private DataSource reflectDS(String cname, DataSource source) {
Class cls;
Constructor cc;
Class[] paramTypes = new Class[1];
Object[] arg = new Object[1];
try {
cls = Class.forName(cname);
if ( cname.indexOf("PullDataSource") >= 0) {
paramTypes[0] = PullDataSource.class;
arg[0] = (PullDataSource)source;
} else if ( cname.indexOf("PushDataSource") >= 0) {
paramTypes[0] = PushDataSource.class;
arg[0] = (PushDataSource)source;
} else if ( cname.indexOf("PullBufferDataSource") >= 0) {
paramTypes[0] = PullBufferDataSource.class;
arg[0] = (PullBufferDataSource)source;
} else if ( cname.indexOf("PushBufferDataSource") >= 0 ) {
paramTypes[0] = PushBufferDataSource.class;
arg[0] = (PushBufferDataSource)source;
}
cc = cls.getConstructor(paramTypes);
return (DataSource)(cc.newInstance(arg));
} catch (Exception ex) {
// return null;
}
return null;
}
static public DataSource createCloneableDataSource(DataSource source) {
if (source instanceof SourceCloneable)
return source;
if (source instanceof CaptureDevice) {
// the created clone will support CaptureDevice interface
if (source instanceof javax.media.protocol.PullDataSource)
return reflectDS("com.ibm.media.protocol.CloneableCapturePullDataSource", source);
if (source instanceof javax.media.protocol.PushDataSource)
return reflectDS("com.ibm.media.protocol.CloneableCapturePushDataSource", source);
if (source instanceof javax.media.protocol.PullBufferDataSource)
return reflectDS("com.ibm.media.protocol.CloneableCapturePullBufferDataSource", source);
if (source instanceof javax.media.protocol.PushBufferDataSource)
return reflectDS("com.ibm.media.protocol.CloneableCapturePushBufferDataSource", source);
}
// Otherwise create a regular non-capture DataSource
if (source instanceof javax.media.protocol.PullDataSource)
return reflectDS("com.ibm.media.protocol.CloneablePullDataSource", source);
if (source instanceof javax.media.protocol.PushDataSource)
return reflectDS("com.ibm.media.protocol.CloneablePushDataSource", source);
if (source instanceof javax.media.protocol.PullBufferDataSource)
return reflectDS("com.ibm.media.protocol.CloneablePullBufferDataSource", source);
if (source instanceof javax.media.protocol.PushBufferDataSource)
return reflectDS("com.ibm.media.protocol.CloneablePushBufferDataSource", source);
return null;
}
/**
* Get the time-base object for the system.
* @return The system time base.
*/
public static TimeBase getSystemTimeBase() {
if (sysTimeBase == null) {
sysTimeBase = new SystemTimeBase();
}
return sysTimeBase;
}
/**
* Create a player for the <CODE>MediaLocator</CODE>.
* <p>
* If <CODE>useUnknownContent</CODE> is <CODE>true</CODE>,
* a <CODE>Player</CODE> for
* content-type <CODE>UNKNOWN_CONTENT_NAME</CODE> is created; otherwise,
* the <CODE>DataSource</CODE> determines the content-type with the
* <code>getContentType</code> method.
*
* @param sourceLocator Used to determine the protocol that
* the <CODE>DataSource</CODE> will use.
* @param useKnownContent Used to determine the content type used
* to find a <CODE>Player</CODE>.
* @returns A new <CODE>Player</CODE>.
* @exception NoPlayerException Thrown if no <CODE>Player</CODE> can
* be found.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
static Player createPlayerForContent(MediaLocator sourceLocator,
boolean useUnknownContent, Hashtable sources)
throws IOException, NoPlayerException {
Player newPlayer = null;
boolean sourceUsed[] = new boolean[1];
sourceUsed[0] = false; // A pass-by-referenced boolean to indicate
// if the data source has been used.
// If so, it need to be disconnected.
// For each DataSource that implements the protocol
// that's specified in the source ...
Enumeration protoList =
getDataSourceList(sourceLocator.getProtocol()).elements();
while(protoList.hasMoreElements()) {
String protoClassName = (String)protoList.nextElement();
DataSource source = null; //
try {
// Look into the registry to see if that DataSource
// has already been created.
if ((source = (DataSource)sources.get(protoClassName)) == null) {
// ... Try an instance a DataSource ....
Class protoClass = getClassForName(protoClassName);
source = (DataSource)protoClass.newInstance();
// ... and get it connected ....
source.setLocator(sourceLocator);
source.connect();
} else
sources.remove(protoClassName);
// ... o.k. we've found one, so now try and get
// a Player for it.
try {
if( useUnknownContent) {
// Either use the default content type ...
newPlayer = createPlayerForSource(source,
UNKNOWN_CONTENT_NAME, sourceUsed);
} else {
// ... or let the source specify the content type.
newPlayer =
createPlayerForSource(source,
source.getContentType(), sourceUsed);
}
// If we got one we're done.
break;
} catch (NoPlayerException e) {
// Go try another one.
newPlayer = null;
}
// No luck so try another source.
if (sourceUsed[0])
source.disconnect();
else
sources.put(protoClassName, source);
} catch (ClassNotFoundException e) {
// try another data source.
source = null;
} catch (InstantiationException e) {
// try another one.
source = null;
} catch (IllegalAccessException e) {
// try another one.
source = null;
} catch (Exception e) {
source = null;
String err = "Error instantiating class: " + protoClassName + " : " + e;
Log.error(e);
throw new NoPlayerException(err);
} catch (Error e) {
source = null;
String err = "Error instantiating class: " + protoClassName + " : " + e;
Log.error(e);
throw new NoPlayerException(err);
}
}
// TEMPORARILY COMMENTED OUT URLDataSource: 1/15/99
// If we don't have a Player yet, then try and create a Player
// from the URL data source.
// DataSource source = null;
// sourceUsed[0] = false;
// if( newPlayer == null) {
// try {
// if ((source = (DataSource)sources.get("javax.media.URLDataSource")) == null) {
// source = new URLDataSource(sourceLocator.getURL());
// source.connect();
// } else
// sources.remove("javax.media.URLDataSource");
// // Got the data source so attach it to
// // a player.
// if( useUnknownContent) {
// // Either use the default content type ...
// newPlayer = createPlayerForSource(source,
// UNKNOWN_CONTENT_NAME, sourceUsed);
// } else {
// // ... or let the source specify the content type.
// newPlayer =
// createPlayerForSource(source,
// source.getContentType(), sourceUsed);
// }
// } catch(MalformedURLException me) {
// // Can't get a URL so we're done.
// source = null;
// } finally {
// if (source != null) {
// if (sourceUsed[0])
// source.disconnect();
// else
// sources.put("javax.media.URLDataSource", source);
// }
// }
// }
if( newPlayer == null)
throw new NoPlayerException("Cannot find a Player for :" + sourceLocator);
return newPlayer;
}
/**
* Create a <CODE>Player</CODE> for a particular content type
* using the source.
*
* @param source The source of media for the <CODE>Player</CODE>.
* @param contentTypeName The type of content the <CODE>Player</CODE>
* should handle.
* @return A new <CODE>Player</CODE>.
* @exception NoPlayerException Thrown if no <CODE>Player</CODE> can
* be found for the source and content-type.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
static Player createPlayerForSource(DataSource source,
String contentTypeName, boolean sourceUsed[])
throws IOException, NoPlayerException {
Player newPlayer = null;
if (sourceUsed != null) sourceUsed[0] = true;
// Try every handler we can find for this content type.
Enumeration playerList =
getHandlerClassList(contentTypeName).elements();
MediaHandler mHandler;
DataSource newSource = null;
while(playerList.hasMoreElements()) {
String handlerClassName = (String)playerList.nextElement();
try {
// ... try and instance the handler ...
Class handlerClass = getClassForName(handlerClassName);
mHandler = (MediaHandler)handlerClass.newInstance();
// ... set the DataSource on it ...
mHandler.setSource(source);
// if this is a Player then we're done.
if( mHandler instanceof Player) {
newPlayer = (Player)mHandler;
break;
}
// Otherwise it must be a proxy.
// Get a new data source, and content type ...
MediaProxy mProxy = (MediaProxy)mHandler;
newSource = mProxy.getDataSource();
String newContentType = newSource.getContentType();
// .. recurse to try and create a Player with it.
try{
newPlayer =
createPlayerForSource(newSource,newContentType, null);
}catch (NoPlayerException e){
newPlayer = createPlayerForSource(newSource,
UNKNOWN_CONTENT_NAME,
null);
if (newPlayer != null)
break;
}
} catch (ClassNotFoundException e) {
// Couldn't find the handler so try another.
newPlayer = null;
if (sourceUsed != null) sourceUsed[0] = false;
} catch (InstantiationException e) {
// Can't instance the handler so try another.
newPlayer = null;
if (sourceUsed != null) sourceUsed[0] = false;
} catch (IllegalAccessException e) {
// Can't get at the handler so try another.
newPlayer = null;
if (sourceUsed != null) sourceUsed[0] = false;
} catch (IncompatibleSourceException e) {
// The handler didn't know what to
// do with the DataSource so try another handler.
newPlayer = null;
} catch (NoDataSourceException e) {
// Proxy failed to produce a new data source
// see if there are other proxies out there.
newPlayer = null;
} catch (Exception e) {
newPlayer = null;
String err = "Error instantiating class: " + handlerClassName + " : " + e;
throw new NoPlayerException(err);
} catch (Error e) {
String err = "Error instantiating class: " + handlerClassName + " : " + e;
Log.error(e);
throw new NoPlayerException(err);
}
}
if( newPlayer == null) {
throw new NoPlayerException("Cannot find a Player for: " + source);
}
Log.comment("Player created: " + newPlayer);
Log.comment(" using DataSource: " + source + "\n");
return newPlayer;
}
/**
* Create a player for the <CODE>MediaLocator</CODE>.
* <p>
* If <CODE>useUnknownContent</CODE> is <CODE>true</CODE>,
* a <CODE>Processor</CODE> for
* content-type <CODE>UNKNOWN_CONTENT_NAME</CODE> is created; otherwise,
* the <CODE>DataSource</CODE> determines the content-type with the
* <code>getContentType</code> method.
*
* @param sourceLocator Used to determine the protocol that
* the <CODE>DataSource</CODE> will use.
* @param useKnownContent Used to determine the content type used
* to find a <CODE>Processor</CODE>.
* @returns A new <CODE>Processor</CODE>.
* @exception NoProcessorException Thrown if no <CODE>Processor</CODE> can
* be found.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
static Processor createProcessorForContent(MediaLocator sourceLocator,
boolean useUnknownContent, Hashtable sources)
throws IOException, NoProcessorException {
Processor newProcessor = null;
boolean sourceUsed[] = new boolean[1];
sourceUsed[0] = false; // A pass-by-referenced boolean to indicate
// if the data source has been used.
// If so, it need to be disconnected.
// For each DataSource that implements the protocol
// that's specified in the source ...
Enumeration protoList =
getDataSourceList(sourceLocator.getProtocol()).elements();
while(protoList.hasMoreElements()) {
String protoClassName = (String)protoList.nextElement();
DataSource source = null;
try {
// Look into the registry to see if that DataSource
// has already been created.
if ((source = (DataSource)sources.get(protoClassName)) == null) {
// ... Try an instance a DataSource ....
Class protoClass = getClassForName(protoClassName);
source = (DataSource)protoClass.newInstance();
// ... and get it connected ....
source.setLocator(sourceLocator);
source.connect();
} else
sources.remove(protoClassName);
// ... o.k. we've found one, so now try and get
// a Processor for it.
try {
if( useUnknownContent) {
// Either use the default content type ...
newProcessor = createProcessorForSource(source,
UNKNOWN_CONTENT_NAME, sourceUsed);
} else {
// ... or let the source specify the content type.
newProcessor =
createProcessorForSource(source,
source.getContentType(), sourceUsed);
}
// If we got one we're done.
break;
} catch (NoProcessorException e) {
// Go try another one.
newProcessor = null;
}
// No luck so try another source.
if (sourceUsed[0])
source.disconnect();
else
sources.put(protoClassName, source);
} catch (ClassNotFoundException e) {
// try another data source.
source = null;
} catch (InstantiationException e) {
// try another one.
source = null;
} catch (IllegalAccessException e) {
// try another one.
source = null;
} catch (Exception e) {
String err = "Error instantiating class: " + protoClassName + " : " + e;
Log.error(e);
throw new NoProcessorException(err);
} catch (Error e) {
String err = "Error instantiating class: " + protoClassName + " : " + e;
Log.error(e);
throw new NoProcessorException(err);
}
}
// TEMPORARILY COMMENTED OUT URLDataSource: 1/15/99
// If we don't have a Processor yet, then try and create a Processor
// from the URL data source.
// DataSource source = null;
// sourceUsed[0] = false;
// if( newProcessor == null) {
// try {
// if ((source = (DataSource)sources.get("javax.media.URLDataSource")) == null) {
// source = new URLDataSource(sourceLocator.getURL());
// source.connect();
// } else
// sources.remove("javax.media.URLDataSource");
// // Got the data source so attach it to
// // a player.
// if( useUnknownContent) {
// // Either use the default content type ...
// newProcessor = createProcessorForSource(source,
// UNKNOWN_CONTENT_NAME, sourceUsed);
// } else {
// // ... or let the source specify the content type.
// newProcessor =
// createProcessorForSource(source,
// source.getContentType(), sourceUsed);
// }
// } catch(MalformedURLException me) {
// // Can't get a URL so we're done.
// source = null;
// } finally {
// if (source != null) {
// if (sourceUsed[0])
// source.disconnect();
// else
// sources.put("javax.media.URLDataSource", source);
// }
// }
// }
if( newProcessor == null)
throw new NoProcessorException("Cannot find a Processor for: " + sourceLocator);
return newProcessor;
}
/**
* Create a <CODE>Processor</CODE> for a particular content type
* using the source.
*
* @param source The source of media for the <CODE>Processor</CODE>.
* @param contentTypeName The type of content the <CODE>Processor</CODE>
* should handle.
* @return A new <CODE>Processor</CODE>.
* @exception NoProcessorException Thrown if no <CODE>Processor</CODE> can
* be found for the source and content-type.
* @exception IOException Thrown if there was a problem connecting
* with the source.
*/
static Processor createProcessorForSource(DataSource source,
String contentTypeName, boolean sourceUsed[])
throws IOException, NoProcessorException {
Processor newProcessor = null;
if (sourceUsed != null) sourceUsed[0] = true;
// Try every handler we can find for this content type.
Enumeration playerList =
getProcessorClassList(contentTypeName).elements();
MediaHandler mHandler;
DataSource newSource = null;
while(playerList.hasMoreElements()) {
String handlerClassName = (String)playerList.nextElement();
try {
// ... try and instance the handler ...
Class handlerClass = getClassForName(handlerClassName);
mHandler = (MediaHandler)handlerClass.newInstance();
// ... set the DataSource on it ...
mHandler.setSource(source);
// if this is a Processor then we're done.
if( mHandler instanceof Processor) {
newProcessor = (Processor)mHandler;
break;
}
// Otherwise it must be a proxy.
// Get a new data source, and content type ...
MediaProxy mProxy = (MediaProxy)mHandler;
newSource = mProxy.getDataSource();
String newContentType = newSource.getContentType();
// .. recurse to try and create a Player with it.
try{
newProcessor =
createProcessorForSource(newSource,newContentType, null);
}catch (NoProcessorException e){
newProcessor = createProcessorForSource(newSource,
UNKNOWN_CONTENT_NAME,
null);
if (newProcessor != null)
break;
}
} catch (ClassNotFoundException e) {
// Couldn't find the handler so try another.
newProcessor = null;
if (sourceUsed != null) sourceUsed[0] = false;
} catch (InstantiationException e) {
// Can't instance the handler so try another.
newProcessor = null;
if (sourceUsed != null) sourceUsed[0] = false;
} catch (IllegalAccessException e) {
// Can't get at the handler so try another.
newProcessor = null;
if (sourceUsed != null) sourceUsed[0] = false;
} catch (IncompatibleSourceException e) {
// The handler didn't know what to
// do with the DataSource so try another handler.
newProcessor = null;
} catch (NoDataSourceException e) {
// Proxy failed to produce a new data source
// see if there are other proxies out there.
newProcessor = null;
} catch (Exception e) {
newProcessor = null;
String err = "Error instantiating class: " + handlerClassName + " : " + e;
Log.error(e);
throw new NoProcessorException(err);
} catch (Error e) {
newProcessor = null;
String err = "Error instantiating class: " + handlerClassName + " : " + e;
Log.error(e);
throw new NoProcessorException(err);
}
}
if( newProcessor == null) {
throw new NoProcessorException("Cannot find a Processor for: " + source);
}
Log.comment("Processor created: " + newProcessor);
Log.comment(" using DataSource: " + source + "\n");
return newProcessor;
}
/**
* Create a <code>DataSink</code> for the specified input
* Datasource and destination Medialocator.
* <p>
*
* The algorithm for creating a <CODE>DataSink</CODE> from
* a <code>MediaLocator and datasource </code> is:
* <ol>
* <li>Get the protocol from the <code>MediaLocator</code>.
* <li>Get a list of <code>MediaHandler</code> classes within the
* datasink package that support the
* protocol, using the content package-prefix-list.
* i.e. search for content-prefix.media.datasink.protocol.Handler
* <li>For each <code>MediaHandler</code> class in the list:
* <ol>
* <li>Instantiate a new <code>MediaHandler</code>.
* <li>If the <code>MediaHandler</code> is a
* <code>DataSink</code>,
* Attach the source to the <code>MediaHandler</code> by calling
* <code>MediaHandler.setSource</code>
* <li>If there are no failures, return this
* <code>DataSink</code>;otherwise try the next
* <code>MediaHandler</code> in the list.
*
* <li>If the <code>MediaHandler</code> is a <code>DataSinkProxy</code>,
* obtain the content type of the proxy using the getContentType() method.
* Now obtain a list of <code>MediaHandlers</code> that support
* the protocol of the Medialocator and the content type returned by
* the proxy <BR>
* i.e. look for content-prefix.media.datasink.protocol.content-type.Handler
* <li>If a <code>MediaHandler</code> is found and it is a
* DataSink, attach the datasource
* to it by calling <code>MediaHandler.setSource</code>.<BR>
* <li>Return the <code>DataSink</code> if there are no errors.
*
* <li>If no <code>MediaHandler</code> is found, or if there are
* any errors,try the next <code>MediaHandler</code in the list.
* </ol>
* <li>If no <code>DataSink</code> is found after trying all of the handlers,
* a <CODE>NoDataSinkException</CODE> is thrown.
* </ol>
* </ol>
* @param datasource The input <CODE>DataSource</CODE> to the DataSink
* @param destLocator A <CODE>MediaLocator</CODE> that describes
* the destination of the media to be handled by the datasink
*
* @return A <CODE>DataSink</CODE> for the media described by the
* destLocator and that supports the datasource .
* @exception NoDataSinkException Thrown if no <CODE>DataSink</CODE> can
* be found.
* @see NoDataSinkException
*
*/
static public DataSink createDataSink(DataSource datasource,
MediaLocator destLocator)
throws NoDataSinkException {
String handlerName = "media.datasink." + destLocator.getProtocol() +
".Handler";
Vector classList = buildClassList(getContentPrefixList(), handlerName);
Enumeration handlerList = classList.elements();
DataSink dataSink = null;
boolean done = false;
while (!done && handlerList.hasMoreElements()) {
String handlerClassName = (String)handlerList.nextElement();
try {
// ... try and instance the handler ...
Class handlerClass = getClassForName(handlerClassName);
Object object = handlerClass.newInstance();
// ... set the DataSource on it ...
if (object instanceof DataSink) {
dataSink = (DataSink) object;
dataSink.setSource(datasource);
dataSink.setOutputLocator(destLocator);
done = true;
break; // we are done
}
// Otherwise it must be a DataSinkProxy
// Get a new data source, and content type ...
DataSinkProxy dsProxy = (DataSinkProxy) object;
String contentType = dsProxy.getContentType(destLocator);
// .. recurse to try and create a Player with it.
handlerName = "media.datasink." + destLocator.getProtocol() +
"." +
contentType +
".Handler";
Vector dataSinkList = buildClassList(getContentPrefixList(), handlerName);
Enumeration elements = dataSinkList.elements();
while (elements.hasMoreElements()) {
String dsClassName = (String) elements.nextElement();
try {
dataSink = (DataSink) getClassForName(dsClassName).newInstance();
// ... set the DataSource on it ...
dataSink.setSource(datasource);
dataSink.setOutputLocator(destLocator);
done = true;
break; // we are done
} catch (Exception e) {
dataSink = null;
}
}
} catch (Exception e) {
dataSink = null;
} catch (Error e) {
dataSink = null;
}
}
if ( dataSink == null )
throw new NoDataSinkException("Cannot find a DataSink for: " + datasource);
Log.comment("DataSink created: " + dataSink);
Log.comment(" using DataSource: " + datasource + "\n");
return dataSink;
}
/**
* Retrieve the directory that's used for playback caching.
* @return the directory that's used for playback caching,
* null if the directory is not specified.
*/
public static String getCacheDirectory() {
String cacheDir;
Object cdir = com.sun.media.util.Registry.get("secure.cacheDir");
if ( (cdir != null) && (cdir instanceof String) ) {
cacheDir = (String) cdir;;
if (cacheDir.indexOf(fileSeparator) == -1) {
if (fileSeparator.equals("/")) {
cacheDir = "/tmp";
} else if (fileSeparator.equals("\\")) {
cacheDir = "C:" + fileSeparator + "temp";
} else {
cacheDir = null;
}
}
return cacheDir;
}
if (fileSeparator.equals("/")) {
cacheDir = "/tmp";
} else if (fileSeparator.equals("\\")) {
cacheDir = "C:" + fileSeparator + "temp";
} else {
cacheDir = null;
}
return cacheDir;
}
/**
* Specify a hint for the <code>Manager</code> to use.
* @param hint The name of the hint to be set.
* @param value The value the hint is to be set.
* @see #MAX_SECURITY
* @see #CACHING
* @see #LIGHTWEIGHT_RENDERER
* @see #PLUGIN_PLAYER
*/
public static void setHint(int hint, Object value) {
if ( (value != null) &&
(hint >= 1) &&
(hint <= numberOfHints) ) { // if test not really necessary
hintTable.put(new Integer(hint), value);
}
}
/**
* Retrieve the value of a hint set.
* @param hint The name of the hint.
* @return The value of the hint.
* @see #MAX_SECURITY
* @see #CACHING
* @see #LIGHTWEIGHT_RENDERER
* @see #PLUGIN_PLAYER
*/
public static Object getHint(int hint) {
if ( (hint >= 1) && (hint <= numberOfHints) ) {
return hintTable.get(new Integer(hint));
} else {
return null;
}
}
static final int DONE = 0;
static final int SUCCESS = 1;
/**
* Realize a player or processor. It blocks until the player is
* realized or if the realize fails.
* Throws a CannotRealizeException if the realize fails.
*/
static private void blockingCall(Player p, int state) throws CannotRealizeException {
// Use this sort of as a pass-by-reference variable.
boolean sync[] = new boolean[2];
ControllerListener cl;
sync[DONE] = false;
sync[SUCCESS] = false;
cl = new MCA(sync, state);
p.addControllerListener(cl);
if (state == Controller.Realized)
p.realize();
else if (state == Processor.Configured)
((Processor)p).configure();
// Wait for notification from the controller.
synchronized (sync) {
while (!sync[DONE]) {
try {
sync.wait();
} catch (InterruptedException e) {}
}
}
p.removeControllerListener(cl);
if (!sync[SUCCESS])
throw new CannotRealizeException();
}
/**
* Build a list of <CODE>DataSource</CODE> class names from the
* protocol prefix-list and a protocol name.
* <p>
* The first name in the list will always be:
* <blockquote><pre>
* media.protocol.<protocol>.DataSource
* </pre></blockquote>
* <p>
*
* Each additional name looks like:
* <blockquote><pre>
* <protocol-prefix>.media.protocol.<protocol>.DataSource
* </pre></blockquote>
* for every <CODE><protocol-prefix></CODE> in the
* protocol-prefix-list.
*
* @param protocol The name of the protocol the source must
* support.
* @return A vector of strings, where each string is
* a <CODE>Player</CODE> class-name.
*/
static public Vector getDataSourceList(String protocolName) {
// The first element is the name of the protocol handler ...
String sourceName =
"media.protocol." + protocolName + ".DataSource";
return buildClassList(getProtocolPrefixList(), sourceName);
}
/**
* Build a list of Player <CODE>Handler</CODE> classes from the
* content-prefix-list and a content name.
* <p>
* The first name in the list will always be:
* <blockquote><pre>
* media.content.<contentType>.Handler
* </pre></blockquote>
* <p>
*
* Each additional name looks like:
* <blockquote><pre>
* <content-prefix>.media.content.<contentName>.Player
* </pre></blockquote>
* for every <CODE><content-prefix></CODE> in the
* content-prefix-list.
*
* @param contentName The content type to use in the class name.
* @return A vector of strings where each one is a <CODE>Player</CODE>
* class-name.
*/
static public Vector getHandlerClassList(String contentName) {
// players are found by content type ....
String handlerName = "media.content." +
ContentDescriptor.mimeTypeToPackageName(contentName) +
".Handler";
// ... build a list of classes using the content-prefix-list.
return buildClassList(getContentPrefixList(), handlerName);
}
/**
* Build a list of Processor <CODE>Handler</CODE> classes from the
* content-prefix-list and a content name.
* <p>
* The first name in the list will always be:
* <blockquote><pre>
* media.processor.<contentType>.Handler
* </pre></blockquote>
* <p>
*
* Each additional name looks like:
* <blockquote><pre>
* <content-prefix>.media.processor.<contentName>.Processor
* </pre></blockquote>
* for every <CODE><content-prefix></CODE> in the
* content-prefix-list.
*
* @param contentName The content type to use in the class name.
* @return A vector of strings where each one is a <CODE>Processor</CODE>
* class-name.
*/
static public Vector getProcessorClassList(String contentName) {
// players are found by content type ....
String handlerName = "media.processor." +
ContentDescriptor.mimeTypeToPackageName(contentName) +
".Handler";
// ... build a list of classes using the content-prefix-list.
return buildClassList(getContentPrefixList(), handlerName);
}
/**
* Build a list of complete class names.
*<p>
*
* For each element of the prefix-list
* the following element is added to the list:
* <blockquote><pre>
* <prefix>.<name>
* </pre></blockquote>
* These are added to the list in the same order as the prefixes appear
* in the prefix-list.
* </ol>
*
* @param prefixList The list of prefixes to prepend to the class name.
* @param name The name of the class to build the list for.
* @return A vector of class name strings.
*/
static Vector buildClassList(Vector prefixList, String name) {
// New list which has the name as the first element ...
Vector classList = new Vector();
// Try and instance one directly from the classpath
// if it's there.
// $jdr: This has been objected to as confusing,
// the argument for it's inclusion is that it
// gives the user (via the classpath) a way
// of modifying the search list at run time
// for all applications.
classList.addElement(name);
// ... for each prefix append the name and put it
// in the class list ...
Enumeration prefix = prefixList.elements();
while( prefix.hasMoreElements()) {
String prefixName = (String)prefix.nextElement();
classList.addElement(prefixName + "." + name);
}
// ... done
return classList;
}
static Vector getContentPrefixList() {
return (Vector)PackageManager.getContentPrefixList().clone();
}
static Vector getProtocolPrefixList() {
return (Vector) PackageManager.getProtocolPrefixList().clone();
}
private static int getNTypesOfCaptureDevices() {
int nDevices = 0;
Vector audioDevs = CaptureDeviceManager.getDeviceList(new AudioFormat(null));
Vector videoDevs = CaptureDeviceManager.getDeviceList(new VideoFormat(null));
if (audioDevs != null && audioDevs.size() > 0)
nDevices++;
if (videoDevs != null && videoDevs.size() > 0)
nDevices++;
return nDevices;
}
private static boolean checkIfJDK12() {
if (jdkInit)
return (forName3ArgsM != null);
jdkInit = true;
try {
forName3ArgsM = Class.class.getMethod("forName",
new Class[] {
String.class, boolean.class, ClassLoader.class
});
getSystemClassLoaderM = ClassLoader.class.getMethod("getSystemClassLoader", null);
// TODO: may need to invoke RuntimePermission("getClassLoader") privilege
systemClassLoader = (ClassLoader) getSystemClassLoaderM.invoke(ClassLoader.class, null);
getContextClassLoaderM = Thread.class.getMethod("getContextClassLoader", null);
return true;
} catch (Throwable t) {
forName3ArgsM = null;
return false;
}
}
// This is a Package private class
static Class getClassForName(String className)
throws ClassNotFoundException {
/**
* Note: if we don't want this functionality
* just replace it with Class.forName(className)
*/
try {
return Class.forName(className);
} catch (Exception e) {
if (!checkIfJDK12()) {
throw new ClassNotFoundException(e.getMessage());
}
} catch (Error e) {
if (!checkIfJDK12()) {
throw e;
}
}
/**
* In jdk1.2 application, when you have jmf.jar in the ext directory and
* you want to access a class that is not in jmf.jar but is in the CLASSPATH,
* you have to load it using the the system class loader.
*/
try {
return (Class) forName3ArgsM.invoke(Class.class, new Object[] {
className, new Boolean(true), systemClassLoader});
} catch (Throwable e) {
}
/**
* In jdk1.2 applet, when you have jmf.jar in the ext directory and
* you want to access a class that is not in jmf.jar but applet codebase,
* you have to load it using the the context class loader.
*/
try {
// TODO: may need to invoke RuntimePermission("getClassLoader") privilege
ClassLoader contextClassLoader =
(ClassLoader) getContextClassLoaderM.invoke(Thread.currentThread(), null);
return (Class) forName3ArgsM.invoke(Class.class, new Object[] {
className, new Boolean(true), contextClassLoader});
} catch (Exception e) {
throw new ClassNotFoundException(e.getMessage());
} catch (Error e) {
throw e;
}
}
}
/**
* A utility class as a ControllerLister to implement the blocking realize.
*/
class MCA extends ControllerAdapter {
// Pass-by-reference for this class.
boolean sync[];
int state;
public MCA(boolean sync[], int state) {
this.sync = sync;
this.state = state;
}
private void succeed() {
synchronized (sync) {
sync[Manager.DONE] = true;
sync[Manager.SUCCESS] = true;
sync.notify();
}
}
private void fail() {
synchronized (sync) {
sync[Manager.DONE] = true;
sync[Manager.SUCCESS] = false;
sync.notify();
}
}
public void configureComplete(ConfigureCompleteEvent evt) {
if (state == Processor.Configured)
succeed();
}
public void realizeComplete(RealizeCompleteEvent evt) {
if (state == Controller.Realized)
succeed();
}
public void controllerError(ControllerErrorEvent evt) {
fail();
}
public void deallocate(DeallocateEvent evt) {
fail();
}
public void controllerClosed(ControllerClosedEvent evt) {
fail();
}
}
|