From 426af4f5eae0726776cf7ee497630ecf5c64dda2 Mon Sep 17 00:00:00 2001 From: codeanticode Date: Wed, 15 Aug 2012 04:33:47 +0000 Subject: [PATCH] Cleaned-up Movie class API --- .../video/src/processing/video/Movie.java | 464 +++++++++--------- .../video/src/processing/video/Video.java | 5 - 2 files changed, 219 insertions(+), 250 deletions(-) diff --git a/java/libraries/video/src/processing/video/Movie.java b/java/libraries/video/src/processing/video/Movie.java index 6f7ef70ce..8f15cdc6e 100644 --- a/java/libraries/video/src/processing/video/Movie.java +++ b/java/libraries/video/src/processing/video/Movie.java @@ -30,7 +30,6 @@ import java.awt.Dimension; import java.io.*; import java.net.URI; import java.nio.*; -import java.util.List; import java.util.concurrent.TimeUnit; import java.lang.reflect.*; @@ -38,6 +37,7 @@ import org.gstreamer.*; import org.gstreamer.Buffer; import org.gstreamer.elements.*; + /** * ( begin auto-generated from Movie.xml ) * @@ -52,19 +52,19 @@ import org.gstreamer.elements.*; */ public class Movie extends PImage implements PConstants { public static String[] supportedProtocols = { "http" }; - - protected String filename; + public float frameRate; + public String filename; protected boolean playing = false; protected boolean paused = false; protected boolean repeat = false; - protected float fps; protected float rate; protected int bufWidth; protected int bufHeight; + protected float volume; - protected PlayBin2 gplayer; + public PlayBin2 playBin; protected Method movieEventMethod; protected Object eventHandler; @@ -79,8 +79,7 @@ public class Movie extends PImage implements PConstants { protected boolean firstFrame = true; protected boolean seeking = false; - protected boolean useBufferSink = false; -// protected boolean useGLSink = true; + protected boolean useBufferSink = false; protected Object bufferSink; protected Method sinkCopyMethod; protected Method sinkSetMethod; @@ -88,6 +87,7 @@ public class Movie extends PImage implements PConstants { protected Buffer natBuffer = null; protected BufferDataAppSink natSink = null; + /** * Creates an instance of GSMovie loading the movie from filename. * @@ -99,15 +99,16 @@ public class Movie extends PImage implements PConstants { initGStreamer(parent, filename); } + /** - * Releases the gstreamer resources associated to this movie object. - * It shouldn't be used after this. + * Disposes all the native resources associated to this movie. */ - public void delete() { - if (gplayer != null) { + public void dispose() { + if (playBin != null) { try { - if (gplayer.isPlaying()) { - gplayer.stop(); + if (playBin.isPlaying()) { + playBin.stop(); + playBin.getState(); } } catch (IllegalStateException e) { PGraphics.showWarning("error when deleting player, maybe some native resource is already disposed"); @@ -131,75 +132,24 @@ public class Movie extends PImage implements PConstants { natSink = null; } - gplayer.dispose(); - gplayer = null; + playBin.dispose(); + playBin = null; } } - /** - * Same as delete. - */ - public void dispose() { - delete(); - } - - /** * Finalizer of the class. */ protected void finalize() throws Throwable { try { - delete(); + dispose(); } finally { super.finalize(); } } - /** - * Get the width of the source video. Note: calling this method repeatedly - * can slow down playback performance. - * - * @return int - */ - protected int getSourceWidth() { - Dimension dim = gplayer.getVideoSize(); - if (dim != null) { - return dim.width; - } else { - return 0; - } - } - - - /** - * Get the height of the source video. Note: calling this method repeatedly - * can slow down playback performance. - * - * @return int - */ - protected int getSourceHeight() { - Dimension dim = gplayer.getVideoSize(); - if (dim != null) { - return dim.height; - } else { - return 0; - } - } - - - /** - * Get the original framerate of the source video. Note: calling this method repeatedly - * can slow down playback performance. - * - * @return float - */ - protected float getSourceFrameRate() { - return (float)gplayer.getVideoSinkFrameRate(); - } - - /** * ( begin auto-generated from Movie_frameRate.xml ) * @@ -213,16 +163,19 @@ public class Movie extends PImage implements PConstants { * @param ifps speed of the movie in frames per second */ public void frameRate(float ifps) { + if (seeking) return; + // We calculate the target ratio in the case both the // current and target framerates are valid (greater than // zero), otherwise we leave it as 1. - float f = (0 < ifps && 0 < fps) ? ifps / fps : 1; + float f = (0 < ifps && 0 < frameRate) ? ifps / frameRate : 1; if (playing) { - gplayer.pause(); + playBin.pause(); + playBin.getState(); } - long t = gplayer.queryPosition(TimeUnit.NANOSECONDS); + long t = playBin.queryPosition(TimeUnit.NANOSECONDS); boolean res; long start, stop; @@ -234,20 +187,27 @@ public class Movie extends PImage implements PConstants { stop = t; } - res = gplayer.seek(rate * f, Format.TIME, SeekFlags.FLUSH, + res = playBin.seek(rate * f, Format.TIME, SeekFlags.FLUSH, SeekType.SET, start, SeekType.SET, stop); - + playBin.getState(); + if (!res) { PGraphics.showWarning("Seek operation failed."); } if (playing) { - gplayer.play(); + playBin.play(); } - fps = ifps; + frameRate = ifps; + + // getState() will wait until any async state change + // (like seek in this case) has completed + seeking = true; + playBin.getState(); + seeking = false; } - + /** * ( begin auto-generated from Movie_speed.xml ) @@ -271,7 +231,7 @@ public class Movie extends PImage implements PConstants { // to take care of that. if (PApplet.abs(rate - irate) > 0.1) { rate = irate; - frameRate(fps); // The framerate is the same, but the rate (speed) could be different. + frameRate(frameRate); // The framerate is the same, but the rate (speed) could be different. } } @@ -288,8 +248,8 @@ public class Movie extends PImage implements PConstants { * @usage web_application */ public float duration() { - float sec = gplayer.queryDuration().toSeconds(); - float nanosec = gplayer.queryDuration().getNanoSeconds(); + float sec = playBin.queryDuration().toSeconds(); + float nanosec = playBin.queryDuration().getNanoSeconds(); return sec + Video.nanoSecToSecFrac(nanosec); } @@ -306,8 +266,8 @@ public class Movie extends PImage implements PConstants { * @usage web_application */ public float time() { - float sec = gplayer.queryPosition().toSeconds(); - float nanosec = gplayer.queryPosition().getNanoSeconds(); + float sec = playBin.queryPosition().toSeconds(); + float nanosec = playBin.queryPosition().getNanoSeconds(); return sec + Video.nanoSecToSecFrac(nanosec); } @@ -335,7 +295,7 @@ public class Movie extends PImage implements PConstants { boolean res; long pos = Video.secToNanoLong(where); - res = gplayer.seek(1.0, Format.TIME, SeekFlags.FLUSH, + res = playBin.seek(1.0, Format.TIME, SeekFlags.FLUSH, SeekType.SET, pos, SeekType.NONE, -1); if (!res) { @@ -345,33 +305,11 @@ public class Movie extends PImage implements PConstants { // getState() will wait until any async state change // (like seek in this case) has completed seeking = true; - gplayer.getState(); + playBin.getState(); seeking = false; } - /** - * Returns true if the stream is already producing frames. - * - * @return boolean - */ - protected boolean ready() { - return 0 < bufWidth && 0 < bufHeight && sinkReady && !seeking; - } - - /** - * Returns true if its called for the first time after a new - * frame has been read, and false afterwards until another frame - * is read. - * - * @return boolean - */ - protected boolean newFrame() { - boolean res = newFrame; - newFrame = false; - return res; - } - /** * ( begin auto-generated from Movie_available.xml ) * @@ -386,33 +324,6 @@ public class Movie extends PImage implements PConstants { return available; } - /** - * Returns whether the movie is playing or not. - * - * @return boolean - */ - protected boolean isPlaying() { - return playing; - } - - /** - * Returns whether the movie is paused or not. If isPlaying() and isPaused() - * both return false it means that the movie is stopped. - * - * @return boolean - */ - protected boolean isPaused() { - return paused; - } - - /** - * Returns whether the movie is looping or not. - * - * @return boolean - */ - protected boolean isLooping() { - return repeat; - } /** * ( begin auto-generated from Movie_play.xml ) @@ -433,9 +344,11 @@ public class Movie extends PImage implements PConstants { playing = true; paused = false; - gplayer.play(); + playBin.play(); + playBin.getState(); } + /** * ( begin auto-generated from Movie_loop.xml ) * @@ -453,6 +366,7 @@ public class Movie extends PImage implements PConstants { play(); } + /** * ( begin auto-generated from Movie_noLoop.xml ) * @@ -474,6 +388,7 @@ public class Movie extends PImage implements PConstants { repeat = false; } + /** * ( begin auto-generated from Movie_pause.xml ) * @@ -494,9 +409,11 @@ public class Movie extends PImage implements PConstants { playing = false; paused = true; - gplayer.pause(); + playBin.pause(); + playBin.getState(); } + /** * ( begin auto-generated from Movie_stop.xml ) * @@ -520,9 +437,11 @@ public class Movie extends PImage implements PConstants { playing = false; } paused = false; - gplayer.stop(); + playBin.stop(); + playBin.getState(); } + /** * ( begin auto-generated from Movie_read.xml ) * @@ -534,10 +453,14 @@ public class Movie extends PImage implements PConstants { * @usage web_application */ public synchronized void read() { - if (fps <= 0) { + if (frameRate < 0) { // Framerate not set yet, so we obtain from stream, // which is already playing since we are in read(). - fps = getSourceFrameRate(); + frameRate = getSourceFrameRate(); + } + if (volume < 0) { + // Idem for volume + volume = (float)playBin.getVolume(); } if (useBufferSink) { // The native buffer from gstreamer is copied to the buffer sink. @@ -595,25 +518,99 @@ public class Movie extends PImage implements PConstants { * @param float v */ public void volume(float v) { - if (playing) { - gplayer.setVolume(v); + if (playing && PApplet.abs(volume - v) > 0.001f) { + playBin.setVolume(v); + volume = v; } } - - /** - * Prints all the gstreamer elements currently used in the - * current player instance. - * - */ - protected void printElements() { - List list = gplayer.getElementsRecursive(); - PApplet.println(list); - for (Element element : list) { - PApplet.println(element.toString()); - } + + //////////////////////////////////////////////////////////// + + // Initialization methods. + + + protected void initGStreamer(PApplet parent, String filename) { + this.parent = parent; + playBin = null; + + File file; + + Video.init(); + + // first check to see if this can be read locally from a file. + try { + try { + // first try a local file using the dataPath. usually this will + // work ok, but sometimes the dataPath is inside a jar file, + // which is less fun, so this will crap out. + file = new File(parent.dataPath(filename)); + if (file.exists()) { + playBin = new PlayBin2("Movie Player"); + playBin.setInputFile(file); + } + } catch (Exception e) { + } // ignored + + // read from a file just hanging out in the local folder. + // this might happen when the video library is used with some + // other application, or the person enters a full path name + if (playBin == null) { + try { + file = new File(filename); + if (file.exists()) { + playBin = new PlayBin2("Movie Player"); + playBin.setInputFile(file); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (playBin == null) { + // Try network read... + for (int i = 0; i < supportedProtocols.length; i++) { + if (filename.startsWith(supportedProtocols[i] + "://")) { + try { + playBin = new PlayBin2("Movie Player"); + playBin.setURI(URI.create(filename)); + break; + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } catch (SecurityException se) { + // online, whups. catch the security exception out here rather than + // doing it three times (or whatever) for each of the cases above. + } + + if (playBin == null) { + parent.die("Could not load movie file " + filename, null); + } + + // we've got a valid movie! let's rock. + try { + // PApplet.println("we've got a valid movie! let's rock."); + this.filename = filename; // for error messages + + // register methods + parent.registerDispose(this); + + setEventHandlerObject(parent); + + rate = 1.0f; + frameRate = -1; + volume = -1; + sinkReady = false; + bufWidth = bufHeight = 0; + } catch (Exception e) { + e.printStackTrace(); + } } + /** * Uses a generic object as handler of the movie. This object should have a * movieEvent method that receives a GSMovie argument. This method will @@ -629,86 +626,8 @@ public class Movie extends PImage implements PConstants { } catch (Exception e) { // no such method, or an error.. which is fine, just ignore } - } - - protected void initGStreamer(PApplet parent, String filename) { - this.parent = parent; - gplayer = null; - - File file; - - Video.init(); - - // first check to see if this can be read locally from a file. - try { - try { - // first try a local file using the dataPath. usually this will - // work ok, but sometimes the dataPath is inside a jar file, - // which is less fun, so this will crap out. - file = new File(parent.dataPath(filename)); - if (file.exists()) { - gplayer = new PlayBin2("Movie Player"); - gplayer.setInputFile(file); - } - } catch (Exception e) { - } // ignored - - // read from a file just hanging out in the local folder. - // this might happen when the video library is used with some - // other application, or the person enters a full path name - if (gplayer == null) { - try { - file = new File(filename); - if (file.exists()) { - gplayer = new PlayBin2("Movie Player"); - gplayer.setInputFile(file); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - if (gplayer == null) { - // Try network read... - for (int i = 0; i < supportedProtocols.length; i++) { - if (filename.startsWith(supportedProtocols[i] + "://")) { - try { - gplayer = new PlayBin2("Movie Player"); - gplayer.setURI(URI.create(filename)); - break; - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - } catch (SecurityException se) { - // online, whups. catch the security exception out here rather than - // doing it three times (or whatever) for each of the cases above. - } - - if (gplayer == null) { - parent.die("Could not load movie file " + filename, null); - } - - // we've got a valid movie! let's rock. - try { - // PApplet.println("we've got a valid movie! let's rock."); - this.filename = filename; // for error messages - - // register methods - parent.registerDispose(this); - - setEventHandlerObject(parent); - - rate = 1.0f; - fps = -1; - sinkReady = false; - bufWidth = bufHeight = 0; - } catch (Exception e) { - e.printStackTrace(); - } - } + } + protected void initSink() { if (bufferSink != null || (Video.useGLBufferSink && parent.g.isGL())) { @@ -730,7 +649,7 @@ public class Movie extends PImage implements PConstants { }); natSink.setAutoDisposeBuffer(false); - gplayer.setVideoSink(natSink); + playBin.setVideoSink(natSink); // The setVideoSink() method sets the videoSink as a property of the PlayBin, // which increments the refcount of the videoSink element. Disposing here once // to decrement the refcount. @@ -745,7 +664,7 @@ public class Movie extends PImage implements PConstants { // Setting direct buffer passing in the video sink. rgbSink.setPassDirectBuffer(Video.passDirectBuffer); - gplayer.setVideoSink(rgbSink); + playBin.setVideoSink(rgbSink); // The setVideoSink() method sets the videoSink as a property of the PlayBin, // which increments the refcount of the videoSink element. Disposing here once // to decrement the refcount. @@ -753,7 +672,7 @@ public class Movie extends PImage implements PConstants { } // Creating bus to handle end-of-stream event. - Bus bus = gplayer.getBus(); + Bus bus = playBin.getBus(); bus.connect(new Bus.EOS() { public void endOfStream(GstObject element) { eosEvent(); @@ -764,6 +683,12 @@ public class Movie extends PImage implements PConstants { newFrame = false; } + + //////////////////////////////////////////////////////////// + + // Stream event handling. + + protected synchronized void invokeEvent(int w, int h, IntBuffer buffer) { available = true; bufWidth = w; @@ -795,6 +720,7 @@ public class Movie extends PImage implements PConstants { } } + protected synchronized void invokeEvent(int w, int h, Buffer buffer) { available = true; bufWidth = w; @@ -815,6 +741,7 @@ public class Movie extends PImage implements PConstants { } } + protected void eosEvent() { if (repeat) { if (0 < rate) { @@ -825,26 +752,68 @@ public class Movie extends PImage implements PConstants { jump(duration()); } - // The rate is reset to 1 when restarting the stream, so - // we call frameRate to restart the rate. - frameRate(fps); + // The rate is set automatically to 1 when restarting the + // stream, so we need to call frameRate in order to reset + // to the latest fps rate. + frameRate(frameRate); } else { playing = false; } } + + //////////////////////////////////////////////////////////// + + // Stream query methods. + + + /** + * Get the height of the source video. Note: calling this method repeatedly + * can slow down playback performance. + * + * @return int + */ + protected int getSourceHeight() { + Dimension dim = playBin.getVideoSize(); + if (dim != null) { + return dim.height; + } else { + return 0; + } + } + + + /** + * Get the original framerate of the source video. Note: calling this method repeatedly + * can slow down playback performance. + * + * @return float + */ + protected float getSourceFrameRate() { + return (float)playBin.getVideoSinkFrameRate(); + } + + + /** + * Get the width of the source video. Note: calling this method repeatedly + * can slow down playback performance. + * + * @return int + */ + protected int getSourceWidth() { + Dimension dim = playBin.getVideoSize(); + if (dim != null) { + return dim.width; + } else { + return 0; + } + } + + //////////////////////////////////////////////////////////// // Buffer source interface. - - /** - * Disables automatic use of hardware acceleration to play video for OpenGL-based - * renderers. - * - */ -// public void noGL() { -// useGLSink = false; -// } + /** * Sets the object to use as destination for the frames read from the stream. @@ -858,6 +827,7 @@ public class Movie extends PImage implements PConstants { initCopyMask(); } + /** * Sets the object to use as destination for the frames read from the stream. * @@ -869,14 +839,17 @@ public class Movie extends PImage implements PConstants { copyMask = mask; } + public boolean hasBufferSink() { return bufferSink != null; } + public synchronized void disposeBuffer(Object buf) { ((Buffer)buf).dispose(); } + protected void getSinkMethods() { try { sinkCopyMethod = bufferSink.getClass().getMethod("copyBufferFromSource", @@ -893,6 +866,7 @@ public class Movie extends PImage implements PConstants { } } + protected void initCopyMask() { if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) { copyMask = "red_mask=(int)0xFF000000, green_mask=(int)0xFF0000, blue_mask=(int)0xFF00"; diff --git a/java/libraries/video/src/processing/video/Video.java b/java/libraries/video/src/processing/video/Video.java index bd9173769..153ad03f2 100644 --- a/java/libraries/video/src/processing/video/Video.java +++ b/java/libraries/video/src/processing/video/Video.java @@ -37,11 +37,6 @@ import java.util.List; * this library. */ public class Video implements PConstants { - // Streams type constants. - static public final int AUDIO = 0; - static public final int VIDEO = 1; - static public final int RAW = 2; - // Priority is given to the system install of GStreamer if this is set to true. public static boolean systemGStreamer = false; public static String systemGStreamerPath;