/*
Java Media APIs: Cross-Platform Imaging, Media and Visualization
Alejandro Terrazas
Sams, Published November 2002,
ISBN 0672320940
*/
import javax.media.*;
import javax.media.datasink.*;
import javax.media.protocol.*;
/*******************************************************************************
* Transfer media from one location to another carrying out the specified
* transcoding (track formats and content type) at the same time.
* <p>
* Users specify a source and destination location, the Formats (to be realised)
* of the individual tracks, and a ContentDescriptor (content type) for output.
* <p>
* A Processor is created to perform and transcoding and its output DataSource
* is employed to construct a DataSink in order to complete the transfer.
* <p>
* The most important method of the class is transfer() as this opens and starts
* the DataSink. The constructor builds both the Processor (which is starts) and
* the DataSink.
* <p>
* The object keeps track of its own state, which can be queried with the
* getState() method. Defined constants are FAILED, TRANSLATING, TRANSFERRING,
* and FINISHED. The process is asychronous: transcoding largish movies can take
* a long time. The calling code should make allowances for that.
******************************************************************************/
public class Location2Location implements ControllerListener {
/** Output of the Processor: the transcoded media. */
protected DataSource source;
/** Sink used to "write" out the transcoded media. */
protected DataSink sink;
/** Processor used to transcode the media. */
protected Processor processor;
/**
* Model used in constructing the processor, and which specifies track
* formats and output content type
*/
protected ProcessorModel model;
/** State the object is in. */
protected int state;
/** Location that the media will be "written" to. */
protected MediaLocator sinkLocation;
/** The rate of translation. */
protected float translationRate;
/** Process has failed. */
public static final int FAILED = 0;
/**
* Processor is working but not finished. DataSink is yet to start.
*/
public static final int TRANSLATING = 1;
/** DataSink has started but not finished. */
public static final int TRANSFERRING = 3;
/** Transcoding and transfer is complete. */
public static final int FINISHED = 4;
/** String names for each of the states. More user friendly */
private static final String[] STATE_NAMES = { "Failed", "Translating",
"<UNUSED>", "Transferring", "Finished" };
/**
* Period (in milliseconds) between checks for the blocking transfer method.
*/
public static final int WAIT_PERIOD = 50;
/**
* Wait an "indefinite" period of time for the transfer method to complete.
* i.e., pass to transfer() if the user wishes to block till the process is
* complete, regardless of how long it will take.
*/
public static final int INDEFINITE = Integer.MAX_VALUE;
/***************************************************************************
* Construct a transfer/transcode object that transfers media from
* sourceLocation to destinationLocation, transcoding the tracks as
* specified by the outputFormats. The output media is to have a content
* type of outputContainer and the process should (if possible) run at the
* passed rate.
**************************************************************************/
Location2Location(MediaLocator sourceLocation,
MediaLocator destinationLocation, Format[] outputFormats,
ContentDescriptor outputContainer, double rate) {
//////////////////////////////////////////////
// Construct the processor for the transcoding
//////////////////////////////////////////////
state = TRANSLATING;
sinkLocation = destinationLocation;
try {
if (sourceLocation == null)
model = new ProcessorModel(outputFormats, outputContainer);
else
model = new ProcessorModel(sourceLocation, outputFormats,
outputContainer);
processor = Manager.createRealizedProcessor(model);
} catch (Exception e) {
state = FAILED;
return;
}
translationRate = processor.setRate((float) Math.abs(rate));
processor.addControllerListener(this);
////////////////////////////////////////////////////////////
// Construct the DataSink and employ an anonymous class as
// a DataSink listener in order that the end of transfer
// (completion of task) can be detected.
///////////////////////////////////////////////////////////
source = processor.getDataOutput();
try {
sink = Manager.createDataSink(source, sinkLocation);
} catch (Exception sinkException) {
state = FAILED;
processor.removeControllerListener(this);
processor.close();
processor = null;
return;
}
sink.addDataSinkListener(new DataSinkListener() {
public void dataSinkUpdate(DataSinkEvent e) {
if (e instanceof EndOfStreamEvent) {
sink.close();
source.disconnect();
if (state != FAILED)
state = FINISHED;
} else if (e instanceof DataSinkErrorEvent) {
if (sink != null)
sink.close();
if (source != null)
source.disconnect();
state = FAILED;
}
}
});
// Start the transcoding
processor.start();
}
/***************************************************************************
* Alternate constructor: source and destination specified as Strings, and
* no rate provided (hence rate of 1.0)
**************************************************************************/
Location2Location(String sourceName, String destinationName,
Format[] outputFormats, ContentDescriptor outputContainer) {
this(new MediaLocator(sourceName), new MediaLocator(destinationName),
outputFormats, outputContainer);
}
/***************************************************************************
* Alternate constructor: No rate specified therefore rate of 1.0
**************************************************************************/
Location2Location(MediaLocator sourceLocation,
MediaLocator destinationLocation, Format[] outputFormats,
ContentDescriptor outputContainer) {
this(sourceLocation, destinationLocation, outputFormats,
outputContainer, 1.0f);
}
/***************************************************************************
* Alternate constructor: source and destination specified as Strings.
**************************************************************************/
Location2Location(String sourceName, String destinationName,
Format[] outputFormats, ContentDescriptor outputContainer,
double rate) {
this(new MediaLocator(sourceName), new MediaLocator(destinationName),
outputFormats, outputContainer, rate);
}
/***************************************************************************
* Respond to events from the Processor performing the transcoding. If its
* task is completed (end of media) close it down. If there is an error
* close it down and mark the process as FAILED.
**************************************************************************/
public synchronized void controllerUpdate(ControllerEvent e) {
if (state == FAILED)
return;
// Transcoding complete.
if (e instanceof StopEvent) {
processor.removeControllerListener(this);
processor.close();
if (state == TRANSLATING)
state = TRANSFERRING;
}
// Transcoding failed.
else if (e instanceof ControllerErrorEvent) {
processor.removeControllerListener(this);
processor.close();
state = FAILED;
}
}
/***************************************************************************
* Initiate the transfer through a DataSink to the destination and wait
* (block) until the process is complete (or failed) or the supplied number
* of milliseconds timeout has passed. The method returns the total amount
* of time it blocked.
**************************************************************************/
public int transfer(int timeOut) {
// Can't initiate: Processor already failed to transcode
////////////////////////////////////////////////////////
if (state == FAILED)
return -1;
// Start the DataSink
//////////////////////
try {
sink.open();
sink.start();
} catch (Exception e) {
state = FAILED;
return -1;
}
if (state == TRANSLATING)
state = TRANSFERRING;
if (timeOut <= 0)
return timeOut;
// Wait till the process is complete, failed, or the
// prescribed time has passed.
/////////////////////////////////////////////////////
int waited = 0;
while (state != FAILED && state != FINISHED && waited < timeOut) {
try {
Thread.sleep(WAIT_PERIOD);
} catch (InterruptedException ie) {
}
waited += WAIT_PERIOD;
}
return waited;
}
/***************************************************************************
* Initiate the transfer through a DataSink to the destination but return
* immediately to the caller.
**************************************************************************/
public void transfer() {
transfer(-1);
}
/***************************************************************************
* Determine the object's current state. Returns one of the class constants.
**************************************************************************/
public int getState() {
return state;
}
/***************************************************************************
* Returns the object's state as a String. A more user friendly version of
* getState().
**************************************************************************/
public String getStateName() {
return STATE_NAMES[state];
}
/***************************************************************************
* Obtain the rate being used for the process. This is often 1, despite what
* the user may have supplied as Clocks (hence Processors) don't have to
* support any other rate than 1 (and will default to that).
**************************************************************************/
public float getRate() {
return translationRate;
}
/***************************************************************************
* Set the time at which media processing will stop. Specification is in
* media time. This means only the first "when" amount of the media will be
* transferred.
**************************************************************************/
public void setStopTime(Time when) {
if (processor != null)
processor.setStopTime(when);
}
/***************************************************************************
* Stop the processing and hence transfer. This gives user control over the
* duration of a transfer. It could be started with the transfer() call and
* after a specified period stop() could be called.
**************************************************************************/
public void stop() {
if (processor != null)
processor.stop();
}
}
|