/*
* Open Source Physics software is free software as described near the bottom of this code file.
*
* For additional information and documentation on Open Source Physics please see:
* <http://www.opensourcephysics.org/>
*/
/*
* The org.opensourcephysics.media.core package defines the Open Source Physics
* media framework for working with video and other media.
*
* Copyright (c) 2014 Douglas Brown and Wolfgang Christian.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* For additional information and documentation on Open Source Physics,
* please see <http://www.opensourcephysics.org/>.
*/
package org.opensourcephysics.media.core;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.event.SwingPropertyChangeSupport;
import org.opensourcephysics.controls.XML;
import org.opensourcephysics.controls.XMLControl;
/**
* A ClipControl controls a VideoClip. This is an abstract
* class that cannot be instantiated directly.
*
* @author Douglas Brown
* @version 1.0
*/
public abstract class ClipControl implements PropertyChangeListener {
// instance fields
protected int stepNumber = 0;
protected int videoFrameNumber = 0;
protected final VideoClip clip;
protected Video video;
protected double rate = 1;
protected boolean looping = false;
protected PropertyChangeSupport support;
protected double timeStretch = 1;
protected DataTrack timeSource;
protected double savedFrameDuration;
public boolean videoVisible = true;
/**
* Returns an instance of ClipControl.
*
* @param clip the video clip
* @return an appropriate clip control
*/
public static ClipControl getControl(VideoClip clip) {
Video video = clip.getVideo();
if(clip.isPlayAllSteps()||(video==null)||(video instanceof ImageVideo)) {
return new StepperClipControl(clip);
}
return new VideoClipControl(clip);
}
/**
* Constructs a ClipControl object. This is an abstract class that
* cannot be instantiated directly.
*
* @param videoClip the video clip
*/
protected ClipControl(VideoClip videoClip) {
clip = videoClip;
video = clip.getVideo();
support = new SwingPropertyChangeSupport(this);
}
/**
* Gets the clip that is controlled by this clip control.
*
* @return the clip
*/
public VideoClip getVideoClip() {
return clip;
}
/**
* Plays the clip.
*/
public void play() {
/** implemented by subclasses */
}
/**
* Stops at the next step.
*/
public void stop() {
/** implemented by subclasses */
}
/**
* Steps forward one step.
*/
public void step() {
/** implemented by subclasses */
}
/**
* Steps back one step.
*/
public void back() {
/** implemented by subclasses */
}
/**
* Sets the frame number.
*
* @param n the desired frame number
*/
public void setFrameNumber(int n) {
if (clip.includesFrame(n)) {
stepNumber = clip.frameToStep(n);
n = Math.max(0, n+clip.getFrameShift());
videoFrameNumber = n;
}
}
/**
* Sets the step number.
*
* @param n the desired step number
*/
public void setStepNumber(int n) {
stepNumber = n;
n = Math.max(0, clip.stepToFrame(n)+clip.getFrameShift());
videoFrameNumber = n;
}
/**
* Gets the step number.
*
* @return the current step number
*/
public int getStepNumber() {
return stepNumber;
}
/**
* Sets the play rate.
*
* @param newRate the desired rate
*/
public void setRate(double newRate) {
rate = newRate;
}
/**
* Gets the play rate.
*
* @return the current rate
*/
public double getRate() {
return rate;
}
/**
* Turns on/off looping.
*
* @param loops <code>true</code> to turn looping on
*/
public void setLooping(boolean loops) {
looping = loops;
}
/**
* Gets the looping status.
*
* @return <code>true</code> if looping is on
*/
public boolean isLooping() {
return looping;
}
/**
* Gets the current frame number.
*
* @return the frame number
*/
public int getFrameNumber() {
int n = videoFrameNumber-clip.getFrameShift();
n = Math.max(0, n); // can't be negative
return n;
}
/**
* Gets the DataTrack time source, if any.
*
* @return the time source (may be null)
*/
public DataTrack getTimeSource() {
return timeSource;
}
/**
* Sets the time source to a DataTrack.
*
* @param source the time source (may be null)
*/
public void setTimeSource(DataTrack source) {
DataTrack prev = timeSource;
timeSource = source;
if (prev==null && timeSource!=null) {
clip.savedStartTime = clip.isDefaultStartTime? Double.NaN: clip.getStartTime();
clip.startTimeIsSaved = true;
savedFrameDuration = getMeanFrameDuration();
}
else if (prev!=null && timeSource==null) {
clip.setStartTime(clip.savedStartTime);
clip.startTimeIsSaved = false;
setFrameDuration(savedFrameDuration);
}
if (timeSource!=null && timeSource.isTimeDataAvailable()) {
clip.setStartTime(timeSource.getVideoStartTime()*1000); // convert to ms
setFrameDuration(timeSource.getFrameDuration()*1000); // convert to ms
}
}
/**
* Responds to property change events.
*
* @param e the property change event
*/
public void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
if(name.equals("startframe")) { // from video clip //$NON-NLS-1$
int n = getFrameNumber();
stepNumber = clip.frameToStep(n);
}
}
/**
* Gets the playing status.
*
* @return <code>true</code> if playing
*/
public abstract boolean isPlaying();
/**
* Gets the current time in milliseconds measured from step 0.
*
* @return the current time
*/
public abstract double getTime();
/**
* Gets the start time of the specified step measured from step 0.
*
* @param stepNumber the step number
* @return the step time
*/
public abstract double getStepTime(int stepNumber);
/**
* Sets the frame duration.
*
* @param duration the desired frame duration in milliseconds
*/
public abstract void setFrameDuration(double duration);
/**
* Gets the average frame duration in milliseconds.
*
* @return the mean frame duration in milliseconds
*/
public abstract double getMeanFrameDuration();
/**
* Adds a PropertyChangeListener.
*
* @param listener the object requesting property change notification
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
/**
* Adds a PropertyChangeListener.
*
* @param property the name of the property of interest
* @param listener the object requesting property change notification
*/
public void addPropertyChangeListener(String property, PropertyChangeListener listener) {
support.addPropertyChangeListener(property, listener);
}
/**
* Removes a PropertyChangeListener.
*
* @param listener the listener requesting removal
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
/**
* Removes a PropertyChangeListener for a specified property.
*
* @param property the name of the property
* @param listener the listener to remove
*/
public void removePropertyChangeListener(String property, PropertyChangeListener listener) {
support.removePropertyChangeListener(property, listener);
}
/**
* Empty dispose method.
*/
public void dispose() {
/** implemented by subclasses */
}
/**
* Determines if a DataTrack is actively providing time data.
*/
public static boolean isTimeSource(DataTrack track) {
if (track.getVideoPanel()==null) return false;
return track==track.getVideoPanel().getPlayer().getClipControl().getTimeSource();
}
/**
* A class to save and load data for this class.
*/
static class Loader implements XML.ObjectLoader {
/**
* Saves object data in an XMLControl.
*
* @param control the control to save to
* @param obj the object to save
*/
public void saveObject(XMLControl control, Object obj) {
ClipControl clipControl = (ClipControl) obj;
control.setValue("rate", clipControl.getRate()); //$NON-NLS-1$
control.setValue("delta_t", clipControl.getTimeSource()!=null? //$NON-NLS-1$
clipControl.savedFrameDuration: clipControl.getMeanFrameDuration());
if(clipControl.isLooping()) {
control.setValue("looping", true); //$NON-NLS-1$
}
control.setValue("frame", clipControl.getFrameNumber()); //$NON-NLS-1$
}
/**
* Creates a new object.
*
* @param control the XMLControl with the object data
* @return the newly created object
*/
public Object createObject(XMLControl control) {
return null;
}
/**
* Loads a VideoClip with data from an XMLControl.
*
* @param control the element
* @param obj the object
* @return the loaded object
*/
public Object loadObject(XMLControl control, Object obj) {
ClipControl clipControl = (ClipControl) obj;
// set rate
double rate = control.getDouble("rate"); //$NON-NLS-1$
if(rate!=Double.NaN) {
clipControl.setRate(rate);
}
// set dt
double dt = control.getDouble("delta_t"); //$NON-NLS-1$
if(dt!=Double.NaN) {
clipControl.setFrameDuration(dt);
}
// set looping and playing
clipControl.setLooping(control.getBoolean("looping")); //$NON-NLS-1$
// set frame number
if (control.getPropertyNames().contains("frame")) { //$NON-NLS-1$
int n = control.getInt("frame"); //$NON-NLS-1$
n = clipControl.getVideoClip().frameToStep(n);
clipControl.setStepNumber(n);
}
return obj;
}
}
}
/*
* Open Source Physics software is free software; you can redistribute
* it and/or modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of the License,
* or(at your option) any later version.
* Code that uses any portion of the code in the org.opensourcephysics package
* or any subpackage (subdirectory) of this package must must also be be released
* under the GNU GPL license.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2007 The Open Source Physics project
* http://www.opensourcephysics.org
*/