diff --git a/examples/Capture/GettingStartedCapture/GettingStartedCapture.pde b/examples/Capture/GettingStartedCapture/GettingStartedCapture.pde index af86a5a..29f1d3a 100644 --- a/examples/Capture/GettingStartedCapture/GettingStartedCapture.pde +++ b/examples/Capture/GettingStartedCapture/GettingStartedCapture.pde @@ -26,12 +26,14 @@ void setup() { // The camera can be initialized directly using an element // from the array returned by list(): cam = new Capture(this, cameras[0]); - // Or, the settings can be defined based on the text in the list - //cam = new Capture(this, 640, 480, "Built-in iSight", 30); - - // Start capturing the images from the camera - cam.start(); + + // Or, the camera name can be retrieved from the list (you need + // to enter valid a width, height, and frame rate for the camera). + //cam = new Capture(this, 640, 480, "FaceTime HD Camera (Built-in)", 30); } + + // Start capturing the images from the camera + cam.start(); } void draw() { diff --git a/library.properties b/library.properties index 3f4d8ae..fe32250 100644 --- a/library.properties +++ b/library.properties @@ -4,7 +4,7 @@ url = http://processing.org/reference/libraries/video/index.html category = Video & Vision sentence = GStreamer-based video library for Processing. paragraph = -version = 10 -prettyVersion = 2.2 +version = 12 +prettyVersion = 2.2.2 minRevision = 1281 maxRevision = 0 diff --git a/library/jna.jar b/library/jna.jar index e73c2c2..77f8c7a 100644 Binary files a/library/jna.jar and b/library/jna.jar differ diff --git a/scripts/pack_linux_libs.sh b/scripts/pack_linux_libs.sh index 572b06b..e19a4d8 100755 --- a/scripts/pack_linux_libs.sh +++ b/scripts/pack_linux_libs.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Set the appropriae build dist folder in this env variable +# Set the appropriate build dist folder in this env variable meson_build_folder=/home/andres/code/gstreamer/build-1.20/lib/x86_64-linux-gnu # Copy the build to the native library folder for linux diff --git a/src/processing/video/Capture.java b/src/processing/video/Capture.java index a302227..844e87b 100755 --- a/src/processing/video/Capture.java +++ b/src/processing/video/Capture.java @@ -30,6 +30,7 @@ import processing.core.*; import java.nio.*; +import java.util.ArrayList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -104,7 +105,7 @@ public class Capture extends PImage implements PConstants { */ public Capture(PApplet parent) { // Attempt to use a default resolution - this(parent, 640, 480, null, 0); + this(parent, 640, 480, null, 30); } @@ -116,7 +117,7 @@ public Capture(PApplet parent) { */ public Capture(PApplet parent, String device) { // Attempt to use a default resolution - this(parent, 640, 480, device, 0); + this(parent, 640, 480, device, 30); } @@ -126,7 +127,7 @@ public Capture(PApplet parent, String device) { * @param height height in pixels */ public Capture(PApplet parent, int width, int height) { - this(parent, width, height, null, 0); + this(parent, width, height, null, 30); } @@ -144,7 +145,16 @@ public Capture(PApplet parent, int width, int height, float fps) { * @see Capture#list() */ public Capture(PApplet parent, int width, int height, String device) { - this(parent, width, height, device, 0); + this(parent, width, height, device, 30); + } + + + /** + * Open a specific capture device with a given framerate + * @see Capture#list() + */ + public Capture(PApplet parent, String device, float fps) { + this(parent, 640, 480, device, fps); } @@ -177,9 +187,11 @@ public void dispose() { pixels = null; - rgbSink.disconnect(newSampleListener); - rgbSink.disconnect(newPrerollListener); - rgbSink.dispose(); + if (rgbSink != null) { + rgbSink.disconnect(newSampleListener); + rgbSink.disconnect(newPrerollListener); + rgbSink.dispose(); + } pipeline.setState(org.freedesktop.gstreamer.State.NULL); pipeline.getState(); pipeline.getBus().dispose(); @@ -492,9 +504,8 @@ protected void initDevicePipeline() { } for (int i=0; i < devices.size(); i++) { - String deviceName = assignDisplayName(devices.get(i), i); + String deviceName = assignDisplayName(devices.get(i), i); if (devices.get(i).getDisplayName().equals(device) || devices.get(i).getName().equals(device) || deviceName.equals(device)) { - // Found device srcElement = devices.get(i).createElement(null); break; } @@ -516,14 +527,24 @@ protected void initDevicePipeline() { if (frameRate != 0.0) { frameRateString = ", framerate=" + fpsToFramerate(frameRate); } else { - frameRateString = ""; + System.err.println("The capture framerate cannot be zero!"); + return; } + capsfilter.set("caps", Caps.fromString("video/x-raw, width=" + width + ", height=" + height + frameRateString)); initSink(); - pipeline.addMany(srcElement, videoscale, videoconvert, capsfilter, rgbSink); - Element.linkMany(srcElement, videoscale, videoconvert, capsfilter, rgbSink); + pipeline.add(srcElement); + pipeline.add(videoscale); + pipeline.add(videoconvert); + pipeline.add(capsfilter); + pipeline.add(rgbSink); + + srcElement.link(videoscale); + videoscale.link(videoconvert); + videoconvert.link(capsfilter); + capsfilter.link(rgbSink); makeBusConnections(pipeline.getBus()); } @@ -717,24 +738,117 @@ public synchronized void post() { * Multiple devices can have identical display names, appending ' #n' to devices * with duplicate display names. * @return array of device names + * @webref capture + * @webBrief Get a list of all capture device names */ static public String[] list() { - Video.init(); + Video.init(); + + String[] out; + + DeviceMonitor monitor = new DeviceMonitor(); + monitor.addFilter("Video/Source", null); + devices = monitor.getDevices(); + monitor.close(); + + out = new String[devices.size()]; + for (int i = 0; i < devices.size(); i++) { + Device dev = devices.get(i); + out[i] = checkCameraDuplicates(dev) > 1 ? assignDisplayName(dev, i) : dev.getDisplayName(); + } + + return out; + } + + // This is a temporary addition until it's decided how to bring back resolution/framerate caps to the official API. + // The old way of doing things is still listed in the video tutorial: + // https://processing.org/tutorials/video + static public String[] getCapabilities(String device) { + for (int i=0; i < devices.size(); i++) { + String deviceName = assignDisplayName(devices.get(i), i); + if (devices.get(i).getDisplayName().equals(device) || devices.get(i).getName().equals(device) || deviceName.equals(device)) { + return parseCaps(devices.get(i)); + } + } + return new String[]{}; + } + + static private String[] parseCaps(Device dev) { + String[] caps = dev.getCaps().toString().split(";"); + ArrayList devCaps = new ArrayList(); - String[] out; + for (String cap: caps) { + if (cap.indexOf("video/x-raw,") == -1) continue; // Looking for raw caps (excluding GLMemory stuff) - DeviceMonitor monitor = new DeviceMonitor(); - monitor.addFilter("Video/Source", null); - devices = monitor.getDevices(); - monitor.close(); + int indexWidth = cap.indexOf("width"); + int indexHeight = cap.indexOf("height"); + int indexFramerate = cap.indexOf("framerate"); - out = new String[devices.size()]; - for (int i = 0; i < devices.size(); i++) { - Device dev = devices.get(i); - out[i] = checkCameraDuplicates(dev) > 1 ? assignDisplayName(dev, i) : dev.getDisplayName(); - } + String stringWidth = ""; + String stringHeight = ""; + String stringFramerate = ""; - return out; + if (0 < indexWidth && 0 < indexHeight && 0 < indexFramerate) { + stringWidth = cap.substring(indexWidth, cap.indexOf(',', indexWidth)); + stringHeight = cap.substring(indexHeight, cap.indexOf(", format", indexHeight)); + stringFramerate = cap.substring(indexFramerate, cap.indexOf(']', indexFramerate)); + } +// PApplet.println("=======>", cap); + if (0 < stringHeight.indexOf("{")) { + // A list of heights... something like "height=(int){ 448, 600 } + stringHeight = stringHeight.substring(13, stringHeight.length() - 1); + String[] values = stringHeight.split(","); + for (String value: values) { + stringHeight = "height=(int)" + value.trim(); + addCapStringsToList(stringWidth, stringHeight, stringFramerate, devCaps); + } + } else { + addCapStringsToList(stringWidth, stringHeight, stringFramerate, devCaps); + } + } + + String[] out = new String[0]; + return devCaps.toArray(out); + } + + static private void addCapStringsToList(String stringWidth, String stringHeight, String stringFramerate, ArrayList devCaps) { + if (0 < stringWidth.split("=").length) { // Expecting a string of the form "width=(int)1600" + stringWidth = stringWidth.substring(11); + try { + Integer.parseInt(stringWidth); + } catch (NumberFormatException ex) { + stringHeight = ""; + } + } + if (0 < stringHeight.split("=").length) { // Expecting a string of the form "height=(int)896" + stringHeight = stringHeight.substring(12); + try { + Integer.parseInt(stringHeight); + } catch (NumberFormatException ex) { + stringHeight = ""; + } + } + if (0 < stringFramerate.split("=,").length) { // Expecting a string of the form "framerate=(fraction)[ 5/1, 10000000/333333" + stringFramerate = stringFramerate.substring(stringFramerate.indexOf("=")); + String[] fpsParts = stringFramerate.split(","); + if (1 < fpsParts.length) { + stringFramerate = fpsParts[1].trim(); + fpsParts = stringFramerate.split("/"); + if (fpsParts.length == 2) { + try { + int fpsNumerator = Integer.parseInt(fpsParts[0]); + int fpsDenominator = Integer.parseInt(fpsParts[1]); + int fps = fpsNumerator / fpsDenominator; + stringFramerate = String.valueOf(fps); + } catch (NumberFormatException ex) { + stringFramerate = ""; + } + } + } + } + if (!stringWidth.equals("") && !stringHeight.equals("") && !stringFramerate.equals("")) { + devCaps.add("size=" + stringWidth + "x" + stringHeight + ",fps=" + stringFramerate); + } } static private String assignDisplayName(Device d, int pos) { diff --git a/src/processing/video/LibraryLoader.java b/src/processing/video/LibraryLoader.java index a2b7395..62649db 100755 --- a/src/processing/video/LibraryLoader.java +++ b/src/processing/video/LibraryLoader.java @@ -298,10 +298,9 @@ public interface DummyLibrary extends Library { { "gmodule-2.0", new String[] {}, false }, { "gthread-2.0", new String[] {}, false }, - // Core GStreamer libraries... somehow the order of these - // libraries is important. For example, if gstbase comes - // before gstreamer, then pluggin scanning crashes with - // "cannot register existing type 'GstObject'" error + // Core GStreamer libraries... the order of these libraries is important (while it does + // not seem to matter for Windows. For example, if gstbase comes before gstreamer, then + // plugin scanning crashes with "cannot register existing type 'GstObject'" error { "gstreamer-1.0", new String[] {}, false }, { "gstbase-1.0", new String[] {}, false }, { "gsturidownloader-1.0", new String[] {}, false },