From 8ae1831c338adc9e22a54fb588b2fa8c762e481b Mon Sep 17 00:00:00 2001 From: benfry Date: Sat, 1 Sep 2012 23:54:32 +0000 Subject: [PATCH] swap how image cache is handled, and use weak reference --- core/src/processing/core/PApplet.java | 156 ++-- core/src/processing/core/PGraphics.java | 53 ++ core/src/processing/core/PGraphicsJava2D.java | 8 +- core/src/processing/core/PImage.java | 154 ++-- core/src/processing/opengl/PFontTexture.java | 46 +- .../processing/opengl/PGraphicsOpenGL.java | 76 +- core/src/processing/opengl/Texture.java | 4 +- core/todo.txt | 46 +- .../video/src/processing/video/Capture.java | 684 +++++++++--------- .../video/src/processing/video/Movie.java | 584 +++++++-------- 10 files changed, 951 insertions(+), 860 deletions(-) diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 867f4c96f..3d82b5ef3 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -1695,13 +1695,18 @@ public class PApplet extends Applet * @see PGraphics#PGraphics */ public PImage createImage(int w, int h, int format) { - return createImage(w, h, format, null); + PImage image = new PImage(w, h, format); + image.parent = this; // make save() work + return image; } - /** - * @nowebref - */ + /* + public PImage createImage(int w, int h, int format) { + return createImage(w, h, format, null); + } + + // unapproved public PImage createImage(int w, int h, int format, Object params) { PImage image = new PImage(w, h, format); if (params != null) { @@ -1710,6 +1715,7 @@ public class PApplet extends Applet image.parent = this; // make save() work return image; } + */ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -4841,27 +4847,28 @@ public class PApplet extends Applet * @see PGraphics#background(float, float, float, float) */ public PImage loadImage(String filename) { - return loadImage(filename, null, null); +// return loadImage(filename, null, null); + return loadImage(filename, null); } - /** - * @param extension the type of image to load, for example "png", "gif", "jpg" - */ - public PImage loadImage(String filename, String extension) { - return loadImage(filename, extension, null); - } +// /** +// * @param extension the type of image to load, for example "png", "gif", "jpg" +// */ +// public PImage loadImage(String filename, String extension) { +// return loadImage(filename, extension, null); +// } + +// /** +// * @nowebref +// */ +// public PImage loadImage(String filename, Object params) { +// return loadImage(filename, null, params); +// } /** * @nowebref */ - public PImage loadImage(String filename, Object params) { - return loadImage(filename, null, params); - } - - /** - * @nowebref - */ - public PImage loadImage(String filename, String extension, Object params) { + public PImage loadImage(String filename, String extension) { //, Object params) { if (extension == null) { String lower = filename.toLowerCase(); int dot = filename.lastIndexOf('.'); @@ -4884,9 +4891,9 @@ public class PApplet extends Applet if (extension.equals("tga")) { try { PImage image = loadImageTGA(filename); - if (params != null) { - image.setParams(g, params); - } +// if (params != null) { +// image.setParams(g, params); +// } return image; } catch (IOException e) { e.printStackTrace(); @@ -4897,9 +4904,9 @@ public class PApplet extends Applet if (extension.equals("tif") || extension.equals("tiff")) { byte bytes[] = loadBytes(filename); PImage image = (bytes == null) ? null : PImage.loadTIFF(bytes); - if (params != null) { - image.setParams(g, params); - } +// if (params != null) { +// image.setParams(g, params); +// } return image; } @@ -4925,9 +4932,9 @@ public class PApplet extends Applet image.checkAlpha(); } - if (params != null) { - image.setParams(g, params); - } +// if (params != null) { +// image.setParams(g, params); +// } return image; } } @@ -4944,9 +4951,9 @@ public class PApplet extends Applet if (extension.equals(loadImageFormats[i])) { PImage image; image = loadImageIO(filename); - if (params != null) { - image.setParams(g, params); - } +// if (params != null) { +// image.setParams(g, params); +// } return image; } } @@ -4957,10 +4964,13 @@ public class PApplet extends Applet return null; } + public PImage requestImage(String filename) { - return requestImage(filename, null, null); +// return requestImage(filename, null, null); + return requestImage(filename, null); } + /** * ( begin auto-generated from requestImage.xml ) * @@ -4984,15 +4994,7 @@ public class PApplet extends Applet * @see PImage#PImage */ public PImage requestImage(String filename, String extension) { - return requestImage(filename, extension, null); - } - - - /** - * @nowebref - */ - public PImage requestImage(String filename, String extension, Object params) { - PImage vessel = createImage(0, 0, ARGB, params); + PImage vessel = createImage(0, 0, ARGB); AsyncImageLoader ail = new AsyncImageLoader(filename, extension, vessel); ail.start(); @@ -5000,6 +5002,18 @@ public class PApplet extends Applet } +// /** +// * @nowebref +// */ +// public PImage requestImage(String filename, String extension, Object params) { +// PImage vessel = createImage(0, 0, ARGB, params); +// AsyncImageLoader ail = +// new AsyncImageLoader(filename, extension, vessel); +// ail.start(); +// return vessel; +// } + + /** * By trial and error, four image loading threads seem to work best when * loading images from online. This is consistent with the number of open @@ -14040,36 +14054,36 @@ public class PApplet extends Applet } - /** - * Store parameters for a renderer that requires extra metadata of - * some kind. - * @param renderer The PGraphics renderer associated to the image - * @param storage The parameters required by the renderer - */ - public void setParams(PGraphics renderer, Object params) { - if (recorder != null) recorder.setParams(renderer, params); - g.setParams(renderer, params); - } - - - /** - * Get the parameters for the specified renderer. - * @param renderer The PGraphics renderer associated to the image - * @return parameters stored for the specified renderer - */ - public Object getParams(PGraphics renderer) { - return g.getParams(renderer); - } - - - /** - * Remove information associated with this renderer from the cache, if any. - * @param renderer The PGraphics renderer whose parameters should be removed - */ - public void removeParams(PGraphics renderer) { - if (recorder != null) recorder.removeParams(renderer); - g.removeParams(renderer); - } +// /** +// * Store parameters for a renderer that requires extra metadata of +// * some kind. +// * @param renderer The PGraphics renderer associated to the image +// * @param storage The parameters required by the renderer +// */ +// public void setParams(PGraphics renderer, Object params) { +// if (recorder != null) recorder.setParams(renderer, params); +// g.setParams(renderer, params); +// } +// +// +// /** +// * Get the parameters for the specified renderer. +// * @param renderer The PGraphics renderer associated to the image +// * @return parameters stored for the specified renderer +// */ +// public Object getParams(PGraphics renderer) { +// return g.getParams(renderer); +// } +// +// +// /** +// * Remove information associated with this renderer from the cache, if any. +// * @param renderer The PGraphics renderer whose parameters should be removed +// */ +// public void removeParams(PGraphics renderer) { +// if (recorder != null) recorder.removeParams(renderer); +// g.removeParams(renderer); +// } /** diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index 8cead17c3..a861a44f5 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -26,6 +26,7 @@ package processing.core; import java.awt.*; import java.util.HashMap; +import java.util.WeakHashMap; import processing.opengl.PGL; import processing.opengl.PShader; @@ -196,6 +197,17 @@ public class PGraphics extends PImage implements PConstants { */ protected boolean[] hints = new boolean[HINT_COUNT]; + // ........................................................ + + /** + * Storage for renderer-specific image data. In 1.x, renderers wrote cache + * data into the image object. In 2.x, the renderer has a weak-referenced + * map that points at any of the images it has worked on already. When the + * images go out of scope, they will be properly garbage collected. + */ + protected WeakHashMap cacheMap = + new WeakHashMap(); + //////////////////////////////////////////////////////////// @@ -746,6 +758,47 @@ public class PGraphics extends PImage implements PConstants { + ////////////////////////////////////////////////////////////// + + // IMAGE METADATA FOR THIS RENDERER + + /** + * Store data of some kind for the renderer that requires extra metadata of + * some kind. Usually this is a renderer-specific representation of the + * image data, for instance a BufferedImage with tint() settings applied for + * PGraphicsJava2D, or resized image data and OpenGL texture indices for + * PGraphicsOpenGL. + * @param renderer The PGraphics renderer associated to the image + * @param storage The metadata required by the renderer + */ + public void setCache(PImage image, Object storage) { + cacheMap.put(image, storage); + } + + + /** + * Get cache storage data for the specified renderer. Because each renderer + * will cache data in different formats, it's necessary to store cache data + * keyed by the renderer object. Otherwise, attempting to draw the same + * image to both a PGraphicsJava2D and a PGraphicsOpenGL will cause errors. + * @param renderer The PGraphics renderer associated to the image + * @return metadata stored for the specified renderer + */ + public Object getCache(PImage image) { + return cacheMap.get(image); + } + + + /** + * Remove information associated with this renderer from the cache, if any. + * @param renderer The PGraphics renderer whose cache data should be removed + */ + public void removeCache(PImage image) { + cacheMap.remove(image); + } + + + ////////////////////////////////////////////////////////////// // FRAME diff --git a/core/src/processing/core/PGraphicsJava2D.java b/core/src/processing/core/PGraphicsJava2D.java index 6c8d874dd..b46d635b4 100644 --- a/core/src/processing/core/PGraphicsJava2D.java +++ b/core/src/processing/core/PGraphicsJava2D.java @@ -943,14 +943,14 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { // Image not ready yet, or an error if (who.width <= 0 || who.height <= 0) return; - if (who.getCache(this) == null) { + if (getCache(who) == null) { //System.out.println("making new image cache"); - who.setCache(this, new ImageCache(who)); + this.setCache(who, new ImageCache(who)); who.updatePixels(); // mark the whole thing for update who.modified = true; } - ImageCache cash = (ImageCache) who.getCache(this); + ImageCache cash = (ImageCache) getCache(who); // if image previously was tinted, or the color changed // or the image was tinted, and tint is now disabled if ((tint && !cash.tinted) || @@ -965,7 +965,7 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { who.modified = false; } - g2.drawImage(((ImageCache) who.getCache(this)).image, + g2.drawImage(((ImageCache) getCache(who)).image, (int) x1, (int) y1, (int) x2, (int) y2, u1, v1, u2, v2, null); diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index b201a9f4c..fd507b553 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -27,7 +27,6 @@ package processing.core; import java.awt.image.*; import java.io.*; import java.lang.reflect.Method; -import java.util.HashMap; import javax.imageio.ImageIO; @@ -126,10 +125,11 @@ public class PImage implements PConstants, Cloneable { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . /** for renderers that need to store info about the image */ - protected HashMap cacheMap; + //protected HashMap cacheMap; +// protected WeakHashMap cacheMap; /** for renderers that need to store parameters about the image */ - protected HashMap paramMap; +// protected HashMap paramMap; /** modified portion of the image */ protected boolean modified; @@ -305,82 +305,82 @@ public class PImage implements PConstants, Cloneable { } - ////////////////////////////////////////////////////////////// - - // METADATA/PARAMETERS REQUIRED BY RENDERERS - - /** - * Store data of some kind for a renderer that requires extra metadata of - * some kind. Usually this is a renderer-specific representation of the - * image data, for instance a BufferedImage with tint() settings applied for - * PGraphicsJava2D, or resized image data and OpenGL texture indices for - * PGraphicsOpenGL. - * @param renderer The PGraphics renderer associated to the image - * @param storage The metadata required by the renderer - */ - public void setCache(PGraphics renderer, Object storage) { - if (cacheMap == null) cacheMap = new HashMap(); - cacheMap.put(renderer, storage); - } +// ////////////////////////////////////////////////////////////// +// +// // METADATA/PARAMETERS REQUIRED BY RENDERERS +// +// /** +// * Store data of some kind for a renderer that requires extra metadata of +// * some kind. Usually this is a renderer-specific representation of the +// * image data, for instance a BufferedImage with tint() settings applied for +// * PGraphicsJava2D, or resized image data and OpenGL texture indices for +// * PGraphicsOpenGL. +// * @param renderer The PGraphics renderer associated to the image +// * @param storage The metadata required by the renderer +// */ +// public void setCache(PGraphics renderer, Object storage) { +// if (cacheMap == null) cacheMap = new WeakHashMap(); +// cacheMap.put(renderer, storage); +// } +// +// +// /** +// * Get cache storage data for the specified renderer. Because each renderer +// * will cache data in different formats, it's necessary to store cache data +// * keyed by the renderer object. Otherwise, attempting to draw the same +// * image to both a PGraphicsJava2D and a PGraphicsOpenGL will cause errors. +// * @param renderer The PGraphics renderer associated to the image +// * @return metadata stored for the specified renderer +// */ +// public Object getCache(PGraphics renderer) { +// if (cacheMap == null) return null; +// return cacheMap.get(renderer); +// } +// +// +// /** +// * Remove information associated with this renderer from the cache, if any. +// * @param renderer The PGraphics renderer whose cache data should be removed +// */ +// public void removeCache(PGraphics renderer) { +// if (cacheMap != null) { +// cacheMap.remove(renderer); +// } +// } - /** - * Get cache storage data for the specified renderer. Because each renderer - * will cache data in different formats, it's necessary to store cache data - * keyed by the renderer object. Otherwise, attempting to draw the same - * image to both a PGraphicsJava2D and a PGraphicsOpenGL will cause errors. - * @param renderer The PGraphics renderer associated to the image - * @return metadata stored for the specified renderer - */ - public Object getCache(PGraphics renderer) { - if (cacheMap == null) return null; - return cacheMap.get(renderer); - } - - - /** - * Remove information associated with this renderer from the cache, if any. - * @param renderer The PGraphics renderer whose cache data should be removed - */ - public void removeCache(PGraphics renderer) { - if (cacheMap != null) { - cacheMap.remove(renderer); - } - } - - - /** - * Store parameters for a renderer that requires extra metadata of - * some kind. - * @param renderer The PGraphics renderer associated to the image - * @param storage The parameters required by the renderer - */ - public void setParams(PGraphics renderer, Object params) { - if (paramMap == null) paramMap = new HashMap(); - paramMap.put(renderer, params); - } - - - /** - * Get the parameters for the specified renderer. - * @param renderer The PGraphics renderer associated to the image - * @return parameters stored for the specified renderer - */ - public Object getParams(PGraphics renderer) { - if (paramMap == null) return null; - return paramMap.get(renderer); - } - - - /** - * Remove information associated with this renderer from the cache, if any. - * @param renderer The PGraphics renderer whose parameters should be removed - */ - public void removeParams(PGraphics renderer) { - if (paramMap != null) { - paramMap.remove(renderer); - } - } +// /** +// * Store parameters for a renderer that requires extra metadata of +// * some kind. +// * @param renderer The PGraphics renderer associated to the image +// * @param storage The parameters required by the renderer +// */ +// public void setParams(PGraphics renderer, Object params) { +// if (paramMap == null) paramMap = new HashMap(); +// paramMap.put(renderer, params); +// } +// +// +// /** +// * Get the parameters for the specified renderer. +// * @param renderer The PGraphics renderer associated to the image +// * @return parameters stored for the specified renderer +// */ +// public Object getParams(PGraphics renderer) { +// if (paramMap == null) return null; +// return paramMap.get(renderer); +// } +// +// +// /** +// * Remove information associated with this renderer from the cache, if any. +// * @param renderer The PGraphics renderer whose parameters should be removed +// */ +// public void removeParams(PGraphics renderer) { +// if (paramMap != null) { +// paramMap.remove(renderer); +// } +// } ////////////////////////////////////////////////////////////// diff --git a/core/src/processing/opengl/PFontTexture.java b/core/src/processing/opengl/PFontTexture.java index 14c8c0b96..a59e4a9e1 100644 --- a/core/src/processing/opengl/PFontTexture.java +++ b/core/src/processing/opengl/PFontTexture.java @@ -65,7 +65,7 @@ class PFontTexture implements PConstants { protected TextureInfo[] glyphTexinfos; protected HashMap texinfoMap; - public PFontTexture(PApplet parent, PFont font, int maxw, int maxh, + public PFontTexture(PApplet parent, PFont font, int maxw, int maxh, boolean is3D) { this.parent = parent; this.font = font; @@ -113,23 +113,23 @@ class PFontTexture implements PConstants { h = PApplet.min(2 * textures[currentTex].glHeight, maxTexHeight); resize = true; } else { - h = PApplet.min(PGraphicsOpenGL.maxTextureSize, PGL.MAX_FONT_TEX_SIZE / 2, + h = PApplet.min(PGraphicsOpenGL.maxTextureSize, PGL.MAX_FONT_TEX_SIZE / 2, maxTexHeight / 4); resize = false; } Texture tex; if (is3D) { - // Bilinear sampling ensures that the texture doesn't look pixelated + // Bilinear sampling ensures that the texture doesn't look pixelated // either when it is magnified or minified... - tex = new Texture(parent, w, h, + tex = new Texture(parent, w, h, new Texture.Parameters(ARGB, Texture.BILINEAR, false)); } else { - // ...however, the effect of bilinear sampling is to add some blurriness - // to the text in its original size. In 2D, we assume that text will be - // shown at its original size, so linear sampling is chosen instead (which + // ...however, the effect of bilinear sampling is to add some blurriness + // to the text in its original size. In 2D, we assume that text will be + // shown at its original size, so linear sampling is chosen instead (which // only affects minimized text). - tex = new Texture(parent, w, h, + tex = new Texture(parent, w, h, new Texture.Parameters(ARGB, Texture.LINEAR, false)); } @@ -147,7 +147,7 @@ class PFontTexture implements PConstants { tex.put(tex0); textures[currentTex] = tex; - images[currentTex].setCache(pg, tex); + pg.setCache(images[currentTex], tex); images[currentTex].width = tex.width; images[currentTex].height = tex.height; } else { @@ -176,13 +176,13 @@ class PFontTexture implements PConstants { setTexture(0); } - + public void end() { for (int i = 0; i < textures.length; i++) { pgl.disableTexturing(textures[i].glTarget); } } - + public void setTexture(int idx) { if (0 <= idx && idx < textures.length) { @@ -258,18 +258,18 @@ class PFontTexture implements PConstants { // Adds this glyph to the opengl texture in PFont. protected void addToTexture(int idx, PFont.Glyph glyph) { - // We add one pixel to avoid issues when sampling the font texture at - // fractional screen positions. I.e.: the pixel on the screen only contains - // half of the font rectangle, so it would sample half of the color from the - // glyph area in the texture, and the other half from the contiguous pixel. - // If the later contains a portion of the neighbor glyph and the former - // doesn't, this would result in a shaded pixel when the correct output is - // blank. This is a consequence of putting all the glyphs in a common + // We add one pixel to avoid issues when sampling the font texture at + // fractional screen positions. I.e.: the pixel on the screen only contains + // half of the font rectangle, so it would sample half of the color from the + // glyph area in the texture, and the other half from the contiguous pixel. + // If the later contains a portion of the neighbor glyph and the former + // doesn't, this would result in a shaded pixel when the correct output is + // blank. This is a consequence of putting all the glyphs in a common // texture with bilinear sampling. int w = 1 + glyph.width + 1; int h = 1 + glyph.height + 1; - // Converting the pixels array from the PImage into a valid RGBA array for + // Converting the pixels array from the PImage into a valid RGBA array for // OpenGL. int[] rgba = new int[w * h]; int t = 0; @@ -332,7 +332,7 @@ class PFontTexture implements PConstants { currentTex = idx; } - TextureInfo tinfo = new TextureInfo(currentTex, offsetX, offsetY, + TextureInfo tinfo = new TextureInfo(currentTex, offsetX, offsetY, w, h, rgba); offsetX += w; @@ -356,12 +356,12 @@ class PFontTexture implements PConstants { public float v0, v1; public int[] pixels; - public TextureInfo(int tidx, int cropX, int cropY, int cropW, int cropH, + public TextureInfo(int tidx, int cropX, int cropY, int cropW, int cropH, int[] pix) { texIndex = tidx; crop = new int[4]; // The region of the texture corresponding to the glyph is surrounded by a - // 1-pixel wide border to avoid artifacts due to bilinear sampling. This + // 1-pixel wide border to avoid artifacts due to bilinear sampling. This // is why the additions and subtractions to the crop values. crop[0] = cropX + 1; crop[1] = cropY + 1 + cropH - 2; @@ -384,7 +384,7 @@ class PFontTexture implements PConstants { void updateTex() { - textures[texIndex].setNative(pixels, crop[0] - 1, crop[1] + crop[3] - 1, + textures[texIndex].setNative(pixels, crop[0] - 1, crop[1] + crop[3] - 1, crop[2] + 2, -crop[3] + 2); } } diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java index 5017e922b..ed835e272 100644 --- a/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/core/src/processing/opengl/PGraphicsOpenGL.java @@ -22,24 +22,12 @@ package processing.opengl; -import processing.core.PApplet; -import processing.core.PFont; -import processing.core.PGraphics; -import processing.core.PImage; -import processing.core.PMatrix; -import processing.core.PMatrix2D; -import processing.core.PMatrix3D; -import processing.core.PShape; -import processing.core.PVector; +import processing.core.*; import java.net.URL; import java.nio.*; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.*; +import java.util.regex.*; /** * OpenGL renderer. @@ -55,6 +43,10 @@ public class PGraphicsOpenGL extends PGraphics { /** The renderer currently in use. */ protected static PGraphicsOpenGL pgCurrent = null; + /** Additional image parameters not covered by the cache. */ + protected WeakHashMap paramMap = + new WeakHashMap(); + // ........................................................ // Basic rendering parameters: @@ -618,6 +610,38 @@ public class PGraphicsOpenGL extends PGraphics { } + ////////////////////////////////////////////////////////////// + + /** + * Store parameters for a renderer that requires extra metadata of + * some kind. + * @param renderer The PGraphics renderer associated to the image + * @param storage The parameters required by the renderer + */ + public void setParams(PImage image, Object params) { + paramMap.put(image, params); + } + + + /** + * Get the parameters for the specified renderer. + * @param renderer The PGraphics renderer associated to the image + * @return parameters stored for the specified renderer + */ + public Object getParams(PImage image) { + return paramMap.get(image); + } + + + /** + * Remove information associated with this renderer from the cache, if any. + * @param renderer The PGraphics renderer whose parameters should be removed + */ + public void removeParams(PImage image) { + paramMap.remove(image); + } + + ////////////////////////////////////////////////////////////// // RESOURCE HANDLING @@ -1631,7 +1655,7 @@ public class PGraphicsOpenGL extends PGraphics { if (texture != null) { // The screen texture should be deleted because it // corresponds to the old window size. - this.removeCache(pgPrimary); + pgPrimary.removeCache(this); this.removeParams(pgPrimary); texture = null; loadTexture(); @@ -5443,8 +5467,8 @@ public class PGraphicsOpenGL extends PGraphics { return; } - if (texture == null || texture != img.getCache(pgPrimary)) { - Texture tex = (Texture)img.getCache(pgPrimary); + if (texture == null || texture != pgPrimary.getCache(img)) { + Texture tex = (Texture)pgPrimary.getCache(img); Texture.Parameters params = tex != null ? tex.getParameters() : null; if (tex == null || tex.contextIsOutdated() || !validSurfaceTex(tex)) { if (primarySurface) { @@ -5457,8 +5481,8 @@ public class PGraphicsOpenGL extends PGraphics { if (tex != null) { texture = tex; texture.setFlippedY(true); - this.setCache(pgPrimary, texture); - this.setParams(pgPrimary, params); + pgPrimary.setCache(this, texture); + pgPrimary.setParams(this, params); if (!primarySurface && offscreenFramebuffer != null) { // Attach as the color buffer for this offscreen surface @@ -5883,7 +5907,7 @@ public class PGraphicsOpenGL extends PGraphics { @Override public Object initCache(PImage img) { - Texture tex = (Texture)img.getCache(pgPrimary); + Texture tex = (Texture)pgPrimary.getCache(img); if (tex == null || tex.contextIsOutdated()) { tex = addTexture(img); if (tex != null) { @@ -5901,7 +5925,7 @@ public class PGraphicsOpenGL extends PGraphics { * @param img the image to have a texture metadata associated to it */ protected Texture addTexture(PImage img) { - Texture.Parameters params = (Texture.Parameters)img.getParams(pgPrimary); + Texture.Parameters params = (Texture.Parameters)pgPrimary.getParams(img); if (params == null) { params = new Texture.Parameters(); if (hints[DISABLE_TEXTURE_MIPMAPS]) { @@ -5929,8 +5953,8 @@ public class PGraphicsOpenGL extends PGraphics { img.parent = parent; } Texture tex = new Texture(img.parent, img.width, img.height, params); - img.setCache(pgPrimary, tex); - img.setParams(pgPrimary, params); + pgPrimary.setCache(img, tex); + pgPrimary.setParams(img, params); return tex; } @@ -5963,8 +5987,8 @@ public class PGraphicsOpenGL extends PGraphics { img.width = tex.width; img.height = tex.height; img.format = ARGB; - img.setCache(pgPrimary, tex); - img.setParams(pgPrimary, tex.getParameters()); + pgPrimary.setCache(img, tex); + pgPrimary.setParams(img, tex.getParameters()); return img; } diff --git a/core/src/processing/opengl/Texture.java b/core/src/processing/opengl/Texture.java index 7a055fe9b..e604563bb 100644 --- a/core/src/processing/opengl/Texture.java +++ b/core/src/processing/opengl/Texture.java @@ -230,13 +230,13 @@ public class Texture implements PConstants { public void set(PImage img) { - Texture tex = (Texture)img.getCache(pg); + Texture tex = (Texture)pg.getCache(img); set(tex); } public void set(PImage img, int x, int y, int w, int h) { - Texture tex = (Texture)img.getCache(pg); + Texture tex = (Texture)pg.getCache(img); set(tex, x, y, w, h); } diff --git a/core/todo.txt b/core/todo.txt index 889827d2d..2ccb168dd 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -5,6 +5,29 @@ X maybe getResource() fails because spaces are not encoded? X PVector limit() is inefficient X http://code.google.com/p/processing/issues/detail?id=1122 +_ memory leak when many createGraphics(..., JAVA2D) calls are used +_ http://code.google.com/p/processing/issues/detail?id=507 +_ need to add clear() method so clear out ARGB surfaces +C clear() ok to add +X decided clear(r, g, b, a) problematic since it's an exception to color model +X and that people should use background() anyway +o or background(r, g, b, a) would be the thing +_ clear() with a color doesn't make (verbal) sense +_ provide a way to clear the PGraphics with plain alpha + +api to be fixed/removed +_ remove PImage.delete() and friends from PImage, Movie, etc. +_ delete()/dispose() being used in the movie +_ buffer sink methods in movie +_ 'newFrame' is 'available', and ready() is part of that + +_ loadShape() mess +_ get things out of PGraphics +_ separate PShape2/3D from OpenGL +_ incorporate SVG loader into the 2D class + +_ loadPixels() implementation + _ Jagged / Glitchy JAVA2D shape strokes in Java 1.6 _ g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, _ RenderingHints.VALUE_STROKE_PURE); @@ -47,19 +70,6 @@ void iconLoaded(PImage image, String filename) { } -api to be fixed/removed -_ remove PImage.delete() and friends from PImage, Movie, etc. -_ delete()/dispose() being used in the movie -_ buffer sink methods in movie -_ 'newFrame' is 'available', and ready() is part of that - -_ loadShape() mess -_ get things out of PGraphics -_ separate PShape2/3D from OpenGL -_ incorporate SVG loader into the 2D class - -_ loadPixels() implementation - _ standardize PGL behavior PGL pgl = beginPGL(); pgl.vertex3f(...) @@ -134,16 +144,6 @@ _ maybe catch RuntimeExceptions in the called sub-functions _ that way more stuff can be static w/o losing useful error handling _ (emitting errors when closest to source.. i.e. w/ the filename) -_ memory leak when many createGraphics(..., JAVA2D) calls are used -_ http://code.google.com/p/processing/issues/detail?id=507 -_ need to add clear() method so clear out ARGB surfaces -C clear() ok to add -X decided clear(r, g, b, a) problematic since it's an exception to color model -X and that people should use background() anyway -o or background(r, g, b, a) would be the thing -_ clear() with a color doesn't make (verbal) sense -_ provide a way to clear the PGraphics with plain alpha - _ sort out edge + 1 issue on stroke/fill for rectangles _ http://code.google.com/p/processing/issues/detail?id=509 _ exactly how should pixel filling work with single pixel strokes? diff --git a/java/libraries/video/src/processing/video/Capture.java b/java/libraries/video/src/processing/video/Capture.java index b3464312d..e14a4c4dc 100644 --- a/java/libraries/video/src/processing/video/Capture.java +++ b/java/libraries/video/src/processing/video/Capture.java @@ -39,22 +39,22 @@ import org.gstreamer.interfaces.Property; /** * ( begin auto-generated from Capture.xml ) - * - * Datatype for storing and manipulating video frames from an attached - * capture device such as a camera. Use Capture.list() to show the - * names of any attached devices. Using the version of the constructor - * without name will attempt to use the last device used by a + * + * Datatype for storing and manipulating video frames from an attached + * capture device such as a camera. Use Capture.list() to show the + * names of any attached devices. Using the version of the constructor + * without name will attempt to use the last device used by a * QuickTime program. - * + * * ( end auto-generated ) - * + * *

