Statistics about the tracks that compose a media object
/* 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); } }