836604a97a
and fixed aspects of QD's QT for Java component. At the moment there are two classes, SmartQT4JPlayer and SmartJMFPlayer, that both extend SmartMoviePanel, an abstract class extending Panel. If it would be better to make SmartMoviePanel an interface that would only require minor changes. You can switch back and forth between JMF and QT4J from the menu bar, or you can type: java -Dthdl.media.player=qt4j/jmf -jar QuillDriver.jar Note that the QT4J module crashes sometimes before it can open the video file. I'm not sure why.
462 lines
11 KiB
Java
462 lines
11 KiB
Java
package org.thdl.quilldriver;
|
|
|
|
import java.awt.*;
|
|
import java.net.*;
|
|
import quicktime.*;
|
|
import quicktime.app.*;
|
|
import quicktime.app.display.*;
|
|
import quicktime.app.players.*;
|
|
import quicktime.app.image.*; //required for the QT4JAVA test
|
|
import quicktime.std.*;
|
|
import quicktime.std.movies.*;
|
|
import quicktime.std.movies.media.DataRef;
|
|
import quicktime.io.*;
|
|
import quicktime.app.time.*;
|
|
import quicktime.std.clocks.*;
|
|
|
|
public class SmartQT4JPlayer extends SmartMoviePanel
|
|
{
|
|
//attributes
|
|
private QTCanvas canvas; //this hands over screen real estate to
|
|
//native graphics that quicktime can control
|
|
|
|
private QTPlayer player; //this is the the client for the canvas
|
|
//In my app it will get set to a
|
|
// quicktime.app.player object
|
|
|
|
private Movie movie; //this is the content used in
|
|
//QTDrawable player's quicktime.app.player constructor
|
|
|
|
private MovieController controller;
|
|
|
|
private TimeBaseRateCallBack theMoviesRateCallback;
|
|
private TimeBaseExtremesCallBack theMoviesExtremeCallback;
|
|
private TimeBaseTimeJumpCallBack theMoviesTimeJumpCallback;
|
|
private TimeBaseTimeCallBack theMoviesTimeCallback;
|
|
private TimeBaseTimeCallBackStopper theStopper;
|
|
|
|
//accessors
|
|
public void setPlayer(QTPlayer player)
|
|
{
|
|
this.player = player;
|
|
}
|
|
public QTPlayer getPlayer()
|
|
{
|
|
return player;
|
|
}
|
|
|
|
public void setMovie(Movie movie)
|
|
{
|
|
this.movie = movie;
|
|
}
|
|
public void setMovie(URL movieURL)
|
|
{
|
|
try
|
|
{
|
|
DataRef movieDataRef = new DataRef( "file://" + movieURL.getFile() );
|
|
Movie m = new Movie();
|
|
m = m.fromDataRef( movieDataRef, StdQTConstants.newMovieActive );
|
|
setMovie(m);
|
|
getMovie().setTimeScale(1000);
|
|
}
|
|
catch(QTException qte)
|
|
{
|
|
qte.printStackTrace();
|
|
System.out.println( movieURL.toString() );
|
|
}
|
|
}
|
|
public Movie getMovie()
|
|
{
|
|
return movie;
|
|
}
|
|
|
|
public void setController(MovieController controller)
|
|
{
|
|
this.controller = controller;
|
|
}
|
|
public MovieController getController()
|
|
{
|
|
return controller;
|
|
}
|
|
|
|
public void setCanvas(QTCanvas canvas)
|
|
{
|
|
this.canvas = canvas;
|
|
}
|
|
public QTCanvas getCanvas()
|
|
{
|
|
return canvas;
|
|
}
|
|
|
|
|
|
//contract methods - initialize
|
|
public void displayBorders(boolean borders) throws SmartMoviePanelException
|
|
{
|
|
}
|
|
public void displayController(boolean controller) throws SmartMoviePanelException
|
|
{
|
|
}
|
|
public void loadMovie(URL mediaURL) throws SmartMoviePanelException
|
|
{
|
|
setMovie(mediaURL);
|
|
try
|
|
{
|
|
getCanvas().removeClient();
|
|
setController( new MovieController( getMovie() ));
|
|
getController().setKeysEnabled(true);
|
|
setPlayer(new QTPlayer( getController() ));
|
|
getCanvas().setClient( getPlayer(), true );
|
|
this.add( getCanvas() );
|
|
TimeBase theMoviesTimeBase = getMovie().getTimeBase();
|
|
|
|
// this callback is triggered when the rate of the movie changes
|
|
theMoviesRateCallback = new TimeBaseRateCallBack(theMoviesTimeBase, 1.0F, StdQTConstants.triggerRateChange);
|
|
// theMoviesRateCallback.callMeWhen();
|
|
|
|
// this callback is triggered when the movie ends
|
|
theMoviesExtremeCallback = new TimeBaseExtremesCallBack(theMoviesTimeBase, StdQTConstants.triggerAtStop);
|
|
// theMoviesExtremeCallback.callMeWhen();
|
|
|
|
// this callback is triggered when the movie starts
|
|
theMoviesExtremeCallback = new TimeBaseExtremesCallBack(theMoviesTimeBase, StdQTConstants.triggerAtStart);
|
|
// theMoviesExtremeCallback.callMeWhen();
|
|
|
|
// this callback is triggered when there is a jump in the timebase
|
|
theMoviesTimeJumpCallback = new TimeBaseTimeJumpCallBack(theMoviesTimeBase);
|
|
// theMoviesTimeJumpCallback.callMeWhen();
|
|
|
|
// this schedules the time callback once every 2 seconds
|
|
// this callback is triggered at a specific time interval
|
|
theMoviesTimeCallback = new TimeBaseTimeCallBack(theMoviesTimeBase, 1, 2, StdQTConstants.triggerTimeEither);
|
|
// theMoviesTimeCallback.callMeWhen();
|
|
|
|
//Using the Timer class you can get rescheduled properly and get callbacks at the set intervals. It uses the same callback
|
|
//mechanism of internally of the TimeCallback.
|
|
//Its recomended to use this Timer class to do callbacks , which would take care of the time base time changes and
|
|
//recscheduling of the tickle method .
|
|
Timer timer = new Timer(1, 2, new Tickler(), getMovie() );
|
|
// timer.setActive(true);
|
|
|
|
}
|
|
catch(QTException qte)
|
|
{
|
|
qte.printStackTrace();
|
|
}
|
|
}
|
|
|
|
//contract methods - control media
|
|
public void cmd_playOn() throws SmartMoviePanelException
|
|
{
|
|
try
|
|
{
|
|
getPlayer().setRate(1.0F);
|
|
}
|
|
catch(QTException qte)
|
|
{
|
|
qte.printStackTrace();
|
|
}
|
|
}
|
|
public void cmd_playSegment(Integer startTime, Integer stopTime) throws SmartMoviePanelException
|
|
{
|
|
//FIXME: need to deal with null stopTime
|
|
try
|
|
{
|
|
getPlayer().setTime( startTime.intValue() );
|
|
|
|
int value=stopTime.intValue();
|
|
TimeBase theMoviesTimeBase = getPlayer().getTimeBase();
|
|
theStopper = new TimeBaseTimeCallBackStopper(theMoviesTimeBase, 1000, value, StdQTConstants.triggerTimeEither);
|
|
theStopper.callMeWhen();
|
|
|
|
cmd_playOn();
|
|
|
|
System.out.println("Set start time to " +startTime.intValue() );
|
|
System.out.println("Set stop time " +stopTime.intValue() );
|
|
System.out.println("Current time " +getPlayer().getTime() );
|
|
System.out.println("Time Stopper's stop trigger " +theStopper.getCallTime() );
|
|
System.out.println("Player Scale: " +getPlayer().getScale() );
|
|
System.out.println("Movie Scale: " +getMovie().getTimeScale() );
|
|
|
|
}
|
|
catch (SmartMoviePanelException smpe) {}
|
|
catch (StdQTException sqte) {}
|
|
catch (QTException qte) {}
|
|
}
|
|
public void cmd_stop() throws SmartMoviePanelException
|
|
{
|
|
try
|
|
{
|
|
getPlayer().setRate(0.0F);
|
|
}
|
|
catch(QTException qte)
|
|
{
|
|
qte.printStackTrace();
|
|
}
|
|
}
|
|
|
|
//contract methods - media status
|
|
public boolean isInitialized()
|
|
{
|
|
return true;
|
|
}
|
|
public int getCurrentTime()
|
|
{
|
|
try {
|
|
return getMovie().getTime();
|
|
} catch (StdQTException stqte) {
|
|
stqte.printStackTrace();
|
|
return -1;
|
|
}
|
|
}
|
|
public int getEndTime()
|
|
{
|
|
try {
|
|
return getMovie().getDuration();
|
|
} catch (StdQTException stqte) {
|
|
stqte.printStackTrace();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
//helper methods - QT4J
|
|
public void startupQTSession()
|
|
{
|
|
//Initialize a QT session and add a test image
|
|
|
|
//These three try/catch blocks come from PlayMovie.java copyright
|
|
// Apple Co. I'm using it to test that QT and QT4Java exist
|
|
try
|
|
{
|
|
if (QTSession.isInitialized() == false)
|
|
QTSession.open();
|
|
}
|
|
catch (NoClassDefFoundError er)
|
|
{
|
|
add (new Label ("Can't Find QTJava classes"), "North");
|
|
add (new Label ("Check install and try again"), "South");
|
|
}
|
|
catch (SecurityException se)
|
|
{
|
|
// this is thrown by MRJ trying to find QTSession class
|
|
add (new Label ("Can't Find QTJava classes"), "North");
|
|
add (new Label ("Check install and try again"), "South");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// do a dynamic test for QTException
|
|
//so the QTException class is not loaded unless
|
|
// an unknown exception is thrown by the runtime
|
|
if (e instanceof ClassNotFoundException || e instanceof java.io.FileNotFoundException)
|
|
{
|
|
add (new Label ("Can't Find QTJava classes"), "North");
|
|
add (new Label ("Check install and try again"), "South");
|
|
}
|
|
else if (e instanceof QTException)
|
|
{
|
|
add (new Label ("Problem with QuickTime install"), "North");
|
|
if (((QTException)e).errorCode() == -2093)
|
|
add (new Label ("QuickTime must be installed"), "South");
|
|
else
|
|
add (new Label (e.getMessage()), "South");
|
|
}
|
|
}
|
|
try
|
|
{
|
|
setCanvas( new QTCanvas(QTCanvas.kInitialSize, 0.5F, 0.5F) );
|
|
this.add( getCanvas() );
|
|
getCanvas().setClient(ImageDrawer.getQTLogo(), true);
|
|
}
|
|
catch(QTException qte)
|
|
{
|
|
qte.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void destroy()
|
|
{
|
|
QTSession.close();
|
|
System.out.println("Clean up performed.");
|
|
}
|
|
|
|
//constructor
|
|
public SmartQT4JPlayer(Container cont, URL mediaURL)
|
|
{
|
|
super( new GridLayout() );
|
|
startupQTSession();
|
|
}
|
|
public SmartQT4JPlayer()
|
|
{
|
|
super( new GridLayout() );
|
|
startupQTSession();
|
|
}
|
|
|
|
// inner classes
|
|
/**
|
|
* This class extends the RateCallBack class and provides an execute routine
|
|
* that is invoked by the callback when the rate of the movie changes
|
|
*
|
|
*@author travis
|
|
*@created September 3, 2002
|
|
*/
|
|
|
|
public class TimeBaseRateCallBack extends RateCallBack
|
|
{
|
|
public TimeBaseRateCallBack(TimeBase tb, float rate, int flag) throws QTException
|
|
{
|
|
super(tb, rate, flag);
|
|
}
|
|
|
|
public void execute()
|
|
{
|
|
try
|
|
{
|
|
System.out.println("--- RateCallBack@ratechange---");
|
|
|
|
cancel();
|
|
//reschedule
|
|
callMeWhen();
|
|
|
|
} catch (StdQTException e)
|
|
{}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class implements a method that is called when the movie stops
|
|
*
|
|
*@author travis
|
|
*@created September 3, 2002
|
|
*/
|
|
|
|
class TimeBaseExtremesCallBack extends quicktime.std.clocks.ExtremesCallBack
|
|
{
|
|
|
|
public TimeBaseExtremesCallBack(TimeBase tb, int flag) throws QTException
|
|
{
|
|
super(tb, flag);
|
|
}
|
|
|
|
public void execute()
|
|
{
|
|
try
|
|
{
|
|
System.out.println("--- ExtremesCallBack@stop---");
|
|
|
|
//cancel
|
|
cancel();
|
|
//reschedule
|
|
callMeWhen();
|
|
}
|
|
catch (StdQTException e)
|
|
{}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class extends the TimeJumpCallBack class to provide a method that is
|
|
* called when the timebase of the movie changes (IE, if the user clicks in
|
|
* the movie controller)
|
|
*
|
|
*@author travis
|
|
*@created September 3, 2002
|
|
*/
|
|
|
|
class TimeBaseTimeJumpCallBack extends quicktime.std.clocks.TimeJumpCallBack
|
|
{
|
|
public TimeBaseTimeJumpCallBack(TimeBase tb) throws QTException
|
|
{
|
|
super(tb);
|
|
}
|
|
|
|
public void execute()
|
|
{
|
|
try
|
|
{
|
|
System.out.println("--- TimeJumpCallBack---");
|
|
|
|
cancel();
|
|
//reschedule
|
|
callMeWhen();
|
|
}
|
|
catch (StdQTException e)
|
|
{}
|
|
}
|
|
}
|
|
|
|
class TimeBaseTimeCallBack extends quicktime.std.clocks.TimeCallBack
|
|
{
|
|
private int callTime;
|
|
private TimeBase theTimeBase;
|
|
|
|
public void setCallTime(int time)
|
|
{
|
|
callTime=time;
|
|
value=callTime;
|
|
}
|
|
public int getCallTime()
|
|
{
|
|
return callTime;
|
|
}
|
|
public void execute()
|
|
{
|
|
try
|
|
{
|
|
System.out.println("--- TimeCallBack@triggerTimeEither--- called at:" + timeWhenCalledMsecs + "msecs");
|
|
|
|
cancel();
|
|
//reschedule
|
|
callMeWhen();
|
|
}
|
|
catch (StdQTException e)
|
|
{}
|
|
}
|
|
public TimeBaseTimeCallBack(TimeBase tb, int scale, int value, int flags) throws QTException
|
|
{
|
|
super(tb, scale, value, flags);
|
|
// setCallTime(value/1000);
|
|
setCallTime(value);
|
|
theTimeBase = tb;
|
|
}
|
|
}
|
|
|
|
class TimeBaseTimeCallBackStopper extends TimeBaseTimeCallBack
|
|
{
|
|
public void execute()
|
|
{
|
|
try
|
|
{
|
|
System.out.println("---TimeCallBackStopper--- called at:" + timeWhenCalledMsecs + "msecs");
|
|
System.out.println("---TimeCallBackStopper--- callTime is: " + getCallTime() + " msecs");
|
|
cmd_stop();
|
|
System.out.println("---TimeCallBackStopper--- Player time is: " + getPlayer().getTime() + " msecs");
|
|
System.out.println("---TimeCallBackStopper--- Movie time is: " + getMovie().getTime() + " msecs");
|
|
|
|
cancelAndCleanup();
|
|
//reschedule
|
|
//callMeWhen();
|
|
}
|
|
catch (StdQTException e) {}
|
|
catch (SmartMoviePanelException smpe) {}
|
|
}
|
|
public TimeBaseTimeCallBackStopper(TimeBase tb, int scale, int value, int flags) throws QTException
|
|
{
|
|
super(tb, scale, value, flags);
|
|
}
|
|
|
|
}
|
|
|
|
public class Tickler implements Ticklish
|
|
{
|
|
public void timeChanged(int newTime) throws QTException
|
|
{
|
|
System.out.println("* * * * Timer Class * * * timeChanged at:" + newTime);
|
|
}
|
|
|
|
public boolean tickle(float er, int time) throws QTException
|
|
{
|
|
System.out.println("* * * * Timer Class * * * tickle at:" + time);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
}
|