diff --git a/source/org/thdl/media/SmartJMFPlayer.java b/source/org/thdl/media/SmartJMFPlayer.java index e26ea88..7dcd53c 100644 --- a/source/org/thdl/media/SmartJMFPlayer.java +++ b/source/org/thdl/media/SmartJMFPlayer.java @@ -30,8 +30,6 @@ import org.thdl.util.ThdlDebug; /*-----------------------------------------------------------------------*/ public class SmartJMFPlayer extends SmartMoviePanel implements ControllerListener { - private EventListenerList listenerList = new EventListenerList(); - public URL mediaURL; private Player player = null; @@ -54,9 +52,12 @@ public class SmartJMFPlayer extends SmartMoviePanel implements ControllerListene private Float to = null; /*-----------------------------------------------------------------------*/ - public String getName() { + public String getIdentifyingName() { return "Java Media Framework"; } + public URL getMediaURL() { + return mediaURL; + } public SmartJMFPlayer() { super(new GridLayout()); } @@ -79,7 +80,17 @@ public class SmartJMFPlayer extends SmartMoviePanel implements ControllerListene public void destroy() throws SmartMoviePanelException { if (false) throw new SmartMoviePanelException(); + removeAllAnnotationPlayers(); player.close(); + removeAll(); + mediaURL = null; + isRealized = false; + isSized = false; + visualComponent = null; + controlComponent = null; + panel = null; + vPanel = null; + isMediaAudio = false; } /*-----------------------------------------------------------------------*/ private void start() { @@ -118,27 +129,27 @@ public class SmartJMFPlayer extends SmartMoviePanel implements ControllerListene /*-----------------------------------------------------------------------*/ private void showMediaComponent() { if (isRealized && isCached) { - if (visualComponent == null) { - if (panel == null) { - setLayout(new GridLayout(1,1)); - vPanel = new JPanel(); - vPanel.setLayout( new BorderLayout() ); - if ((visualComponent = player.getVisualComponent())!= null) - vPanel.add("Center", visualComponent); - else - isMediaAudio = true; - if ((controlComponent = player.getControlPanelComponent()) != null) { - if (visualComponent == null) //no video - vPanel.setPreferredSize(new Dimension(400,25)); - vPanel.add("South", controlComponent); + if (visualComponent == null) { + if (panel == null) { + setLayout(new GridLayout(1,1)); + vPanel = new JPanel(); + vPanel.setLayout( new BorderLayout() ); + if ((visualComponent = player.getVisualComponent())!= null) + vPanel.add("Center", visualComponent); + else + isMediaAudio = true; + if ((controlComponent = player.getControlPanelComponent()) != null) { + if (visualComponent == null) //no video + vPanel.setPreferredSize(new Dimension(400,25)); + vPanel.add("South", controlComponent); + } } + add(vPanel); + parent.invalidate(); + parent.validate(); + parent.repaint(); + isSized = true; } - add(vPanel); - parent.invalidate(); - parent.validate(); - parent.repaint(); - isSized = true; - } } } public synchronized void controllerUpdate(ControllerEvent event) { @@ -147,23 +158,14 @@ public class SmartJMFPlayer extends SmartMoviePanel implements ControllerListene if (event instanceof RealizeCompleteEvent) { System.out.println("received RealizeCompleteEvent event"); isRealized = true; - if (mediaURL.getProtocol() == "file") { //if http then wait until entire media is cached + if (mediaURL.getProtocol().equals("file")) { //if http then wait until entire media is cached isCached = true; showMediaComponent(); } else if (isCached) //must be http showMediaComponent(); } else if (event instanceof StartEvent) { - StartEvent se = (StartEvent)event; - Time t = se.getMediaTime(); - long longt = t.getNanoseconds(); - Float from = new Float(longt); - float f = (from.floatValue() / 1000000000); - from = new Float(f); - t = player.getStopTime(); - longt = t.getNanoseconds(); - to = new Float(longt); - f = (to.floatValue() / 1000000000); - to = new Float(f); + launchAnnotationTimer(); //FIXME should have upper limit (stop time) + if (timer != null) { timer.cancel(); @@ -179,7 +181,7 @@ public class SmartJMFPlayer extends SmartMoviePanel implements ControllerListene }}, 0, 15); } else if (event instanceof StopEvent) { pauseTime = player.getMediaTime(); - + cancelAnnotationTimer(); /*messy problems require messy solutions: if the slider is present, dragging it while playing creates diff --git a/source/org/thdl/media/SmartMoviePanel.java b/source/org/thdl/media/SmartMoviePanel.java index c04b2ad..1edb11e 100644 --- a/source/org/thdl/media/SmartMoviePanel.java +++ b/source/org/thdl/media/SmartMoviePanel.java @@ -20,10 +20,258 @@ package org.thdl.media; import java.awt.*; import java.net.*; +import java.util.*; +import javax.swing.event.EventListenerList; + +import org.thdl.savant.AnnotationPlayer; //should move AP to org.thdl.annotation + public abstract class SmartMoviePanel extends Panel { - public abstract String getName(); +//fields + private EventListenerList listenerList = new EventListenerList(); + + private Vector orderStartID = null, orderEndID = null; + private Stack pileStart = null, pileEnd = null; + private Hashtable hashStart = null, hashEnd = null; + + private Timer annTimer = null; + +/*-----------------------------------------------------------------------*/ + public void addAnnotationPlayer(AnnotationPlayer ap) + { + listenerList.add(AnnotationPlayer.class, ap); + } + public void removeAnnotationPlayer(AnnotationPlayer ap) + { + listenerList.remove(AnnotationPlayer.class, ap); + } + public void removeAllAnnotationPlayers() { + listenerList = new EventListenerList(); + } + private void fireStartAnnotation(String id) + { + //see javadocs on EventListenerList for how following array is structured + Object[] listeners = listenerList.getListenerList(); + + for (int i = listeners.length-2; i>=0; i-=2) + { + if (listeners[i]==AnnotationPlayer.class) + ((AnnotationPlayer)listeners[i+1]).startAnnotation(id); + } + } + private void fireStopAnnotation(String id) + { + //see javadocs on EventListenerList for how following array is structured + Object[] listeners = listenerList.getListenerList(); + + for (int i = listeners.length-2; i>=0; i-=2) + { + if (listeners[i]==AnnotationPlayer.class) + ((AnnotationPlayer)listeners[i+1]).stopAnnotation(id); + } + } +/*-----------------------------------------------------------------------*/ + public void initForSavant(String starts, String ends, String ids) { + String TAB_STARTS = starts; + String TAB_ENDS = ends; + String TAB_IDS = ids; + + hashStart = new Hashtable(); + hashEnd = new Hashtable(); + pileStart = new Stack(); + pileEnd = new Stack(); + + StringTokenizer stIDS = new StringTokenizer(TAB_IDS, ","); + StringTokenizer stSTARTS = new StringTokenizer(TAB_STARTS, ","); + StringTokenizer stENDS = new StringTokenizer(TAB_ENDS, ","); + while ((stIDS.hasMoreTokens()) && (stSTARTS.hasMoreTokens()) && (stENDS.hasMoreTokens())) { + String sID = stIDS.nextToken(); + String sStart = stSTARTS.nextToken(); + String sEnd = stENDS.nextToken(); + try { + Integer start = new Integer(sStart); + hashStart.put(sID, start); + } catch (NumberFormatException err) { + hashStart.put(sID, new Integer(0)); + } + try { + Integer end = new Integer(sEnd); + hashEnd.put(sID, end); + } catch (NumberFormatException err) { + hashEnd.put(sID, new Integer(0)); + } + } + + Vector saveOrder = new Vector(); + for (Enumeration e = hashStart.keys() ; e.hasMoreElements() ;) { + Object o = e.nextElement(); + saveOrder.addElement(o); + } + orderStartID = new Vector(); + while (saveOrder.size() > 0) { + int num = getMinusStart(saveOrder); + orderStartID.addElement(saveOrder.elementAt(num)); + saveOrder.removeElementAt(num); + } + saveOrder = new Vector(); + for (Enumeration e = hashEnd.keys() ; e.hasMoreElements() ;) { + Object o = e.nextElement(); + saveOrder.addElement(o); + } + orderEndID = new Vector(); + while (saveOrder.size() > 0) { + int num = getMinusEnd(saveOrder); + orderEndID.addElement(saveOrder.elementAt(num)); + saveOrder.removeElementAt(num); + } + } + private int getMinusStart(Vector v) { + int index = 0; + String first = (String)v.elementAt(index); + Integer minus = (Integer)hashStart.get(first); + for (int i=0;i f.intValue()) { + minus = f; + index = i; + } + } + return index; + } + private int getMinusEnd(Vector v) { + int index = 0; + String first = (String)v.elementAt(index); + Integer minus = (Integer)hashEnd.get(first); + for (int i=0;i f.intValue()) { + minus = f; + index = i; + } + } + return index; + } + public boolean cmd_isID(String theID) { + System.out.println(hashStart.containsKey(theID)); + return hashStart.containsKey(theID); + } + public void cmd_playFrom(String fromID) { + Integer from = (Integer)hashStart.get(fromID); + try { + cmd_playSegment(from, null); + } catch (SmartMoviePanelException smpe) { + smpe.printStackTrace(); + } + } + public void cmd_playS(String fromID) { + Integer from = (Integer)hashStart.get(fromID); + Integer to = (Integer)hashEnd.get(fromID); + try { + cmd_playSegment(from, to); + } catch (SmartMoviePanelException smpe) { + smpe.printStackTrace(); + } + } + public void launchAnnotationTimer() { //FIXME: should have upper limit - stop time else end time + if (listenerList.getListenerCount() == 0) //no annotation listeners + return; + + int i = getCurrentTime(); + Integer from = new Integer(i); + remplisPileStart(from, new Integer(getEndTime())); + if (annTimer != null) { + annTimer.cancel(); + annTimer = null; + } + annTimer = new java.util.Timer(true); + annTimer.schedule(new TimerTask() { + public void run() { + cmd_nextEvent(); + }}, 0, 15); + } + public void cancelAnnotationTimer() { + if (listenerList.getListenerCount() == 0) //no annotation listeners + return; + + if (annTimer != null) { + annTimer.cancel(); + annTimer = null; + } + } + private void cmd_nextEvent() { + Integer when = new Integer(getCurrentTime()); + if (!pileStart.empty()) { + String id = (String)pileStart.peek(); + Integer f = (Integer)hashStart.get(id); + if (when.intValue() >= f.intValue()) { + id = (String)pileStart.pop(); + fireStartAnnotation(id); + } + } + if (!pileEnd.empty()) { + String id = (String)pileEnd.peek(); + Integer f = (Integer)hashEnd.get(id); + if (when.intValue() >= f.intValue()) { + id = (String)pileEnd.pop(); + fireStopAnnotation(id); + } + } + } + private void vide_Pile() { + while (!pileEnd.empty()) { //vider la pile des items qui ne sont pas + String id = (String)pileEnd.pop(); //encore fini + if (pileStart.search(id) == -1) { + fireStopAnnotation(id); + } + } + } +/* empties the pile, and then reconstructs it to consist of all ids + whose start time or end time is included between start and end. */ + + private void remplisPileStart(Integer start, Integer end) { + vide_Pile(); + pileStart.removeAllElements(); + pileEnd.removeAllElements(); + for (int i=orderEndID.size()-1; i!=-1; i--) { + String id = (String)orderEndID.elementAt(i); + Integer f = (Integer)hashEnd.get(id); + if ((f.intValue() > start.intValue()) && (f.intValue() <= end.intValue())) { + pileEnd.push(id); + } + } + +/* note: we are also interested in ids that begin before start, + provided they overlap with the interval start-end. */ + + for (int i=orderStartID.size()-1; i!=-1; i--) { + String id = (String)orderStartID.elementAt(i); + Integer f = (Integer)hashStart.get(id); + Integer f2 = (Integer)hashEnd.get(id); + if ( (f.intValue() >= start.intValue() && f.intValue() < end.intValue()) || + (f.intValue() < start.intValue() && f2.intValue() > start.intValue())) { + pileStart.push(id); + } + } + } +/*-----------------------------------------------------------------------*/ + + +//constructor + public SmartMoviePanel(GridLayout layout) + { + super(layout); + } + + +/*-----------------------------------------------------------------------*/ +//abstract methods + + + public abstract String getIdentifyingName(); + public abstract URL getMediaURL(); public abstract void setParentContainer(Container c); //helper methods - initialize @@ -44,9 +292,4 @@ public abstract class SmartMoviePanel extends Panel //helper methods - cleanup public abstract void destroy() throws SmartMoviePanelException; -//constructor - public SmartMoviePanel(GridLayout layout) - { - super(layout); - } } diff --git a/source/org/thdl/media/SmartPlayerFactory.java b/source/org/thdl/media/SmartPlayerFactory.java index 2adb9c5..7aa89c3 100644 --- a/source/org/thdl/media/SmartPlayerFactory.java +++ b/source/org/thdl/media/SmartPlayerFactory.java @@ -19,35 +19,63 @@ Contributor(s): ______________________________________. package org.thdl.media; import java.lang.reflect.*; -import java.util.*; +import java.util.List; +import java.util.ArrayList; + +import org.thdl.util.*; import org.thdl.util.ThdlDebug; public class SmartPlayerFactory { + public static List moviePlayers; + /** You cannot instantiate this class. */ private SmartPlayerFactory() { } - static final String[] possiblePlayers - = {"org.thdl.media.SmartJMFPlayer", "org.thdl.media.SmartQT4JPlayer"}; + public static List getAllAvailableSmartPlayers() { + String os; + try { + os = System.getProperty("os.name").toLowerCase(); + } catch (SecurityException e) { + os = "unknown"; + } - static SmartMoviePanel[] getAllAvailableSmartPlayers() { - List moviePlayers = new ArrayList(); + String defaultPlayer; + if (os.indexOf("mac") != -1) //macs default to org.thdl.media.SmartQT4JPlayer + defaultPlayer = ThdlOptions.getStringOption("thdl.media.player", "org.thdl.media.SmartQT4JPlayer"); + else if (os.indexOf("windows") != -1) //windows defaults to SmartJMFPlayer + defaultPlayer = ThdlOptions.getStringOption("thdl.media.player", "org.thdl.media.SmartJMFPlayer"); + else //put linux etc. here + defaultPlayer = ThdlOptions.getStringOption("thdl.media.player", "org.thdl.media.SmartJMFPlayer"); + + String[] possiblePlayers; + if (defaultPlayer.equals("org.thdl.media.SmartJMFPlayer")) + possiblePlayers = new String[] {"org.thdl.media.SmartJMFPlayer", "org.thdl.media.SmartQT4JPlayer"}; + else + possiblePlayers = new String[] {"org.thdl.media.SmartQT4JPlayer", "org.thdl.media.SmartJMFPlayer"}; + + moviePlayers = new ArrayList(); for (int i=0; i0 is playing) + rateCallBack = new RateCallBack(theMoviesTimeBase, 0, StdQTConstants.triggerRateChange) { + public void execute() { +System.out.println("Rate changed to: " + String.valueOf(rateWhenCalled)); + if (rateWhenCalled > 0) + launchAnnotationTimer(); + else + cancelAnnotationTimer(); + schedule(this); + } + }; + schedule(rateCallBack); + // this callback is triggered when there is a jump in the timebase, ie when the slider control is adjusted + jumpCallBack = new TimeJumpCallBack(theMoviesTimeBase) { + public void execute() { +System.out.println("Time Jump. New rate: " + String.valueOf(rateWhenCalled)); + if (rateWhenCalled > 0) + launchAnnotationTimer(); + schedule(this); + } + }; + schedule(jumpCallBack); } catch(QTException qte) { qte.printStackTrace(); } } + + private void schedule(QTCallBack callBack) { + try { + callBack.callMeWhen(); + } catch (StdQTException stdqte) { + stdqte.printStackTrace(); + } + } //contract methods - control media public void cmd_playOn() throws SmartMoviePanelException @@ -234,6 +243,7 @@ public class SmartQT4JPlayer extends SmartMoviePanel { try { + getPlayer().setRate(0); getPlayer().setTime( startTime.intValue() ); int value; @@ -248,13 +258,16 @@ public class SmartQT4JPlayer extends SmartMoviePanel cmd_playOn(); +/* System.out.println("Set start time to " +startTime.intValue() ); - System.out.println("Set stop time " +stopTime.intValue() ); + if (stopTime != null) + 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() ); + if (stopTime != null) + 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) {} @@ -273,12 +286,17 @@ public class SmartQT4JPlayer extends SmartMoviePanel } //contract methods - media status - public boolean isInitialized() - { - return true; + public boolean isInitialized() { + return true; //FIXME what should this do? } public boolean isPlaying() { - return false; //FIXME + try { + if (getMovie().getRate() > 0) + return true; + } catch (StdQTException stdqte) { + stdqte.printStackTrace(); + } + return false; } public int getCurrentTime() { @@ -286,7 +304,7 @@ public class SmartQT4JPlayer extends SmartMoviePanel return getMovie().getTime(); } catch (StdQTException stqte) { stqte.printStackTrace(); - return -1; + return 0; } } public int getEndTime() @@ -295,70 +313,22 @@ public class SmartQT4JPlayer extends SmartMoviePanel return getMovie().getDuration(); } catch (StdQTException stqte) { stqte.printStackTrace(); - return -1; + return 0; } } //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() { + if (rateCallBack != null) + rateCallBack.cancelAndCleanup(); + if (jumpCallBack != null) + jumpCallBack.cancelAndCleanup(); + removeAllAnnotationPlayers(); QTSession.close(); + removeAll(); + mediaUrl = null; System.out.println("Clean up performed."); } @@ -366,7 +336,6 @@ public class SmartQT4JPlayer extends SmartMoviePanel public SmartQT4JPlayer(Container cont, URL mediaURL) { super( new GridLayout() ); -// startupQTSession(); try { loadMovie(mediaURL); } catch (SmartMoviePanelException smpe) { @@ -376,101 +345,10 @@ public class SmartQT4JPlayer extends SmartMoviePanel 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 { @@ -490,7 +368,7 @@ public class SmartQT4JPlayer extends SmartMoviePanel { try { - System.out.println("--- TimeCallBack@triggerTimeEither--- called at:" + timeWhenCalledMsecs + "msecs"); +// System.out.println("--- TimeCallBack@triggerTimeEither--- called at:" + timeWhenCalledMsecs + "msecs"); cancel(); //reschedule @@ -512,19 +390,22 @@ public class SmartQT4JPlayer extends SmartMoviePanel { public void execute() { - try - { + 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 @@ -533,18 +414,4 @@ public class SmartQT4JPlayer extends SmartMoviePanel } } - - 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; - } - } }