From 392b2b180a5ea4e1f16c08d51e3249e4ea2497cc Mon Sep 17 00:00:00 2001 From: eg3p Date: Sat, 2 Nov 2002 20:20:30 +0000 Subject: [PATCH] These files have been updated for use with Savant. That is, org.thdl.savant.SoundPanel has been eliminated in favour of these classes, which are shared between QD and Savant. The main change is that SmartMoviePanels can now communicate with the outside world, for example to send messages to a Savant text window telling it to update highlights. --- source/org/thdl/media/SmartJMFPlayer.java | 72 ++--- source/org/thdl/media/SmartMoviePanel.java | 255 +++++++++++++++- source/org/thdl/media/SmartPlayerFactory.java | 44 ++- source/org/thdl/media/SmartQT4JPlayer.java | 277 +++++------------- 4 files changed, 394 insertions(+), 254 deletions(-) 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; - } - } }