Advanced

* Class for storing and manipulating video frames from an attached capture * device such as a camera. * @webref video * @usage application */ -public class Capture extends PImage implements PConstants { +public class Capture extends PImage implements PConstants { protected static String sourceElementName; protected static String devicePropertyName; protected static String indexPropertyName; @@ -62,7 +62,7 @@ public class Capture extends PImage implements PConstants { static { if (PApplet.platform == MACOSX) { sourceElementName = "qtkitvideosrc"; - devicePropertyName = "device-name"; + devicePropertyName = "device-name"; indexPropertyName = "device-index"; } else if (PApplet.platform == WINDOWS) { sourceElementName = "ksvideosrc"; @@ -70,55 +70,55 @@ public class Capture extends PImage implements PConstants { indexPropertyName = "device-index"; } else if (PApplet.platform == LINUX) { sourceElementName = "v4l2src"; - // The "device" property in v4l2src expects the device location - // (/dev/video0, etc). v4l2src has "device-name", which requires the + // The "device" property in v4l2src expects the device location + // (/dev/video0, etc). v4l2src has "device-name", which requires the // human-readable name... but how to query in linux?. devicePropertyName = "device"; indexPropertyName = "device-fd"; } else {} } protected static boolean useResMacHack = true; - + public float frameRate; - public Pipeline pipeline; - + public Pipeline pipeline; + protected boolean capturing = false; - - protected String frameRateString; + + protected String frameRateString; protected int bufWidth; protected int bufHeight; - + protected String sourceName; protected Element sourceElement; - + protected Method captureEventMethod; protected Object eventHandler; - + protected boolean available; protected boolean pipelineReady; protected boolean newFrame; protected RGBDataAppSink rgbSink = null; protected int[] copyPixels = null; - + protected boolean firstFrame = true; - + protected int reqWidth; - protected int reqHeight; - - protected boolean useBufferSink = false; + protected int reqHeight; + + protected boolean useBufferSink = false; protected Object bufferSink; protected Method sinkCopyMethod; protected Method sinkSetMethod; - protected String copyMask; + protected String copyMask; protected Buffer natBuffer = null; - protected BufferDataAppSink natSink = null; - - + protected BufferDataAppSink natSink = null; + + public Capture(PApplet parent) { String[] configs = Capture.list(); if (configs.length == 0) { - throw new RuntimeException("There are no cameras available for capture"); + throw new RuntimeException("There are no cameras available for capture"); } String name = getName(configs[0]); int[] size = getSize(configs[0]); @@ -126,7 +126,7 @@ public class Capture extends PImage implements PConstants { String idName; Object idValue; if (devicePropertyName.equals("")) { - // For plugins without device name property, the name is casted + // For plugins without device name property, the name is casted // as an index idName = indexPropertyName; idValue = new Integer(PApplet.parseInt(name)); @@ -134,11 +134,11 @@ public class Capture extends PImage implements PConstants { idName = devicePropertyName; idValue = name; } - initGStreamer(parent, size[0], size[1], sourceElementName, - idName, idValue, fps); + initGStreamer(parent, size[0], size[1], sourceElementName, + idName, idValue, fps); } - - + + public Capture(PApplet parent, String requestConfig) { String name = getName(requestConfig); int[] size = getSize(requestConfig); @@ -146,7 +146,7 @@ public class Capture extends PImage implements PConstants { String idName; Object idValue; if (devicePropertyName.equals("")) { - // For plugins without device name property, the name is casted + // For plugins without device name property, the name is casted // as an index idName = indexPropertyName; idValue = new Integer(PApplet.parseInt(name)); @@ -154,11 +154,11 @@ public class Capture extends PImage implements PConstants { idName = devicePropertyName; idValue = name; } - initGStreamer(parent, size[0], size[1], sourceElementName, + initGStreamer(parent, size[0], size[1], sourceElementName, idName, idValue, fps); } - + /** * @param parent typically use "this" * @param requestWidth width of the frame @@ -166,38 +166,38 @@ public class Capture extends PImage implements PConstants { */ public Capture(PApplet parent, int requestWidth, int requestHeight) { super(requestWidth, requestHeight, RGB); - initGStreamer(parent, requestWidth, requestHeight, sourceElementName, + initGStreamer(parent, requestWidth, requestHeight, sourceElementName, null, null, ""); } - + /** *

Advanced

* Constructor that takes resolution and framerate. - * + * * @param frameRate number of frames to read per second - */ - public Capture(PApplet parent, int requestWidth, int requestHeight, + */ + public Capture(PApplet parent, int requestWidth, int requestHeight, int frameRate) { super(requestWidth, requestHeight, RGB); - initGStreamer(parent, requestWidth, requestHeight, sourceElementName, + initGStreamer(parent, requestWidth, requestHeight, sourceElementName, null, null, frameRate + "/1"); } - + /** *

Advanced

* This constructor allows to specify resolution and camera name. - * + * * @param cameraName name of the camera - */ - public Capture(PApplet parent, int requestWidth, int requestHeight, + */ + public Capture(PApplet parent, int requestWidth, int requestHeight, String cameraName) { super(requestWidth, requestHeight, RGB); String idName; Object idValue; if (devicePropertyName.equals("")) { - // For plugins without device name property, the name is casted + // For plugins without device name property, the name is casted // as an index idName = indexPropertyName; idValue = new Integer(PApplet.parseInt(cameraName)); @@ -205,23 +205,23 @@ public class Capture extends PImage implements PConstants { idName = devicePropertyName; idValue = cameraName; } - initGStreamer(parent, requestWidth, requestHeight, sourceElementName, + initGStreamer(parent, requestWidth, requestHeight, sourceElementName, idName, idValue, ""); } - + /** *

Advanced

- * This constructor allows to specify the camera name and the desired + * This constructor allows to specify the camera name and the desired * framerate, in addition to the resolution. - */ - public Capture(PApplet parent, int requestWidth, int requestHeight, + */ + public Capture(PApplet parent, int requestWidth, int requestHeight, String cameraName, int frameRate) { super(requestWidth, requestHeight, RGB); String idName; Object idValue; if (devicePropertyName.equals("")) { - // For plugins without device name property, the name is casted + // For plugins without device name property, the name is casted // as an index idName = indexPropertyName; idValue = new Integer(PApplet.parseInt(cameraName)); @@ -229,14 +229,14 @@ public class Capture extends PImage implements PConstants { idName = devicePropertyName; idValue = cameraName; } - initGStreamer(parent, requestWidth, requestHeight, sourceElementName, + initGStreamer(parent, requestWidth, requestHeight, sourceElementName, idName, idValue, frameRate + "/1"); - } - - + } + + /** * Disposes all the native resources associated to this capture device. - */ + */ public void dispose() { if (pipeline != null) { try { @@ -247,56 +247,56 @@ public class Capture extends PImage implements PConstants { } catch (Exception e) { e.printStackTrace(); } - - pixels = null; - + + pixels = null; + copyPixels = null; if (rgbSink != null) { rgbSink.removeListener(); rgbSink.dispose(); rgbSink = null; - } - + } + natBuffer = null; if (natSink != null) { natSink.removeListener(); natSink.dispose(); natSink = null; - } - + } + pipeline.dispose(); pipeline = null; } - } - - + } + + /** * Finalizer of the class. - */ + */ protected void finalize() throws Throwable { try { dispose(); } finally { super.finalize(); } - } - - + } + + /** * ( begin auto-generated from Capture_available.xml ) - * + * * Returns "true" when a new video frame is available to read. - * + * * ( end auto-generated ) - * + * * @webref capture * @usage web_application */ public boolean available() { return available; } - - + + /** * Starts capturing frames from the selected device. */ @@ -306,23 +306,23 @@ public class Capture extends PImage implements PConstants { initPipeline(); init = true; } - + capturing = true; pipeline.play(); - + if (init) { checkResIsValid(); } } - - + + /** * ( begin auto-generated from Capture_stop.xml ) - * + * * Stops capturing frames from an attached device. - * + * * ( end auto-generated ) - * + * * @webref capture * @usage web_application */ @@ -330,25 +330,25 @@ public class Capture extends PImage implements PConstants { if (!pipelineReady) { initPipeline(); } - + capturing = false; pipeline.stop(); pipeline.getState(); - } - - - /** + } + + + /** * ( begin auto-generated from Capture_read.xml ) - * + * * Reads the current video frame. - * + * * ( end auto-generated ) - * + * *

Advanced

* This method() and invokeEvent() are now synchronized, so that invokeEvent() * can't be called whilst we're busy reading. Problematic frame error * fixed by Charl P. Botha - * + * * @webref capture * @usage web_application */ @@ -358,91 +358,91 @@ public class Capture extends PImage implements PConstants { // which is already playing since we are in read(). frameRate = getSourceFrameRate(); } - + if (useBufferSink) { // The native buffer from gstreamer is copied to the buffer sink. - if (natBuffer == null) { + if (natBuffer == null) { return; } - + if (firstFrame) { super.init(bufWidth, bufHeight, ARGB); firstFrame = false; - } - + } + if (bufferSink == null) { - Object cache = getCache(parent.g); + Object cache = parent.g.getCache(this); if (cache == null) { return; - } - setBufferSink(cache); + } + setBufferSink(cache); getSinkMethods(); } - + ByteBuffer byteBuffer = natBuffer.getByteBuffer(); - + try { - sinkCopyMethod.invoke(bufferSink, + sinkCopyMethod.invoke(bufferSink, new Object[] { natBuffer, byteBuffer, bufWidth, bufHeight }); } catch (Exception e) { e.printStackTrace(); } - - natBuffer = null; - } else { // The pixels just read from gstreamer are copied to the pixels array. + + natBuffer = null; + } else { // The pixels just read from gstreamer are copied to the pixels array. if (copyPixels == null) { return; - } - + } + if (firstFrame) { super.init(bufWidth, bufHeight, RGB); firstFrame = false; } - + int[] temp = pixels; pixels = copyPixels; updatePixels(); - copyPixels = temp; + copyPixels = temp; } - + available = false; newFrame = true; } - - + + //////////////////////////////////////////////////////////// - + // List methods. - - + + /** * ( begin auto-generated from Capture_list.xml ) - * - * Gets a list of all available capture devices such as a camera. Use + * + * Gets a list of all available capture devices such as a camera. Use * print() to write the information to the text window. - * + * * ( end auto-generated ) - * + * * @webref capture * @usage web_application - */ - static public String[] list() { + */ + static public String[] list() { if (devicePropertyName.equals("")) { - return list(sourceElementName, indexPropertyName); + return list(sourceElementName, indexPropertyName); } else { return list(sourceElementName, devicePropertyName); } } - - + + static protected String[] list(String sourceName, String propertyName) { Video.init(); ArrayList devices = listDevices(sourceName, propertyName); - + ArrayList configList = new ArrayList(); for (String device: devices) { - ArrayList resolutions = listResolutions(sourceName, propertyName, + ArrayList resolutions = listResolutions(sourceName, propertyName, device); - if (0 < resolutions.size()) { + if (0 < resolutions.size()) { for (String res: resolutions) { configList.add("name=" + device + "," + res); } @@ -450,17 +450,17 @@ public class Capture extends PImage implements PConstants { configList.add("name=" + device); } } - + String[] configs = new String[configList.size()]; for (int i = 0; i < configs.length; i++) { - configs[i] = configList.get(i); + configs[i] = configList.get(i); } - + return configs; } - - static protected ArrayList listDevices(String sourceName, + + static protected ArrayList listDevices(String sourceName, String propertyName) { ArrayList devices = new ArrayList(); try { @@ -497,32 +497,32 @@ public class Capture extends PImage implements PConstants { } } } - } else { - PGraphics.showWarning("The capture plugin does not support " + + } else { + PGraphics.showWarning("The capture plugin does not support " + "device query!"); devices = new ArrayList(); - } + } } return devices; } - - - static protected ArrayList listResolutions(String sourceName, - String propertyName, + + + static protected ArrayList listResolutions(String sourceName, + String propertyName, Object propertyValue) { - // Creating temporary pipeline so that we can query + // Creating temporary pipeline so that we can query // the resolutions supported by the device. - Pipeline testPipeline = new Pipeline("test"); - Element source = ElementFactory.make(sourceName, "source"); + Pipeline testPipeline = new Pipeline("test"); + Element source = ElementFactory.make(sourceName, "source"); source.set(propertyName, propertyValue); - + BufferDataAppSink sink = new BufferDataAppSink("sink", "", new BufferDataAppSink.Listener() { public void bufferFrame(int w, int h, Buffer buffer) { } }); testPipeline.addMany(source, sink); - Element.linkMany(source, sink); - + Element.linkMany(source, sink); + // Play/pause sequence (with getState() calls to to make sure // all async operations are done) to trigger the capture momentarily // for the device and obtain its supported resolutions. @@ -530,23 +530,23 @@ public class Capture extends PImage implements PConstants { testPipeline.getState(); testPipeline.pause(); testPipeline.getState(); - - ArrayList resolutions = new ArrayList(); + + ArrayList resolutions = new ArrayList(); addResFromSource(resolutions, source); - + testPipeline.stop(); testPipeline.getState(); - + if (sink != null) { sink.removeListener(); sink.dispose(); - } - + } + testPipeline.dispose(); return resolutions; - } - - + } + + static protected void addResFromSource(ArrayList res, Element src) { if (PApplet.platform == MACOSX && useResMacHack) { addResFromSourceMacHack(res, src); @@ -554,33 +554,33 @@ public class Capture extends PImage implements PConstants { addResFromSourceImpl(res, src); } } - - - static protected void addResFromSourceImpl(ArrayList res, + + + static protected void addResFromSourceImpl(ArrayList res, Element src) { for (Pad pad : src.getPads()) { Caps caps = pad.getCaps(); - int n = caps.size(); - for (int i = 0; i < n; i++) { + int n = caps.size(); + for (int i = 0; i < n; i++) { Structure str = caps.getStructure(i); - + if (!str.hasIntField("width") || !str.hasIntField("height")) continue; - + int w = ((Integer)str.getValue("width")).intValue(); - int h = ((Integer)str.getValue("height")).intValue(); - + int h = ((Integer)str.getValue("height")).intValue(); + if (PApplet.platform == WINDOWS) { // In Windows the getValueList() method doesn't seem to // return a valid list of fraction values, so working on - // the string representation of the caps structure. + // the string representation of the caps structure. addResFromString(res, str.toString(), w, h); } else { addResFromStructure(res, str, w, h); - } + } } - } + } } - + // The problem on OSX, at least when using qtkitvideosrc, is that it is only // possible to obtain a single supported caps, the native maximum, using @@ -589,36 +589,36 @@ public class Capture extends PImage implements PConstants { // QTKit allows to capture streams at arbitrary resolutions, then the list is // faked by repeatedly dividing the maximum by 2 until the width becomes too // small (or not divisible by 2). - static protected void addResFromSourceMacHack(ArrayList res, + static protected void addResFromSourceMacHack(ArrayList res, Element src) { for (Pad pad : src.getPads()) { Caps caps = pad.getNegotiatedCaps(); int n = caps.size(); if (0 < n) { Structure str = caps.getStructure(0); - + if (!str.hasIntField("width") || !str.hasIntField("height")) return; - + int w = ((Integer)str.getValue("width")).intValue(); - int h = ((Integer)str.getValue("height")).intValue(); + int h = ((Integer)str.getValue("height")).intValue(); while (80 <= w) { int num = 30; int den = 1; try { Fraction fr = str.getFraction("framerate"); - num = fr.numerator; + num = fr.numerator; den = fr.denominator; } catch (Exception e) { - } - + } + res.add(makeResolutionString(w, h, num, den)); if (num == 30 && den == 1) { // Adding additional framerates to allow for slower capture. Again, - // QTKit can output frames at arbitrary rates. + // QTKit can output frames at arbitrary rates. res.add(makeResolutionString(w, h, 15, 1)); res.add(makeResolutionString(w, h, 1, 1)); } - + if (w % 2 == 0 && h % 2 == 0) { w /= 2; h /= 2; @@ -629,10 +629,10 @@ public class Capture extends PImage implements PConstants { } } } - - - static protected void addResFromString(ArrayList res, String str, - int w, int h) { + + + static protected void addResFromString(ArrayList res, String str, + int w, int h) { int n0 = str.indexOf("framerate=(fraction)"); if (-1 < n0) { String temp = str.substring(n0 + 20, str.length()); @@ -640,10 +640,10 @@ public class Capture extends PImage implements PConstants { int n2 = temp.indexOf("]"); if (-1 < n1 && -1 < n2) { // A list of fractions enclosed between '[' and ']' - temp = temp.substring(n1 + 1, n2); + temp = temp.substring(n1 + 1, n2); String[] fractions = temp.split(","); for (int k = 0; k < fractions.length; k++) { - String fpsStr = fractions[k].trim(); + String fpsStr = fractions[k].trim(); res.add(makeResolutionString(w, h, fpsStr)); } } else { @@ -659,34 +659,34 @@ public class Capture extends PImage implements PConstants { } else { n5 = PApplet.min(n3, n4); } - + temp = temp.substring(0, n5); - String fpsStr = temp.trim(); + String fpsStr = temp.trim(); res.add(makeResolutionString(w, h, fpsStr)); } - } + } } } - - - static protected void addResFromStructure(ArrayList res, + + + static protected void addResFromStructure(ArrayList res, Structure str, int w, int h) { boolean singleFrac = false; try { Fraction fr = str.getFraction("framerate"); res.add(makeResolutionString(w, h, fr.numerator, fr.denominator)); singleFrac = true; - } catch (Exception e) { + } catch (Exception e) { } - + if (!singleFrac) { ValueList flist = null; - + try { flist = str.getValueList("framerate"); - } catch (Exception e) { + } catch (Exception e) { } - + if (flist != null) { // All the framerates are put together, but this is not // entirely accurate since there might be some of them @@ -694,35 +694,35 @@ public class Capture extends PImage implements PConstants { for (int k = 0; k < flist.getSize(); k++) { Fraction fr = flist.getFraction(k); res.add(makeResolutionString(w, h, fr.numerator, fr.denominator)); - } - } - } + } + } + } } - - static protected String makeResolutionString(int width, int height, int - fpsNumerator, + + static protected String makeResolutionString(int width, int height, int + fpsNumerator, int fpsDenominator) { String res = "size=" + width + "x" + height + ",fps=" + fpsNumerator; if (fpsDenominator != 1) { - res += "/" + fpsDenominator; - } + res += "/" + fpsDenominator; + } return res; } - - static protected String makeResolutionString(int width, int height, + + static protected String makeResolutionString(int width, int height, String fpsStr) { - String res = "size=" + width + "x" + height; + String res = "size=" + width + "x" + height; String[] parts = fpsStr.split("/"); - if (parts.length == 2) { + if (parts.length == 2) { int fpsNumerator = PApplet.parseInt(parts[0]); int fpsDenominator = PApplet.parseInt(parts[1]); res += ",fps=" + fpsNumerator; if (fpsDenominator != 1) { - res += "/" + fpsDenominator; + res += "/" + fpsDenominator; } - } + } return res; } @@ -730,28 +730,28 @@ public class Capture extends PImage implements PConstants { protected void checkResIsValid() { ArrayList resolutions = new ArrayList(); addResFromSource(resolutions, sourceElement); - - boolean valid = resolutions.size() == 0; + + boolean valid = resolutions.size() == 0; for (String res: resolutions) { if (validRes(res)) { valid = true; break; } - } - + } + if (!valid) { String fpsStr = ""; if (!frameRateString.equals("")) { fpsStr = ", " + frameRateString + "fps"; - } - throw new RuntimeException("The requested resolution of " + reqWidth + - "x" + reqHeight + fpsStr + - " is not supported by the selected capture " + + } + throw new RuntimeException("The requested resolution of " + reqWidth + + "x" + reqHeight + fpsStr + + " is not supported by the selected capture " + "device.\n"); - } + } } - + protected void checkValidDevices(String src) { ArrayList devices; if (devicePropertyName.equals("")) { @@ -760,75 +760,75 @@ public class Capture extends PImage implements PConstants { devices = listDevices(src, devicePropertyName); } if (devices.size() == 0) { - throw new RuntimeException("There are no capture devices connected to " + + throw new RuntimeException("There are no capture devices connected to " + "this computer.\n"); } } - - + + protected boolean validRes(String res) { int[] size = getSize(res); - String fps = getFrameRate(res); - return (reqWidth == 0 || reqHeight == 0 || - (size[0] == reqWidth && size[1] == reqHeight)) && - (frameRateString.equals("") || frameRateString.equals(fps)); + String fps = getFrameRate(res); + return (reqWidth == 0 || reqHeight == 0 || + (size[0] == reqWidth && size[1] == reqHeight)) && + (frameRateString.equals("") || frameRateString.equals(fps)); } - + //////////////////////////////////////////////////////////// - + // Initialization methods. - - + + // The main initialization here. protected void initGStreamer(PApplet parent, int rw, int rh, String src, - String idName, Object idValue, + String idName, Object idValue, String fps) { this.parent = parent; Video.init(); checkValidDevices(src); - + // register methods parent.registerDispose(this); setEventHandlerObject(parent); pipeline = new Pipeline("Video Capture"); - + frameRateString = fps; if (frameRateString.equals("")) { frameRate = -1; } else { String[] parts = frameRateString.split("/"); - if (parts.length == 2) { + if (parts.length == 2) { int fpsDenominator = PApplet.parseInt(parts[0]); int fpsNumerator = PApplet.parseInt(parts[1]); - frameRate = (float)fpsDenominator / (float)fpsNumerator; + frameRate = (float)fpsDenominator / (float)fpsNumerator; } else if (parts.length == 1) { frameRateString += "/1"; - frameRate = PApplet.parseFloat(parts[0]); + frameRate = PApplet.parseFloat(parts[0]); } else { frameRateString = ""; frameRate = -1; } } - + reqWidth = rw; - reqHeight = rh; - + reqHeight = rh; + sourceName = src; sourceElement = ElementFactory.make(src, "Source"); - + if (idName != null && !idName.equals("")) { sourceElement.set(idName, idValue); } - + bufWidth = bufHeight = 0; - pipelineReady = false; + pipelineReady = false; } - + protected void initPipeline() { String whStr = ""; if (0 < reqWidth && 0 < reqHeight) { @@ -838,77 +838,77 @@ public class Capture extends PImage implements PConstants { " to open the capture device at 320x240"); whStr = "width=320, height=240"; } - + String fpsStr = ""; if (!frameRateString.equals("")) { // If the framerate string is empty we left the source element - // to use the default value. + // to use the default value. fpsStr = ", framerate=" + frameRateString; - } - + } + if (bufferSink != null || (Video.useGLBufferSink && parent.g.isGL())) { useBufferSink = true; - + if (bufferSink != null) { - getSinkMethods(); - } - - if (copyMask == null || copyMask.equals("")) { + getSinkMethods(); + } + + if (copyMask == null || copyMask.equals("")) { initCopyMask(); } - + String caps = whStr + fpsStr + ", " + copyMask; - + natSink = new BufferDataAppSink("nat", caps, new BufferDataAppSink.Listener() { public void bufferFrame(int w, int h, Buffer buffer) { invokeEvent(w, h, buffer); } }); - + natSink.setAutoDisposeBuffer(false); - + // No need for rgbSink.dispose(), because the addMany() doesn't increment the - // refcount of the videoSink object. - + // refcount of the videoSink object. + pipeline.addMany(sourceElement, natSink); - Element.linkMany(sourceElement, natSink); - + Element.linkMany(sourceElement, natSink); + } else { Element conv = ElementFactory.make("ffmpegcolorspace", "ColorConverter"); Element videofilter = ElementFactory.make("capsfilter", "ColorFilter"); - videofilter.setCaps(new Caps("video/x-raw-rgb, width=" + reqWidth + - ", height=" + reqHeight + + videofilter.setCaps(new Caps("video/x-raw-rgb, width=" + reqWidth + + ", height=" + reqHeight + ", bpp=32, depth=24" + fpsStr)); - - rgbSink = new RGBDataAppSink("rgb", + + rgbSink = new RGBDataAppSink("rgb", new RGBDataAppSink.Listener() { public void rgbFrame(int w, int h, IntBuffer buffer) { invokeEvent(w, h, buffer); } }); - + // Setting direct buffer passing in the video sink. - rgbSink.setPassDirectBuffer(Video.passDirectBuffer); - - // No need for rgbSink.dispose(), because the addMany() doesn't increment - // the refcount of the videoSink object. - + rgbSink.setPassDirectBuffer(Video.passDirectBuffer); + + // No need for rgbSink.dispose(), because the addMany() doesn't increment + // the refcount of the videoSink object. + pipeline.addMany(sourceElement, conv, videofilter, rgbSink); - Element.linkMany(sourceElement, conv, videofilter, rgbSink); - } - + Element.linkMany(sourceElement, conv, videofilter, rgbSink); + } + pipelineReady = true; newFrame = false; } - - + + /** * Uses a generic object as handler of the capture. This object should have a * movieEvent method that receives a GSMovie argument. This method will - * be called upon a new frame read event. - * + * be called upon a new frame read event. + * */ protected void setEventHandlerObject(Object obj) { eventHandler = obj; @@ -920,13 +920,13 @@ public class Capture extends PImage implements PConstants { // no such method, or an error.. which is fine, just ignore } } - - + + //////////////////////////////////////////////////////////// - - // Stream event handling. - - + + // Stream event handling. + + /** * invokeEvent() and read() are synchronized so that they can not be * called simultaneously. when they were not synchronized, this caused @@ -940,7 +940,7 @@ public class Capture extends PImage implements PConstants { if (copyPixels == null) { copyPixels = new int[w * h]; } - buffer.rewind(); + buffer.rewind(); try { buffer.get(copyPixels); } catch (BufferUnderflowException e) { @@ -948,7 +948,7 @@ public class Capture extends PImage implements PConstants { copyPixels = null; return; } - + // Creates a movieEvent. if (captureEventMethod != null) { try { @@ -961,14 +961,14 @@ public class Capture extends PImage implements PConstants { } } } - + protected synchronized void invokeEvent(int w, int h, Buffer buffer) { available = true; bufWidth = w; - bufHeight = h; + bufHeight = h; natBuffer = buffer; - + // Creates a movieEvent. if (captureEventMethod != null) { try { @@ -980,13 +980,13 @@ public class Capture extends PImage implements PConstants { captureEventMethod = null; } } - } - - + } + + //////////////////////////////////////////////////////////// - - // Stream query methods. - + + // Stream query methods. + protected float getSourceFrameRate() { for (Element sink : pipeline.getSinks()) { @@ -998,27 +998,27 @@ public class Capture extends PImage implements PConstants { } } return 0; - } - - + } + + protected String getName(String config) { String name = ""; - String[] parts = PApplet.split(config, ','); + String[] parts = PApplet.split(config, ','); for (String part: parts) { - if (-1 < part.indexOf("name")) { + if (-1 < part.indexOf("name")) { String[] values = PApplet.split(part, '='); if (0 < values.length) { name = values[1]; - } + } } } return name; } - - + + protected int[] getSize(String config) { - int[] wh = {0, 0}; - String[] parts = PApplet.split(config, ','); + int[] wh = {0, 0}; + String[] parts = PApplet.split(config, ','); for (String part: parts) { if (-1 < part.indexOf("size")) { String[] values = PApplet.split(part, '='); @@ -1029,95 +1029,95 @@ public class Capture extends PImage implements PConstants { wh[1] = PApplet.parseInt(whstr[1]); } } - } + } } return wh; } - - + + protected String getFrameRate(String config) { String fps = ""; - String[] parts = PApplet.split(config, ','); + String[] parts = PApplet.split(config, ','); for (String part: parts) { - if (-1 < part.indexOf("fps")) { + if (-1 < part.indexOf("fps")) { String[] values = PApplet.split(part, '='); if (0 < values.length) { fps = values[1]; if (fps.indexOf("/") == -1) { - fps += "/1"; - } + fps += "/1"; + } } } - } + } return fps; } - - + + //////////////////////////////////////////////////////////// - - // Buffer source interface. + + // Buffer source interface. /** * Sets the object to use as destination for the frames read from the stream. * The color conversion mask is automatically set to the one required to * copy the frames to OpenGL. - * + * * @param Object dest - */ + */ public void setBufferSink(Object sink) { bufferSink = sink; initCopyMask(); - } - - + } + + /** * Sets the object to use as destination for the frames read from the stream. - * + * * @param Object dest - * @param String mask - */ + * @param String mask + */ public void setBufferSink(Object sink, String mask) { bufferSink = sink; copyMask = mask; - } - - + } + + public boolean hasBufferSink() { return bufferSink != null; } - - + + public synchronized void disposeBuffer(Object buf) { ((Buffer)buf).dispose(); } - + protected void getSinkMethods() { - try { + try { sinkCopyMethod = bufferSink.getClass().getMethod("copyBufferFromSource", - new Class[] { Object.class, ByteBuffer.class, int.class, int.class }); + new Class[] { Object.class, ByteBuffer.class, int.class, int.class }); } catch (Exception e) { throw new RuntimeException("Capture: provided sink object doesn't have " + "a copyBufferFromSource method."); } - - try { - sinkSetMethod = bufferSink.getClass().getMethod("setBufferSource", + + try { + sinkSetMethod = bufferSink.getClass().getMethod("setBufferSource", new Class[] { Object.class }); - sinkSetMethod.invoke(bufferSink, new Object[] { this }); + sinkSetMethod.invoke(bufferSink, new Object[] { this }); } catch (Exception e) { throw new RuntimeException("Capture: provided sink object doesn't have "+ "a setBufferSource method."); - } + } } - - + + protected void initCopyMask() { if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) { - copyMask = "red_mask=(int)0xFF000000, green_mask=(int)0xFF0000, blue_mask=(int)0xFF00"; + copyMask = "red_mask=(int)0xFF000000, green_mask=(int)0xFF0000, blue_mask=(int)0xFF00"; } else { copyMask = "red_mask=(int)0xFF, green_mask=(int)0xFF00, blue_mask=(int)0xFF0000"; - } + } } } \ No newline at end of file diff --git a/java/libraries/video/src/processing/video/Movie.java b/java/libraries/video/src/processing/video/Movie.java index b2299043a..5f97e88f1 100644 --- a/java/libraries/video/src/processing/video/Movie.java +++ b/java/libraries/video/src/processing/video/Movie.java @@ -40,59 +40,59 @@ import org.gstreamer.elements.*; /** * ( begin auto-generated from Movie.xml ) - * - * Datatype for storing and playing movies in Apple's QuickTime format. - * Movies must be located in the sketch's data directory or an accessible + * + * Datatype for storing and playing movies in Apple's QuickTime format. + * Movies must be located in the sketch's data directory or an accessible * place on the network to load without an error. - * + * * ( end auto-generated ) - * + * * @webref video * @usage application */ public class Movie extends PImage implements PConstants { - public static String[] supportedProtocols = { "http" }; - public float frameRate; + public static String[] supportedProtocols = { "http" }; + public float frameRate; public String filename; public PlayBin2 playbin; - + protected boolean playing = false; protected boolean paused = false; protected boolean repeat = false; - + protected float rate; protected int bufWidth; protected int bufHeight; protected float volume; - + protected Method movieEventMethod; protected Object eventHandler; - - protected boolean available; + + protected boolean available; protected boolean sinkReady; protected boolean newFrame; - + protected RGBDataAppSink rgbSink = null; protected int[] copyPixels = null; - + protected boolean firstFrame = true; - protected boolean seeking = false; - - protected boolean useBufferSink = false; + protected boolean seeking = false; + + protected boolean useBufferSink = false; protected Object bufferSink; protected Method sinkCopyMethod; protected Method sinkSetMethod; - protected String copyMask; + protected String copyMask; protected Buffer natBuffer = null; - protected BufferDataAppSink natSink = null; - - + protected BufferDataAppSink natSink = null; + + /** * Creates an instance of GSMovie loading the movie from filename. - * + * * @param parent PApplet * @param filename String - */ + */ public Movie(PApplet parent, String filename) { super(0, 0, RGB); initGStreamer(parent, filename); @@ -101,7 +101,7 @@ public class Movie extends PImage implements PConstants { /** * Disposes all the native resources associated to this movie. - */ + */ public void dispose() { if (playbin != null) { try { @@ -112,68 +112,68 @@ public class Movie extends PImage implements PConstants { } catch (Exception e) { e.printStackTrace(); } - - pixels = null; - + + pixels = null; + copyPixels = null; if (rgbSink != null) { rgbSink.removeListener(); rgbSink.dispose(); rgbSink = null; - } - + } + natBuffer = null; if (natSink != null) { natSink.removeListener(); natSink.dispose(); natSink = null; - } + } playbin.dispose(); playbin = null; } } - - + + /** * Finalizer of the class. - */ + */ protected void finalize() throws Throwable { try { dispose(); } finally { super.finalize(); } - } - - + } + + /** * ( begin auto-generated from Movie_frameRate.xml ) - * - * Sets how often frames are read from the movie. Setting the fps + * + * Sets how often frames are read from the movie. Setting the fps * parameter to 4, for example, will cause 4 frames to be read per second. - * + * * ( end auto-generated ) - * + * * @webref movie - * @usage web_application + * @usage web_application * @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 + + // 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 < frameRate) ? ifps / frameRate : 1; - + if (playing) { playbin.pause(); playbin.getState(); } long t = playbin.queryPosition(TimeUnit.NANOSECONDS); - + boolean res; long start, stop; if (rate > 0) { @@ -182,65 +182,65 @@ public class Movie extends PImage implements PConstants { } else { start = 0; stop = t; - } - + } + 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) { playbin.play(); } frameRate = ifps; - - // getState() will wait until any async state change - // (like seek in this case) has completed + + // getState() will wait until any async state change + // (like seek in this case) has completed seeking = true; playbin.getState(); - seeking = false; + seeking = false; } - - + + /** * ( begin auto-generated from Movie_speed.xml ) - * - * Sets the relative playback speed of the movie. The rate - * parameters sets the speed where 2.0 will play the movie twice as fast, - * 0.5 will play at half the speed, and -1 will play the movie in normal + * + * Sets the relative playback speed of the movie. The rate + * parameters sets the speed where 2.0 will play the movie twice as fast, + * 0.5 will play at half the speed, and -1 will play the movie in normal * speed in reverse. - * + * * ( end auto-generated ) - * - + * + * @webref movie - - * @usage web_application + + * @usage web_application * @param irate speed multiplier for movie playback */ public void speed(float irate) { // If the frameRate() method is called continuously with very similar // rate values, playback might become sluggish. This condition attempts // to take care of that. - if (PApplet.abs(rate - irate) > 0.1) { + if (PApplet.abs(rate - irate) > 0.1) { rate = irate; frameRate(frameRate); // The framerate is the same, but the rate (speed) could be different. } } - - + + /** * ( begin auto-generated from Movie_duration.xml ) - * - * Returns the length of the movie in seconds. If the movie is 1 minute and + * + * Returns the length of the movie in seconds. If the movie is 1 minute and * 20 seconds long the value returned will be 80.0. - * + * * ( end auto-generated ) - * + * * @webref movie * @usage web_application */ @@ -248,19 +248,19 @@ public class Movie extends PImage implements PConstants { float sec = playbin.queryDuration().toSeconds(); float nanosec = playbin.queryDuration().getNanoSeconds(); return sec + Video.nanoSecToSecFrac(nanosec); - } - - + } + + /** * ( begin auto-generated from Movie_time.xml ) - * - * Returns the location of the playback head in seconds. For example, if + * + * Returns the location of the playback head in seconds. For example, if * the movie has been playing for 4 seconds, the number 4.0 will be returned. - * + * * ( end auto-generated ) - * + * * @webref movie - * @usage web_application + * @usage web_application */ public float time() { float sec = playbin.queryPosition().toSeconds(); @@ -268,190 +268,190 @@ public class Movie extends PImage implements PConstants { return sec + Video.nanoSecToSecFrac(nanosec); } - + /** * ( begin auto-generated from Movie_jump.xml ) - * - * Jumps to a specific location within a movie. The parameter where - * is in terms of seconds. For example, if the movie is 12.2 seconds long, + * + * Jumps to a specific location within a movie. The parameter where + * is in terms of seconds. For example, if the movie is 12.2 seconds long, * calling jump(6.1) would go to the middle of the movie. - * + * * ( end auto-generated ) - * + * * @webref movie - * @usage web_application + * @usage web_application * @param where position to jump to specified in seconds */ public void jump(float where) { if (seeking) return; - + if (!sinkReady) { initSink(); - } - + } + // Round the time to a multiple of the source framerate, in // order to eliminate stutter. Suggested by Daniel Shiffman float fps = getSourceFrameRate(); int frame = (int)(where * fps); - where = frame / fps; - + where = frame / fps; + boolean res; long pos = Video.secToNanoLong(where); - + res = playbin.seek(1.0, Format.TIME, SeekFlags.FLUSH, SeekType.SET, pos, SeekType.NONE, -1); - + if (!res) { PGraphics.showWarning("Seek operation failed."); - } - - // getState() will wait until any async state change - // (like seek in this case) has completed + } + + // 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_available.xml ) - * + * * Returns "true" when a new movie frame is available to read. - * + * * ( end auto-generated ) - * + * * @webref movie - * @usage web_application + * @usage web_application */ public boolean available() { return available; } - - + + /** * ( begin auto-generated from Movie_play.xml ) - * + * * Plays a movie one time and stops at the last frame. - * + * * ( end auto-generated ) - * + * * @webref movie * @usage web_application */ public void play() { if (seeking) return; - + if (!sinkReady) { initSink(); } - + playing = true; paused = false; playbin.play(); playbin.getState(); } - + /** * ( begin auto-generated from Movie_loop.xml ) - * + * * Plays a movie continuously, restarting it when it is over. - * + * * ( end auto-generated ) - * + * * @webref movie * @usage web_application */ public void loop() { if (seeking) return; - + repeat = true; play(); } - + /** * ( begin auto-generated from Movie_noLoop.xml ) - * - * If a movie is looping, calling noLoop() will cause it to play until the + * + * If a movie is looping, calling noLoop() will cause it to play until the * end and then stop on the last frame. - * + * * ( end auto-generated ) - * + * * @webref movie * @usage web_application */ public void noLoop() { if (seeking) return; - + if (!sinkReady) { initSink(); } - + repeat = false; } - + /** * ( begin auto-generated from Movie_pause.xml ) - * - * Pauses a movie during playback. If a movie is started again with play(), + * + * Pauses a movie during playback. If a movie is started again with play(), * it will continue from where it was paused. - * + * * ( end auto-generated ) - * + * * @webref movie * @usage web_application */ public void pause() { if (seeking) return; - + if (!sinkReady) { initSink(); } - + playing = false; paused = true; - playbin.pause(); + playbin.pause(); playbin.getState(); } - + /** * ( begin auto-generated from Movie_stop.xml ) - * - * Stops a movie from continuing. The playback returns to the beginning so + * + * Stops a movie from continuing. The playback returns to the beginning so * when a movie is played, it will begin from the beginning. - * + * * ( end auto-generated ) - * + * * @webref movie * @usage web_application */ public void stop() { if (seeking) return; - + if (!sinkReady) { initSink(); } - - if (playing) { + + if (playing) { jump(0); playing = false; } - paused = false; + paused = false; playbin.stop(); playbin.getState(); } - + /** * ( begin auto-generated from Movie_read.xml ) - * - * Reads the current frame of the movie. - * + * + * Reads the current frame of the movie. + * * ( end auto-generated ) - * + * * @webref movie * @usage web_application */ @@ -465,51 +465,51 @@ public class Movie extends PImage implements PConstants { // Idem for volume volume = (float)playbin.getVolume(); } - + if (useBufferSink) { // The native buffer from gstreamer is copied to the buffer sink. - if (natBuffer == null) { + if (natBuffer == null) { return; } - + if (firstFrame) { super.init(bufWidth, bufHeight, ARGB); firstFrame = false; - } - + } + if (bufferSink == null) { - Object cache = getCache(parent.g); + Object cache = parent.g.getCache(this); if (cache == null) { return; - } - setBufferSink(cache); + } + setBufferSink(cache); getSinkMethods(); } - + ByteBuffer byteBuffer = natBuffer.getByteBuffer(); - + try { sinkCopyMethod.invoke(bufferSink, new Object[] { natBuffer, byteBuffer, bufWidth, bufHeight }); } catch (Exception e) { e.printStackTrace(); } - - natBuffer = null; - } else { // The pixels just read from gstreamer are copied to the pixels array. + + natBuffer = null; + } else { // The pixels just read from gstreamer are copied to the pixels array. if (copyPixels == null) { return; - } - + } + if (firstFrame) { super.init(bufWidth, bufHeight, RGB); firstFrame = false; } - + int[] temp = pixels; pixels = copyPixels; updatePixels(); copyPixels = temp; } - + available = false; newFrame = true; } @@ -517,8 +517,8 @@ public class Movie extends PImage implements PConstants { /** * Change the volume. Values are from 0 to 1. - * - * @param float v + * + * @param float v */ public void volume(float v) { if (playing && PApplet.abs(volume - v) > 0.001f) { @@ -526,13 +526,13 @@ public class Movie extends PImage implements PConstants { volume = v; } } - - + + //////////////////////////////////////////////////////////// - + // Initialization methods. - - + + protected void initGStreamer(PApplet parent, String filename) { this.parent = parent; playbin = null; @@ -549,7 +549,7 @@ public class Movie extends PImage implements PConstants { // which is less fun, so this will crap out. file = new File(parent.dataPath(filename)); if (file.exists()) { - playbin = new PlayBin2("Movie Player"); + playbin = new PlayBin2("Movie Player"); playbin.setInputFile(file); } } catch (Exception e) { @@ -562,28 +562,28 @@ public class Movie extends PImage implements PConstants { try { file = new File(filename); if (file.exists()) { - playbin = new PlayBin2("Movie Player"); + playbin = new PlayBin2("Movie Player"); playbin.setInputFile(file); } } catch (Exception e) { e.printStackTrace(); } } - - if (playbin == null) { - // Try network read... + + if (playbin == null) { + // Try network read... for (int i = 0; i < supportedProtocols.length; i++) { - if (filename.startsWith(supportedProtocols[i] + "://")) { + if (filename.startsWith(supportedProtocols[i] + "://")) { try { - playbin = new PlayBin2("Movie Player"); + 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. @@ -602,23 +602,23 @@ public class Movie extends PImage implements PConstants { parent.registerDispose(this); setEventHandlerObject(parent); - + rate = 1.0f; frameRate = -1; volume = -1; sinkReady = false; - bufWidth = bufHeight = 0; + 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 - * be called upon a new frame read event. - * + * be called upon a new frame read event. + * */ protected void setEventHandlerObject(Object obj) { eventHandler = obj; @@ -629,51 +629,51 @@ public class Movie extends PImage implements PConstants { } catch (Exception e) { // no such method, or an error.. which is fine, just ignore } - } - - + } + + protected void initSink() { if (bufferSink != null || (Video.useGLBufferSink && parent.g.isGL())) { useBufferSink = true; - + if (bufferSink != null) { - getSinkMethods(); - } - - if (copyMask == null || copyMask.equals("")) { + getSinkMethods(); + } + + if (copyMask == null || copyMask.equals("")) { initCopyMask(); } - + natSink = new BufferDataAppSink("nat", copyMask, new BufferDataAppSink.Listener() { public void bufferFrame(int w, int h, Buffer buffer) { invokeEvent(w, h, buffer); } }); - + natSink.setAutoDisposeBuffer(false); playbin.setVideoSink(natSink); - // The setVideoSink() method sets the videoSink as a property of the - // PlayBin, which increments the refcount of the videoSink element. + // 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. - natSink.dispose(); + natSink.dispose(); } else { - rgbSink = new RGBDataAppSink("rgb", + rgbSink = new RGBDataAppSink("rgb", new RGBDataAppSink.Listener() { public void rgbFrame(int w, int h, IntBuffer buffer) { invokeEvent(w, h, buffer); } }); - + // Setting direct buffer passing in the video sink. rgbSink.setPassDirectBuffer(Video.passDirectBuffer); playbin.setVideoSink(rgbSink); - // The setVideoSink() method sets the videoSink as a property of the - // PlayBin, which increments the refcount of the videoSink element. + // 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. - rgbSink.dispose(); + rgbSink.dispose(); } - + // Creating bus to handle end-of-stream event. Bus bus = playbin.getBus(); bus.connect(new Bus.EOS() { @@ -681,22 +681,22 @@ public class Movie extends PImage implements PConstants { eosEvent(); } }); - + sinkReady = true; newFrame = false; } - - + + //////////////////////////////////////////////////////////// - - // Stream event handling. - - + + // Stream event handling. + + protected synchronized void invokeEvent(int w, int h, IntBuffer buffer) { available = true; bufWidth = w; - bufHeight = h; - + bufHeight = h; + if (copyPixels == null) { copyPixels = new int[w * h]; } @@ -708,28 +708,7 @@ public class Movie extends PImage implements PConstants { copyPixels = null; return; } - - if (playing) { - // Creates a movieEvent. - if (movieEventMethod != null) { - try { - movieEventMethod.invoke(eventHandler, new Object[] { this }); - } catch (Exception e) { - System.err.println("error, disabling movieEvent() for " + filename); - e.printStackTrace(); - movieEventMethod = null; - } - } - } - } - - - protected synchronized void invokeEvent(int w, int h, Buffer buffer) { - available = true; - bufWidth = w; - bufHeight = h; - natBuffer = buffer; - + if (playing) { // Creates a movieEvent. if (movieEventMethod != null) { @@ -743,9 +722,30 @@ public class Movie extends PImage implements PConstants { } } } - - - protected void eosEvent() { + + + protected synchronized void invokeEvent(int w, int h, Buffer buffer) { + available = true; + bufWidth = w; + bufHeight = h; + natBuffer = buffer; + + if (playing) { + // Creates a movieEvent. + if (movieEventMethod != null) { + try { + movieEventMethod.invoke(eventHandler, new Object[] { this }); + } catch (Exception e) { + System.err.println("error, disabling movieEvent() for " + filename); + e.printStackTrace(); + movieEventMethod = null; + } + } + } + } + + + protected void eosEvent() { if (repeat) { if (0 < rate) { // Playing forward, so we return to the beginning @@ -753,29 +753,29 @@ public class Movie extends PImage implements PConstants { } else { // Playing backwards, so we go to the end. jump(duration()); - } - - // 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. + } + + // 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. - - + + // 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) { @@ -785,24 +785,24 @@ public class Movie extends PImage implements PConstants { } } - + /** - * Get the original framerate of the source video. Note: calling this method + * 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) { @@ -810,74 +810,74 @@ public class Movie extends PImage implements PConstants { } else { return 0; } - } - - + } + + //////////////////////////////////////////////////////////// - - // Buffer source interface. - - + + // Buffer source interface. + + /** * Sets the object to use as destination for the frames read from the stream. * The color conversion mask is automatically set to the one required to * copy the frames to OpenGL. - * + * * @param Object dest - */ + */ public void setBufferSink(Object sink) { bufferSink = sink; initCopyMask(); - } - - + } + + /** * Sets the object to use as destination for the frames read from the stream. - * + * * @param Object dest - * @param String mask - */ + * @param String mask + */ public void setBufferSink(Object sink, String mask) { bufferSink = sink; copyMask = mask; - } + } + - public boolean hasBufferSink() { return bufferSink != null; - } - - + } + + public synchronized void disposeBuffer(Object buf) { ((Buffer)buf).dispose(); } - + protected void getSinkMethods() { - try { + try { sinkCopyMethod = bufferSink.getClass().getMethod("copyBufferFromSource", - new Class[] { Object.class, ByteBuffer.class, int.class, int.class }); + new Class[] { Object.class, ByteBuffer.class, int.class, int.class }); } catch (Exception e) { throw new RuntimeException("Movie: provided sink object doesn't have a " + "copyBufferFromSource method."); } - - try { - sinkSetMethod = bufferSink.getClass().getMethod("setBufferSource", + + try { + sinkSetMethod = bufferSink.getClass().getMethod("setBufferSource", new Class[] { Object.class }); - sinkSetMethod.invoke(bufferSink, new Object[] { this }); + sinkSetMethod.invoke(bufferSink, new Object[] { this }); } catch (Exception e) { - throw new RuntimeException("Movie: provided sink object doesn't have a " + + throw new RuntimeException("Movie: provided sink object doesn't have a " + "setBufferSource method."); - } + } } - - + + protected void initCopyMask() { if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) { - copyMask = "red_mask=(int)0xFF000000, green_mask=(int)0xFF0000, blue_mask=(int)0xFF00"; + copyMask = "red_mask=(int)0xFF000000, green_mask=(int)0xFF0000, blue_mask=(int)0xFF00"; } else { copyMask = "red_mask=(int)0xFF, green_mask=(int)0xFF00, blue_mask=(int)0xFF0000"; - } + } } }