/*
Java Media APIs: Cross-Platform Imaging, Media and Visualization
Alejandro Terrazas
Sams, Published November 2002,
ISBN 0672320940
*/
import javax.media.*;
import javax.media.control.*;
import javax.media.format.*;
/*******************************************************************************
* A Class to determine statistics about the tracks that compose a media object.
* Given the name (URL/location) of media a Processor is constructed and brought
* to the Configured state. At that stage its TrackControls are obtained as a
* means of discovering the Formats of the individual tracks.
*
* Because reaching Configured can take time, the MediaStatistics object keeps
* track of its own state and provides methods for determining that state. Only
* when it reaches the KNOWN state can statistics be obtained. Similarly there
* are 2 constructors: one creating a Processor and starting it toward
* Configured but returning immediately. The other is a blocking constructor, it
* won't return until the Processor reaches Configured or the specified time-out
* expires. This has the advantage that the object can be used immediately
* (rather than polling it to determine when it enters the KNOWN state.
*
* The chief information gathering method is getReport() which returns a String
* reporting on the Format of all tracks of the media. Alternatively the Format
* of individual tracks can also be ascertained.
*
* @author Spike Barlow
******************************************************************************/
public class MediaStatistics implements ControllerListener {
/** State: Yet to create the Processor. */
public static final int NOT_CREATED = 0;
/** State: Unable to create the Processor. */
public static final int FAILED = -1;
/** State: Processor is Configuring. */
public static final int CONFIGURING = 1;
/** State: Details of media are Known. */
public static final int KNOWN = 2;
/** Number of tracks is Unknown. */
public static final int UNKNOWN = Integer.MIN_VALUE;
/**
* Period in milliseconds to sleep for before rechecking if reached KNOWN
* state.
*/
protected static final int WAIT_INTERVAL = 20;
/** Number of tracks possessed by the media. */
protected int numTracks = UNKNOWN;
/** Formats of the individual tracks. */
protected Format[] trackFormats;
/** Processor needed to ascertain track information. */
protected Processor processor;
/**
* State that the object is currently in. A reflection of the state the
* Processor is in.
*/
protected int state = NOT_CREATED;
/** The name of the media on which stats are being compiled. */
protected String nameOfMedia;
/***************************************************************************
* Construct a MediaStatistics object for the media with the passed name.
* This is a blocking constructor. It returns only when it is possible to
* obtain the track statistics or when the specified time-out period (in
* milliseconds) has transpired.
**************************************************************************/
MediaStatistics(String mediaName, int timeOutInMilliseconds) {
nameOfMedia = mediaName;
// Construct the Processor
try {
MediaLocator locator = new MediaLocator(mediaName);
processor = Manager.createProcessor(locator);
}
// Any exception is a failure.
catch (Exception e) {
state = FAILED;
return;
}
// Listen to and start configuration of the Processor.
processor.addControllerListener(this);
state = CONFIGURING;
processor.configure();
//////////////////////////////////////////////////////////
// Wait till the Processor reaches configured (the object
// reaches KNOWN) or the specified time-out interval has
// transpired, by looping, sleeping,and rechecking.
//////////////////////////////////////////////////////////
if (timeOutInMilliseconds > 0) {
int waitTime = 0;
while (waitTime < timeOutInMilliseconds && !isKnown()) {
try {
Thread.sleep(WAIT_INTERVAL);
} catch (InterruptedException ie) {
}
waitTime += WAIT_INTERVAL;
}
}
}
/***************************************************************************
* Construct a MediaStatistics object for the media with the passed name.
* This is not a blocking constructor: it returns immediately. Thus calling
* getReport() immediately may result in "Still parsing media" report. The
* isKnown() method should be used to check for this condition.
**************************************************************************/
MediaStatistics(String mediaName) {
this(mediaName, -1);
}
/***************************************************************************
* Respond to events from the Porcessor. In particular the ConfigureComplete
* event is the only one of interest. In this case obtain the TrackControls
* anduse these to obtain the Formats of each track. Also modify the state
* and close down the Processor (free up its resources).
**************************************************************************/
public synchronized void controllerUpdate(ControllerEvent e) {
if (e instanceof ConfigureCompleteEvent) {
TrackControl[] controls = processor.getTrackControls();
// As long as there are TrackControls, get each track's format.
if (controls.length != 0) {
numTracks = controls.length;
trackFormats = new Format[controls.length];
for (int i = 0; i < controls.length; i++) {
trackFormats[i] = controls[i].getFormat();
}
state = KNOWN;
} else {
state = FAILED;
}
// Close down the Processor.
processor.removeControllerListener(this);
processor.close();
processor = null;
}
}
/***************************************************************************
* Determine what state the object is in. Returns one of the class constants
* such as KNOWN, FAILED or CONFIGURING.
**************************************************************************/
public int getState() {
return state;
}
/***************************************************************************
* Determine the number of tracks possessed by the media. If that is
* unknown, either due to the processor creation failing or because the
* processor is not yet Configured then the class constant UNKNOWN is
* returned.
**************************************************************************/
public int getNumTracks() {
return numTracks;
}
/***************************************************************************
* Obtain the Format for the specified track number. If the track doesn't
* exist, or it has yet to be determined how many tracks the media
* possesses, null is returned.
**************************************************************************/
public Format getTrackFormat(int track) {
if (track < 0 || track >= numTracks)
return null;
return trackFormats[track];
}
/***************************************************************************
* Is the object in the KNOWN state? The KNOWN state reflects the fact that
* information is known about the number and Format of the tracks. The
* method can be used to ascertain whether a report is available
* (meaningful) or not.
**************************************************************************/
public boolean isKnown() {
return state == KNOWN;
}
/***************************************************************************
* Returns true if the specified track number is an audio track. If the
* track doesn't exist, the number of tracks is yet unknown, or it isn't
* audio then false is returned.
**************************************************************************/
public boolean isAudioTrack(int track) {
if (track < 0 || track >= numTracks)
return false;
return trackFormats[track] instanceof AudioFormat;
}
/***************************************************************************
* Returns true if the specified track number is a video track. If the track
* doesn't exist, the number of tracks is yet unknown, or it isn't video
* then false is returned.
**************************************************************************/
public boolean isVideoTrack(int track) {
if (track < 0 || track >= numTracks)
return false;
return trackFormats[track] instanceof VideoFormat;
}
/***************************************************************************
* Returns a report, as a String, detailing thenumber and format of the
* individual tracks that compose the media that this object obtained
* statistics for. If the object is not in the KNOWN state then the report
* is a simple String, indicating this.
**************************************************************************/
public String getReport() {
String mess;
if (state == FAILED)
return "Unable to Handle Media " + nameOfMedia;
else if (state == CONFIGURING)
return "Still Parsing Media " + nameOfMedia;
else if (state == KNOWN) {
if (numTracks == 1)
mess = nameOfMedia + ": 1 Track\n";
else
mess = nameOfMedia + ": " + numTracks + " Tracks\n";
for (int i = 0; i < numTracks; i++) {
if (trackFormats[i] instanceof AudioFormat)
mess += "\t" + (i + 1) + " [Audio]: ";
else if (trackFormats[i] instanceof VideoFormat)
mess += "\t" + (i + 1) + " [Video]: ";
else
mess += "\t" + (i + 1) + " [Unknown]: ";
mess += trackFormats[i].toString() + "\n";
}
return mess;
} else
return "Unknown State in Processing " + nameOfMedia;
}
/***************************************************************************
* Simple main method to exercise the class. Takes command line arguments
* and constructs MediaStatistics objects for them, before generating a
* report on them.
**************************************************************************/
public static void main(String[] args) {
MediaStatistics[] stats = new MediaStatistics[args.length];
for (int i = 0; i < args.length; i++) {
stats[i] = new MediaStatistics(args[i], 200);
System.out.println(stats[i].getReport());
stats[i] = null;
}
System.exit(0);
}
}
|