diff --git a/.gitignore b/.gitignore index ae18b21..acee6fc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ bin /library/windows* /library/linux* +/library/video.jar + local.properties diff --git a/build.xml b/build.xml index f874f23..1a6517e 100755 --- a/build.xml +++ b/build.xml @@ -63,7 +63,7 @@ ${line.separator}with the path to where you have the code for Processing 4 check - + Code signing will only work if you have a $99/yr Apple developer ID. @@ -76,7 +76,7 @@ ${line.separator}with the path to where you have the code for Processing 4 check - + @@ -91,11 +91,11 @@ ${line.separator}with the path to where you have the code for Processing 4 check - + - + @@ -133,7 +133,7 @@ ${line.separator}with the path to where you have the code for Processing 4 check - + @@ -149,7 +149,7 @@ ${line.separator}with the path to where you have the code for Processing 4 check Check on notarization status with: - xcrun altool -u $VIDEO_APPLE_ID -p $VIDEO_LIB_PASSWORD --notarization-info [the RequestUUID above] + xcrun altool -u $APPLE_ID -p $VIDEO_LIB_PASSWORD --notarization-info [the RequestUUID above] @@ -159,9 +159,10 @@ ${line.separator}with the path to where you have the code for Processing 4 check - - - + + + + @@ -178,7 +179,7 @@ ${line.separator}with the path to where you have the code for Processing 4 check - - - - - - - - - - + + + + + + + + + + + + + + + - + + + + Creating zip package... + + + + + + + + + + + + Done! + diff --git a/examples/Capture/AsciiVideo/AsciiVideo.pde b/examples/Capture/AsciiVideo/AsciiVideo.pde index 5936428..aadc30c 100644 --- a/examples/Capture/AsciiVideo/AsciiVideo.pde +++ b/examples/Capture/AsciiVideo/AsciiVideo.pde @@ -1,11 +1,11 @@ /** * ASCII Video - * by Ben Fry. + * by Ben Fry. + * * - * * Text characters have been used to represent images since the earliest computers. * This sketch is a simple homage that re-interprets live video as ASCII text. - * See the keyPressed function for more options, like changing the font size. + * See the keyPressed() function for more options, like changing the font size. */ import processing.video.*; @@ -13,14 +13,13 @@ import processing.video.*; Capture video; boolean cheatScreen; -// All ASCII characters, sorted according to their visual density +// Characters sorted according to their visual density String letterOrder = " .`-_':,;^=+/\"|)\\<>)iv%xclrs{*}I?!][1taeo7zjLu" + "nT#JCwfy325Fp6mqSghVd4EgXPGZbYkOA&8U$@KHDBWNMR0Q"; char[] letters; float[] bright; -char[] chars; PFont font; float fontSize = 1.5; @@ -29,13 +28,13 @@ float fontSize = 1.5; void setup() { size(640, 480); - // This the default video input, see the GettingStartedCapture + // This the default video input, see the GettingStartedCapture // example if it creates an error video = new Capture(this, 160, 120); - + // Start capturing the images from the camera - video.start(); - + video.start(); + int count = video.width * video.height; //println(count); @@ -49,9 +48,6 @@ void setup() { letters[i] = letterOrder.charAt(index); } - // current characters for each position in the video - chars = new char[count]; - // current brightness for each point bright = new float[count]; for (int i = 0; i < count; i++) { @@ -87,7 +83,7 @@ void draw() { pushMatrix(); for (int x = 0; x < video.width; x++) { int pixelColor = video.pixels[index]; - // Faster method of calculating r, g, b than red(), green(), blue() + // Faster method of calculating r, g, b than red(), green(), blue() int r = (pixelColor >> 16) & 0xff; int g = (pixelColor >> 8) & 0xff; int b = pixelColor & 0xff; @@ -105,7 +101,7 @@ void draw() { fill(pixelColor); int num = int(bright[index]); text(letters[num], 0, 0); - + // Move to the next pixel index++; 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 cb25752..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 = 9 -prettyVersion = 2.1 +version = 12 +prettyVersion = 2.2.2 minRevision = 1281 maxRevision = 0 diff --git a/library/.gitignore b/library/.gitignore deleted file mode 100644 index 374cc6c..0000000 --- a/library/.gitignore +++ /dev/null @@ -1 +0,0 @@ -video.jar diff --git a/library/jna.jar b/library/jna.jar old mode 100755 new mode 100644 index af5dd08..77f8c7a Binary files a/library/jna.jar and b/library/jna.jar differ diff --git a/scripts/macosx_gstreamer_install.sh b/scripts/macosx_gstreamer_install.sh index a4c9ec5..7d137bd 100755 --- a/scripts/macosx_gstreamer_install.sh +++ b/scripts/macosx_gstreamer_install.sh @@ -1,55 +1,22 @@ #!/bin/bash -# This script downloads the GStreamer image with separate package to install -# only those needed by Processing: -# https://gstreamer.freedesktop.org/documentation/deploying/mac-osx.html?gi-language=c#deploy-only-necessary-packages-using-the-provided-ones +# This script downloads the latest GStreamer universal runtime package -GST_VERSION=${1:-1.18.0} -INSTALL_PACKAGES=0 # version 1.16.0 does not yet have a package image +GST_VERSION=${1:-1.20.3} GST_PKG_URL="https://gstreamer.freedesktop.org/data/pkg/osx" DOWNLOAD_PATH="." TARGET_PATH="/" CURRENT_PATH=`pwd` -if [ $INSTALL_PACKAGES -eq 1 ] -then - echo "PACKAGES INSTALL..." +echo "FULL INSTALL..." - SRC_FILE="$GST_PKG_URL/$GST_VERSION/gstreamer-1.0-$GST_VERSION-x86_64-packages.dmg" - DEST_FILE="$DOWNLOAD_PATH/gstreamer-1.0-$GST_VERSION-x86_64-packages.dmg" +SRC_FILE="$GST_PKG_URL/$GST_VERSION/gstreamer-1.0-$GST_VERSION-universal.pkg" +DEST_FILE="$DOWNLOAD_PATH/gstreamer-1.0-$GST_VERSION-universal.pkg" - curl $SRC_FILE --output $DEST_FILE +curl $SRC_FILE --output $DEST_FILE - sudo hdiutil attach $DOWNLOAD_PATH/gstreamer-1.0-$GST_VERSION-x86_64-packages.dmg - cd /Volumes/hdidir +sudo installer -pkg $DOWNLOAD_PATH/gstreamer-1.0-$GST_VERSION-universal.pkg -target $TARGET_PATH - sudo installer -pkg base-system-1.0-${GST_VERSION}-x86_64.pkg -target $TARGET_PATH - sudo installer -pkg base-crypto-${GST_VERSION}-x86_64.pkg -target $TARGET_PATH - sudo installer -pkg gstreamer-1.0-system-${GST_VERSION}-x86_64.pkg -target $TARGET_PATH - sudo installer -pkg gstreamer-1.0-core-${GST_VERSION}-x86_64.pkg -target $TARGET_PATH - sudo installer -pkg gstreamer-1.0-playback-${GST_VERSION}-x86_64.pkg -target $TARGET_PATH - sudo installer -pkg gstreamer-1.0-capture-${GST_VERSION}-x86_64.pkg -target $TARGET_PATH - sudo installer -pkg gstreamer-1.0-codecs-${GST_VERSION}-x86_64.pkg -target $TARGET_PATH - sudo installer -pkg gstreamer-1.0-libav-${GST_VERSION}-x86_64.pkg -target $TARGET_PATH - sudo installer -pkg gstreamer-1.0-net-${GST_VERSION}-x86_64.pkg -target $TARGET_PATH - sudo installer -pkg gstreamer-1.0-editing-${GST_VERSION}-x86_64.pkg -target $TARGET_PATH +rm $DOWNLOAD_PATH/gstreamer-1.0-$GST_VERSION-universal.pkg - cd $CURRENT_PATH - hdiutil detach /Volumes/hdidir - rm $DOWNLOAD_PATH/gstreamer-1.0-$GST_VERSION-x86_64-packages.dmg - - echo "DONE..." -else - echo "FULL INSTALL..." - - SRC_FILE="$GST_PKG_URL/$GST_VERSION/gstreamer-1.0-$GST_VERSION-x86_64.pkg" - DEST_FILE="$DOWNLOAD_PATH/gstreamer-1.0-$GST_VERSION-x86_64.pkg" - - curl $SRC_FILE --output $DEST_FILE - - sudo installer -pkg $DOWNLOAD_PATH/gstreamer-1.0-$GST_VERSION-x86_64.pkg -target $TARGET_PATH - - rm $DOWNLOAD_PATH/gstreamer-1.0-$GST_VERSION-x86_64.pkg - - echo "DONE..." -fi \ No newline at end of file +echo "DONE..." \ No newline at end of file diff --git a/scripts/macosx_remove_extra_libs.py b/scripts/macosx_remove_extra_libs.py index 73474b8..640c7ea 100755 --- a/scripts/macosx_remove_extra_libs.py +++ b/scripts/macosx_remove_extra_libs.py @@ -12,7 +12,7 @@ import subprocess import re -lib_folder = '../library/macosx' +lib_folder = '../library/macos-universal' # First, remove libraries from packages we don't bundle with the video library: # gstreamer-1.0-codecs-gpl diff --git a/scripts/pack_linux_libs.sh b/scripts/pack_linux_libs.sh new file mode 100755 index 0000000..e19a4d8 --- /dev/null +++ b/scripts/pack_linux_libs.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# 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 +mkdir ../library/linux-amd64 +cp -a ${meson_build_folder}/* ../library/linux-amd64 + +# Remove static .a libs +rm -r ../library/linux-amd64/*.a + +# Remove unncessary folders +rm -r ../library/linux-amd64/cairo +rm -r ../library/linux-amd64/cmake +rm -r ../library/linux-amd64/gio +rm -r ../library/linux-amd64/glib-2.0 +rm -r ../library/linux-amd64/gst-validate-launcher +rm -r ../library/linux-amd64/pkgconfig +rm -r ../library/linux-amd64/gstreamer-1.0/validate \ No newline at end of file diff --git a/scripts/pack_macosx_libs.sh b/scripts/pack_macosx_libs.sh index 8d34ffb..3b2c997 100755 --- a/scripts/pack_macosx_libs.sh +++ b/scripts/pack_macosx_libs.sh @@ -19,25 +19,54 @@ else fi gst_folder=/Library/Frameworks/GStreamer.framework/Versions/1.0/lib -lib_folder=../library/macosx +lib_folder_univ=../library/macos-universal +lib_folder_x86_64=../library/macos-x86_64 +lib_folder_aarch64=../library/macos-aarch64 echo "Copying base gstreamer libs..." -mkdir -p ${lib_folder} -cp ${gst_folder}/*.dylib ${lib_folder} +mkdir -p ${lib_folder_univ} +cp ${gst_folder}/*.dylib ${lib_folder_univ} echo "Relocating dependencies in base libs..." -./macosx_relocator.py ${lib_folder} ${dep_path} "@loader_path/" +./macosx_relocator.py ${lib_folder_univ} ${dep_path} "@loader_path/" echo "Copying gstreamer plugins..." -mkdir -p ${lib_folder}/gstreamer-1.0 -cp ${gst_folder}/gstreamer-1.0/* ${lib_folder}/gstreamer-1.0 +mkdir -p ${lib_folder_univ}/gstreamer-1.0 +cp ${gst_folder}/gstreamer-1.0/* ${lib_folder_univ}/gstreamer-1.0 -# Silence runtime error from these plugins -rm -f ${lib_folder}/gstreamer-1.0/libgsthls.so -rm -f ${lib_folder}/gstreamer-1.0/libgstopenjpeg.so +# Remove plugins that give runtime errors: +rm -f ${lib_folder_univ}/gstreamer-1.0/libgstsrt.dylib +rm -f ${lib_folder_univ}/gstreamer-1.0/libgstsrtp.dylib + +# These seem okay now (with GStreamer 1.20.x) +# rm -f ${lib_folder_univ}/gstreamer-1.0/libgsthls.so +# rm -f ${lib_folder_univ}/gstreamer-1.0/libgstopenjpeg.so echo "Relocating dependencies in gstreamer plugins..." -./macosx_relocator.py ${lib_folder}/gstreamer-1.0 ${dep_path} "@loader_path/../" +./macosx_relocator.py ${lib_folder_univ}/gstreamer-1.0 ${dep_path} "@loader_path/../" echo "Removing unused dependencies..." -./macosx_remove_extra_libs.py \ No newline at end of file +./macosx_remove_extra_libs.py + +echo "Extracting x86_64 and aarch64 native libraries..." + +mkdir -p ${lib_folder_x86_64} +mkdir -p ${lib_folder_aarch64} +for file in ${lib_folder_univ}/*.dylib; do + fn="$(basename ${file})" + lipo ${file} -thin x86_64 -output ${lib_folder_x86_64}/${fn}; + lipo ${file} -thin arm64 -output ${lib_folder_aarch64}/${fn}; +done + +mkdir -p ${lib_folder_x86_64}/gstreamer-1.0 +mkdir -p ${lib_folder_aarch64}/gstreamer-1.0 +for file in ${lib_folder_univ}/gstreamer-1.0/*.dylib; do + fn="$(basename ${file})" + lipo ${file} -thin x86_64 -output ${lib_folder_x86_64}/gstreamer-1.0/${fn}; + lipo ${file} -thin arm64 -output ${lib_folder_aarch64}/gstreamer-1.0/${fn}; +done + +echo "Removing universal native libraries..." +rm -rf ${lib_folder_univ} + +echo "Done." \ No newline at end of file diff --git a/scripts/pack_windows_libs.bat b/scripts/pack_windows_libs.bat old mode 100644 new mode 100755 index 063a09c..0844b4c --- a/scripts/pack_windows_libs.bat +++ b/scripts/pack_windows_libs.bat @@ -1,46 +1,11 @@ -SET gst_folder=C:\gstreamer -SET gst_toolchain= -SET lib_folder=..\library - -echo "Copying base gstreamer libs..." -md %lib_folder%\windows64 %lib_folder%\windows32 -copy %gst_folder%\1.0\%gst_toolchain%x86_64\bin\*.dll %lib_folder%\windows64 -copy %gst_folder%\1.0\%gst_toolchain%x86\bin\*.dll %lib_folder%\windows32 - -echo "Copying gstreamer plugins..." -md %lib_folder%\windows64\gstreamer-1.0 %lib_folder%\windows32\gstreamer-1.0 -copy %gst_folder%\1.0\%gst_toolchain%x86_64\lib\gstreamer-1.0\*.dll %lib_folder%\windows64\gstreamer-1.0 -copy %gst_folder%\1.0\%gst_toolchain%x86\lib\gstreamer-1.0\*.dll %lib_folder%\windows32\gstreamer-1.0 - -echo "Remove broken plugins..." -del %lib_folder%\windows64\gstreamer-1.0\libgsta52dec.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstamrnb.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstamrwbdec.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstassrender.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstdtsdec.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstdvdread.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstges.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstlibvisual.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstmms.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstresindvd.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstrtmp.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstsoundtouch.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstvoaacenc.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstwebrtcdsp.dll /f -del %lib_folder%\windows64\gstreamer-1.0\libgstx264.dll /f - -del %lib_folder%\windows32\gstreamer-1.0\libgsta52dec.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstamrnb.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstamrwbdec.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstassrender.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstdtsdec.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstdvdread.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstges.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstlibvisual.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstmms.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstresindvd.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstrtmp.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstsoundtouch.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstvoaacenc.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstwebrtcdsp.dll /f -del %lib_folder%\windows32\gstreamer-1.0\libgstx264.dll /f \ No newline at end of file +SET gst_folder=C:\gstreamer +SET gst_toolchain=msvc +SET lib_folder=..\library + +echo "Copying base gstreamer libs..." +md %lib_folder%\windows-amd64 +copy %gst_folder%\1.0\%gst_toolchain%_x86_64\bin\*.dll %lib_folder%\windows-amd64 + +echo "Copying gstreamer plugins..." +md %lib_folder%\windows-amd64\gstreamer-1.0 +copy %gst_folder%\1.0\%gst_toolchain%_x86_64\lib\gstreamer-1.0\*.dll %lib_folder%\windows-amd64\gstreamer-1.0 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 9f89310..62649db 100755 --- a/src/processing/video/LibraryLoader.java +++ b/src/processing/video/LibraryLoader.java @@ -1,351 +1,527 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2012-22 The Processing Foundation - Copyright (c) 2011-12 Ben Fry and Casey Reas - GStreamer implementation ported from GSVideo library by Andres Colubri - Library loader based on code by Tal Shalif - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA -*/ - -package processing.video; - -import java.util.HashMap; -import java.util.Map; - -import com.sun.jna.Library; -import com.sun.jna.Native; -import com.sun.jna.Platform; - -/** - * This class loads the gstreamer native libraries. - * - */ -public class LibraryLoader { - - public interface DummyLibrary extends Library { - } - - private static LibraryLoader instance; - - static final Object[][] WINDOWS_DEPENDENCIES = { - // Core GStreamer libraries - { "libgstadaptivedemux-1.0-0", new String[] {}, false }, - { "libgstallocators-1.0-0", new String[] {}, false }, - { "libgstapp-1.0-0", new String[] {}, false }, - { "libgstaudio-1.0-0", new String[] {}, false }, - { "libgstbadaudio-1.0-0", new String[] {}, false }, - { "libgstbase-1.0-0", new String[] {}, false }, - { "libgstbasecamerabinsrc-1.0-0", new String[] {}, false }, - { "libgstcheck-1.0-0", new String[] {}, false }, - { "libgstcodecparsers-1.0-0", new String[] {}, false }, - { "libgstcontroller-1.0-0", new String[] {}, false }, - { "libgstfft-1.0-0", new String[] {}, false }, - { "libgstgl-1.0-0", new String[] {}, false }, - { "libgstinsertbin-1.0-0", new String[] {}, false }, - { "libgstisoff-1.0-0", new String[] {}, false }, - { "libgstmpegts-1.0-0", new String[] {}, false }, - { "libgstnet-1.0-0", new String[] {}, false }, - { "libgstpbutils-1.0-0", new String[] {}, false }, - { "libgstphotography-1.0-0", new String[] {}, false }, - { "libgstplayer-1.0-0", new String[] {}, false }, - { "libgstreamer-1.0-0", new String[] {}, false }, - { "libgstriff-1.0-0", new String[] {}, false }, - { "libgstrtp-1.0-0", new String[] {}, false }, - { "libgstrtsp-1.0-0", new String[] {}, false }, - { "libgstrtspserver-1.0-0", new String[] {}, false }, - { "libgstsctp-1.0-0", new String[] {}, false }, - { "libgstsdp-1.0-0", new String[] {}, false }, - { "libgsttag-1.0-0", new String[] {}, false }, - { "libgsturidownloader-1.0-0", new String[] {}, false }, - { "libgstvideo-1.0-0", new String[] {}, false }, - { "libgstwebrtc-1.0-0", new String[] {}, false }, - - // External libraries - { "libbz2", new String[] {}, false }, - { "libcairo-2", new String[] {}, false }, - { "libcairo-gobject-2", new String[] {}, false }, - { "libcairo-script-interpreter-2", new String[] {}, false }, - { "libcroco-0.6-3", new String[] {}, false }, - { "libcrypto-1_1-x64", new String[] {}, false }, - { "libdv-4", new String[] {}, false }, - { "libexpat-1", new String[] {}, false }, - { "libffi-7", new String[] {}, false }, - { "libFLAC-8", new String[] {}, false }, - { "libfontconfig-1", new String[] {}, false }, - { "libfreetype-6", new String[] {}, false }, - { "libfribidi-0", new String[] {}, false }, - { "libgcc_s_sjlj-1", new String[] {}, false }, - { "libgdk_pixbuf-2.0-0", new String[] {}, false }, - { "libgio-2.0-0", new String[] {}, false }, - { "libglib-2.0-0", new String[] {}, false }, - { "libgmodule-2.0-0", new String[] {}, false }, - { "libgmp-10", new String[] {}, false }, - { "libgnutls-30", new String[] {}, false }, - { "libgnutlsxx-28", new String[] {}, false }, - { "libgobject-2.0-0", new String[] {}, false }, - { "libgomp-1", new String[] {}, false }, - { "libgraphene-1.0-0", new String[] {}, false }, - { "libgthread-2.0-0", new String[] {}, false }, - { "libharfbuzz-0", new String[] {}, false }, - { "libhogweed-4", new String[] {}, false }, - { "libintl-8", new String[] {}, false }, - { "libjpeg-8", new String[] {}, false }, - { "libjson-glib-1.0-0", new String[] {}, false }, - { "libkate-1", new String[] {}, false }, - { "libmp3lame-0", new String[] {}, false }, - { "libmpg123-0", new String[] {}, false }, - { "libnettle-6", new String[] {}, false }, - { "libnice-10", new String[] {}, false }, - { "libogg-0", new String[] {}, false }, - { "liboggkate-1", new String[] {}, false }, - { "libopenh264", new String[] {}, false }, - { "libopenjp2", new String[] {}, false }, - { "libopus-0", new String[] {}, false }, - { "liborc-0.4-0", new String[] {}, false }, - { "liborc-test-0.4-0", new String[] {}, false }, - { "libpango-1.0-0", new String[] {}, false }, - { "libpangocairo-1.0-0", new String[] {}, false }, - { "libpangoft2-1.0-0", new String[] {}, false }, - { "libpangowin32-1.0-0", new String[] {}, false }, - { "libpixman-1-0", new String[] {}, false }, - { "libpng16-16", new String[] {}, false }, - { "librsvg-2-2", new String[] {}, false }, - { "libsbc-1", new String[] {}, false }, - { "libsoup-2.4-1", new String[] {}, false }, - { "libspandsp-2", new String[] {}, false }, - { "libspeex-1", new String[] {}, false }, - { "libsrt", new String[] {}, false }, - { "libsrtp", new String[] {}, false }, - { "libssl-1_1-x64", new String[] {}, false }, - { "libstdc++-6", new String[] {}, false }, - { "libtag", new String[] {}, false }, - { "libtasn1-6", new String[] {}, false }, - { "libtheora-0", new String[] {}, false }, - { "libtheoradec-1", new String[] {}, false }, - { "libtheoraenc-1", new String[] {}, false }, - { "libtiff-5", new String[] {}, false }, - { "libturbojpeg-0", new String[] {}, false }, - { "libusrsctp-1", new String[] {}, false }, - { "libvorbis-0", new String[] {}, false }, - { "libvorbisenc-2", new String[] {}, false }, - { "libvorbisfile-3", new String[] {}, false }, - { "libwavpack-1", new String[] {}, false }, - { "libwinpthread-1", new String[] {}, false }, - { "libxml2-2", new String[] {}, false }, - { "libz-1", new String[] {}, false }, - { "avcodec-58", new String[] {}, false }, - { "avfilter-7", new String[] {}, false }, - { "avformat-58", new String[] {}, false }, - { "avutil-56", new String[] {}, false }, - { "swresample-3", new String[] {}, false } - }; - - static final Object[][] LINUX_DEPENDENCIES = { - // GLib libraries - { "glib-2.0", new String[] {}, false }, - { "gobject-2.0", new String[] {}, false }, - { "gio-2.0", new String[] {}, false }, - { "gmodule-2.0", new String[] {}, false }, - { "gthread-2.0", new String[] {}, false }, - - // Core GStreamer libraries - { "gstreamer-1.0", new String[] {}, false }, - { "gstbase-1.0", new String[] {}, false }, - { "gsturidownloader-1.0", new String[] {}, false }, - { "gstadaptivedemux-1.0", new String[] {}, false }, - { "gstapp-1.0", new String[] {}, false }, - { "gsttag-1.0", new String[] {}, false }, - { "gstvideo-1.0", new String[] {}, false }, - { "gstaudio-1.0", new String[] {}, false }, - { "gstpbutils-1.0", new String[] {}, false }, - { "gstplayer-1.0", new String[] {}, false }, - { "gstbadaudio-1.0", new String[] {}, false }, - { "gstbasecamerabinsrc-1.0", new String[] {}, false }, - { "gstcheck-1.0", new String[] {}, false }, - { "gstcodecparsers-1.0", new String[] {}, false }, - { "gstcontroller-1.0", new String[] {}, false }, - { "gstfft-1.0", new String[] {}, false }, - { "gstinsertbin-1.0", new String[] {}, false }, - { "gstisoff-1.0", new String[] {}, false }, - { "gstmpegts-1.0", new String[] {}, false }, - { "gstnet-1.0", new String[] {}, false }, - { "gstphotography-1.0", new String[] {}, false }, - { "gstallocators-1.0", new String[] {}, false }, - { "gstriff-1.0", new String[] {}, false }, - { "gstrtp-1.0", new String[] {}, false }, - { "gstrtsp-1.0", new String[] {}, false }, - { "gstsdp-1.0", new String[] {}, false }, - { "gstsctp-1.0", new String[] {}, false }, - { "gstrtspserver-1.0", new String[] {}, false }, - { "gstvalidate-1.0", new String[] {}, false }, - { "gstvalidate-default-overrides-1.0", new String[] {}, false }, - { "gstwebrtc-1.0", new String[] {}, false }, - - // External libraries - { "avutil", new String[] {}, false }, - { "swresample", new String[] {}, false }, - { "swscale", new String[] {}, false }, - { "avcodec", new String[] {}, false }, - { "avformat", new String[] {}, false }, - { "avresample", new String[] {}, false }, - { "avfilter", new String[] {}, false }, - { "avdevice", new String[] {}, false }, - { "ges-1.0", new String[] {}, false }, - { "json-glib-1.0", new String[] {}, false }, - { "nice", new String[] {}, false }, - { "x264", new String[] {}, false }, - { "openh264", new String[] {}, false }, - { "orc-0.4", new String[] {}, false }, - { "orc-test-0.4", new String[] {}, false }, - { "postproc", new String[] {}, false } - }; - - static final Object[][] MACOS_DEPENDENCIES = { }; - - static final Object[][] DEFAULT_DEPENDENCIES = { }; - - static final Object[][] dependencies = - Platform.isWindows() ? WINDOWS_DEPENDENCIES : - Platform.isLinux() ? LINUX_DEPENDENCIES : - Platform.isMac() ? MACOS_DEPENDENCIES : DEFAULT_DEPENDENCIES; - - - private static final Map loadedMap = - new HashMap<>(); - - - private static final int RECURSIVE_LOAD_MAX_DEPTH = 5; - - - private LibraryLoader() { - } - - - private void preLoadLibs() { - for (Object[] a : dependencies) { - load(a[0].toString(), DummyLibrary.class, true, 0, (Boolean) a[2]); - } - } - - - static private String[] findDeps(String name) { - - for (Object[] a : dependencies) { - if (name.equals(a[0])) { - - return (String[]) a[1]; - } - } - - // library dependency load chain unspecified - probably client call - return new String[] { }; - } - - - public Object load(String name, Class clazz, boolean reqLib) { - return load(name, clazz, true, 0, reqLib); - } - - - private Object load(String name, Class clazz, boolean forceReload, - int depth, boolean reqLib) { - - assert depth < RECURSIVE_LOAD_MAX_DEPTH : String.format( - "recursive max load depth %s has been exceeded", depth); - - Object library = loadedMap.get(name); - - if (null == library || forceReload) { - - // Logger.getAnonymousLogger().info(String.format("%" + ((depth + 1) * 2) - // + "sloading %s", "->", name)); - - try { - String[] deps = findDeps(name); - - for (String lib : deps) { - load(lib, DummyLibrary.class, false, depth + 1, reqLib); - } - - library = loadLibrary(name, clazz, reqLib); - - if (library != null) { - loadedMap.put(name, library); - } - } catch (Exception e) { - if (reqLib) - throw new RuntimeException(String.format( - "can not load required library %s", name, e)); - else - System.out.println(String.format("can not load library %s", name, e)); - } - } - - return library; - } - - - private static Object loadLibrary(String name, Class clazz, - boolean reqLib) { - - // Logger.getAnonymousLogger().info(String.format("loading %s", name)); - - String[] nameFormats; - nameFormats = Platform.isWindows() ? new String[] { "lib%s", "lib%s-0", - "%s" } : new String[] { "%s-0", "%s" }; - - UnsatisfiedLinkError linkError = null; - - for (String fmt : nameFormats) { - try { - String s = String.format(fmt, name); - //System.out.println("Trying to load library file " + s); - Object obj = Native.loadLibrary(s, clazz); - //System.out.println("Loaded library " + s + " successfully!"); - return obj; - } catch (UnsatisfiedLinkError ex) { - linkError = ex; - } - } - - if (reqLib) - throw new UnsatisfiedLinkError( - String.format( - "can't load library %s (%1$s|lib%1$s|lib%1$s-0) with " + - "-Djna.library.path=%s. Last error:%s", - name, System.getProperty("jna.library.path"), linkError)); - else { - System.out.println(String.format( - "can't load library %s (%1$s|lib%1$s|lib%1$s-0) with " + - "-Djna.library.path=%s. Last error:%s", - name, System.getProperty("jna.library.path"), linkError)); - return null; - } - } - - - public static synchronized LibraryLoader getInstance() { - if (null == instance) { - instance = new LibraryLoader(); - instance.preLoadLibs(); - } - return instance; - } -} +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-22 The Processing Foundation + Copyright (c) 2011-12 Ben Fry and Casey Reas + GStreamer implementation ported from GSVideo library by Andres Colubri + Library loader based on code by Tal Shalif + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.video; + +import java.util.HashMap; +import java.util.Map; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Platform; + +/** + * This class loads the gstreamer native libraries. + * + */ +public class LibraryLoader { + + public interface DummyLibrary extends Library { + } + + private static LibraryLoader instance; + + static final Object[][] WINDOWS_MINGW_DEPENDENCIES = { + // Base GStreamer native libraries for a COMPLETE MINGW installation + { "avcodec-58", new String[] {}, false }, + { "avfilter-7", new String[] {}, false }, + { "avformat-58", new String[] {}, false }, + { "avutil-56", new String[] {}, false }, + { "libass-9", new String[] {}, false }, + { "libbz2", new String[] {}, false }, + { "libcairo-2", new String[] {}, false }, + { "libcairo-gobject-2", new String[] {}, false }, + { "libcairo-script-interpreter-2", new String[] {}, false }, + { "libcharset-1", new String[] {}, false }, + { "libcroco-0.6-3", new String[] {}, false }, + { "libcrypto-1_1-x64", new String[] {}, false }, + { "libdca-0", new String[] {}, false }, + { "libdv-4", new String[] {}, false }, + { "libexpat-1", new String[] {}, false }, + { "libffi-7", new String[] {}, false }, + { "libFLAC-8", new String[] {}, false }, + { "libfontconfig-1", new String[] {}, false }, + { "libfreetype-6", new String[] {}, false }, + { "libfribidi-0", new String[] {}, false }, + { "libgcc_s_seh-1", new String[] {}, false }, + { "libgdk_pixbuf-2.0-0", new String[] {}, false }, + { "libges-1.0-0", new String[] {}, false }, + { "libgio-2.0-0", new String[] {}, false }, + { "libglib-2.0-0", new String[] {}, false }, + { "libgmodule-2.0-0", new String[] {}, false }, + { "libgobject-2.0-0", new String[] {}, false }, + { "libgraphene-1.0-0", new String[] {}, false }, + { "libgstadaptivedemux-1.0-0", new String[] {}, false }, + { "libgstallocators-1.0-0", new String[] {}, false }, + { "libgstapp-1.0-0", new String[] {}, false }, + { "libgstaudio-1.0-0", new String[] {}, false }, + { "libgstbadaudio-1.0-0", new String[] {}, false }, + { "libgstbase-1.0-0", new String[] {}, false }, + { "libgstbasecamerabinsrc-1.0-0", new String[] {}, false }, + { "libgstcheck-1.0-0", new String[] {}, false }, + { "libgstcodecparsers-1.0-0", new String[] {}, false }, + { "libgstcodecs-1.0-0", new String[] {}, false }, + { "libgstcontroller-1.0-0", new String[] {}, false }, + { "libgstd3d11-1.0-0", new String[] {}, false }, + { "libgstfft-1.0-0", new String[] {}, false }, + { "libgstgl-1.0-0", new String[] {}, false }, + { "libgstinsertbin-1.0-0", new String[] {}, false }, + { "libgstisoff-1.0-0", new String[] {}, false }, + { "libgstmpegts-1.0-0", new String[] {}, false }, + { "libgstnet-1.0-0", new String[] {}, false }, + { "libgstpbutils-1.0-0", new String[] {}, false }, + { "libgstphotography-1.0-0", new String[] {}, false }, + { "libgstplay-1.0-0", new String[] {}, false }, + { "libgstplayer-1.0-0", new String[] {}, false }, + { "libgstreamer-1.0-0", new String[] {}, false }, + { "libgstriff-1.0-0", new String[] {}, false }, + { "libgstrtp-1.0-0", new String[] {}, false }, + { "libgstrtsp-1.0-0", new String[] {}, false }, + { "libgstrtspserver-1.0-0", new String[] {}, false }, + { "libgstsctp-1.0-0", new String[] {}, false }, + { "libgstsdp-1.0-0", new String[] {}, false }, + { "libgsttag-1.0-0", new String[] {}, false }, + { "libgsttranscoder-1.0-0", new String[] {}, false }, + { "libgsturidownloader-1.0-0", new String[] {}, false }, + { "libgstvalidate-1.0-0", new String[] {}, false }, + { "libgstvideo-1.0-0", new String[] {}, false }, + { "libgstwebrtc-1.0-0", new String[] {}, false }, + { "libgthread-2.0-0", new String[] {}, false }, + { "libharfbuzz-0", new String[] {}, false }, + { "libiconv-2", new String[] {}, false }, + { "libintl-8", new String[] {}, false }, + { "libjpeg-8", new String[] {}, false }, + { "libjson-glib-1.0-0", new String[] {}, false }, + { "libkate-1", new String[] {}, false }, + { "libmp3lame-0", new String[] {}, false }, + { "libmpg123-0", new String[] {}, false }, + { "libnice-10", new String[] {}, false }, + { "libogg-0", new String[] {}, false }, + { "liboggkate-1", new String[] {}, false }, + { "libopencore-amrnb-0", new String[] {}, false }, + { "libopencore-amrwb-0", new String[] {}, false }, + { "libopenh264-6", new String[] {}, false }, + { "libopenjp2", new String[] {}, false }, + { "libopus-0", new String[] {}, false }, + { "liborc-0.4-0", new String[] {}, false }, + { "liborc-test-0.4-0", new String[] {}, false }, + { "libpango-1.0-0", new String[] {}, false }, + { "libpangocairo-1.0-0", new String[] {}, false }, + { "libpangoft2-1.0-0", new String[] {}, false }, + { "libpangowin32-1.0-0", new String[] {}, false }, + { "libpixman-1-0", new String[] {}, false }, + { "libpng16-16", new String[] {}, false }, + { "libpsl-5", new String[] {}, false }, + { "librsvg-2-2", new String[] {}, false }, + { "librtmp-1", new String[] {}, false }, + { "libsbc-1", new String[] {}, false }, + { "libSoundTouch-1", new String[] {}, false }, + { "libsoup-2.4-1", new String[] {}, false }, + { "libspandsp-2", new String[] {}, false }, + { "libspeex-1", new String[] {}, false }, + { "libsqlite3-0", new String[] {}, false }, + { "libsrt", new String[] {}, false }, + { "libsrtp2-1", new String[] {}, false }, + { "libssl-1_1-x64", new String[] {}, false }, + { "libstdc++-6", new String[] {}, false }, + { "libtag", new String[] {}, false }, + { "libtheora-0", new String[] {}, false }, + { "libtheoradec-1", new String[] {}, false }, + { "libtheoraenc-1", new String[] {}, false }, + { "libtiff-5", new String[] {}, false }, + { "libturbojpeg-0", new String[] {}, false }, + { "libvo-aacenc-0", new String[] {}, false }, + { "libvorbis-0", new String[] {}, false }, + { "libvorbisenc-2", new String[] {}, false }, + { "libvorbisfile-3", new String[] {}, false }, + { "libwavpack", new String[] {}, false }, + { "libwebrtc_audio_processing-0", new String[] {}, false }, + { "libwinpthread-1", new String[] {}, false }, + { "libx264-157", new String[] {}, false }, + { "libxml2-2", new String[] {}, false }, + { "libz-1", new String[] {}, false }, + { "libzbar-0", new String[] {}, false }, + { "swresample-3", new String[] {}, false } + }; + + static final Object[][] WINDOWS_MSVC_DEPENDENCIES = { + // Base GStreamer native libraries for a COMPLETE MSVC installation + { "avcodec-58", new String[] {}, false }, + { "avfilter-7", new String[] {}, false }, + { "avformat-58", new String[] {}, false }, + { "avutil-56", new String[] {}, false }, + { "bz2", new String[] {}, false }, + { "cairo-2", new String[] {}, false }, + { "cairo-gobject-2", new String[] {}, false }, + { "cairo-script-interpreter-2", new String[] {}, false }, + { "dv-4", new String[] {}, false }, + { "ffi-7", new String[] {}, false }, + { "fontconfig-1", new String[] {}, false }, + { "fribidi-0", new String[] {}, false }, + { "gdk_pixbuf-2.0-0", new String[] {}, false }, + { "ges-1.0-0", new String[] {}, false }, + { "gio-2.0-0", new String[] {}, false }, + { "glib-2.0-0", new String[] {}, false }, + { "gmodule-2.0-0", new String[] {}, false }, + { "gobject-2.0-0", new String[] {}, false }, + { "graphene-1.0-0", new String[] {}, false }, + { "gstadaptivedemux-1.0-0", new String[] {}, false }, + { "gstallocators-1.0-0", new String[] {}, false }, + { "gstapp-1.0-0", new String[] {}, false }, + { "gstaudio-1.0-0", new String[] {}, false }, + { "gstbadaudio-1.0-0", new String[] {}, false }, + { "gstbase-1.0-0", new String[] {}, false }, + { "gstbasecamerabinsrc-1.0-0", new String[] {}, false }, + { "gstcheck-1.0-0", new String[] {}, false }, + { "gstcodecparsers-1.0-0", new String[] {}, false }, + { "gstcodecs-1.0-0", new String[] {}, false }, + { "gstcontroller-1.0-0", new String[] {}, false }, + { "gstd3d11-1.0-0", new String[] {}, false }, + { "gstfft-1.0-0", new String[] {}, false }, + { "gstgl-1.0-0", new String[] {}, false }, + { "gstinsertbin-1.0-0", new String[] {}, false }, + { "gstisoff-1.0-0", new String[] {}, false }, + { "gstmpegts-1.0-0", new String[] {}, false }, + { "gstnet-1.0-0", new String[] {}, false }, + { "gstpbutils-1.0-0", new String[] {}, false }, + { "gstphotography-1.0-0", new String[] {}, false }, + { "gstplay-1.0-0", new String[] {}, false }, + { "gstplayer-1.0-0", new String[] {}, false }, + { "gstreamer-1.0-0", new String[] {}, false }, + { "gstriff-1.0-0", new String[] {}, false }, + { "gstrtp-1.0-0", new String[] {}, false }, + { "gstrtsp-1.0-0", new String[] {}, false }, + { "gstrtspserver-1.0-0", new String[] {}, false }, + { "gstsctp-1.0-0", new String[] {}, false }, + { "gstsdp-1.0-0", new String[] {}, false }, + { "gsttag-1.0-0", new String[] {}, false }, + { "gsttranscoder-1.0-0", new String[] {}, false }, + { "gsturidownloader-1.0-0", new String[] {}, false }, + { "gstvalidate-1.0-0", new String[] {}, false }, + { "gstvideo-1.0-0", new String[] {}, false }, + { "gstwebrtc-1.0-0", new String[] {}, false }, + { "gstwinrt-1.0-0", new String[] {}, false }, + { "gthread-2.0-0", new String[] {}, false }, + { "harfbuzz", new String[] {}, false }, + { "intl-8", new String[] {}, false }, + { "json-glib-1.0-0", new String[] {}, false }, + { "libass-9", new String[] {}, false }, + { "libcharset-1", new String[] {}, false }, + { "libcroco-0.6-3", new String[] {}, false }, + { "libcrypto-1_1-x64", new String[] {}, false }, + { "libdca-0", new String[] {}, false }, + { "libexpat-1", new String[] {}, false }, + { "libFLAC-8", new String[] {}, false }, + { "libfreetype-6", new String[] {}, false }, + { "libgcc_s_seh-1", new String[] {}, false }, + { "libiconv-2", new String[] {}, false }, + { "libjpeg-8", new String[] {}, false }, + { "libkate-1", new String[] {}, false }, + { "libmp3lame-0", new String[] {}, false }, + { "libmpg123-0", new String[] {}, false }, + { "libogg-0", new String[] {}, false }, + { "liboggkate-1", new String[] {}, false }, + { "libopencore-amrnb-0", new String[] {}, false }, + { "libopencore-amrwb-0", new String[] {}, false }, + { "libpng16-16", new String[] {}, false }, + { "librsvg-2-2", new String[] {}, false }, + { "librtmp-1", new String[] {}, false }, + { "libsbc-1", new String[] {}, false }, + { "libspandsp-2", new String[] {}, false }, + { "libspeex-1", new String[] {}, false }, + { "libsrt", new String[] {}, false }, + { "libssl-1_1-x64", new String[] {}, false }, + { "libstdc++-6", new String[] {}, false }, + { "libtheora-0", new String[] {}, false }, + { "libtheoradec-1", new String[] {}, false }, + { "libtheoraenc-1", new String[] {}, false }, + { "libtiff-5", new String[] {}, false }, + { "libturbojpeg-0", new String[] {}, false }, + { "libvo-aacenc-0", new String[] {}, false }, + { "libvorbis-0", new String[] {}, false }, + { "libvorbisenc-2", new String[] {}, false }, + { "libvorbisfile-3", new String[] {}, false }, + { "libwinpthread-1", new String[] {}, false }, + { "libx264-157", new String[] {}, false }, + { "libxml2-2", new String[] {}, false }, + { "libzbar-0", new String[] {}, false }, + { "nice-10", new String[] {}, false }, + { "openh264-6", new String[] {}, false }, + { "openjp2", new String[] {}, false }, + { "opus-0", new String[] {}, false }, + { "orc-0.4-0", new String[] {}, false }, + { "orc-test-0.4-0", new String[] {}, false }, + { "pango-1.0-0", new String[] {}, false }, + { "pangocairo-1.0-0", new String[] {}, false }, + { "pangoft2-1.0-0", new String[] {}, false }, + { "pangowin32-1.0-0", new String[] {}, false }, + { "pixman-1-0", new String[] {}, false }, + { "psl-5", new String[] {}, false }, + { "soup-2.4-1", new String[] {}, false }, + { "sqlite3-0", new String[] {}, false }, + { "srtp2-1", new String[] {}, false }, + { "swresample-3", new String[] {}, false }, + { "wavpack", new String[] {}, false }, + { "z-1", new String[] {}, false } + }; + + static final Object[][] LINUX_DEPENDENCIES = { + // Base GStreamer native libraries from a meson build + + // GLib libraries + { "glib-2.0", new String[] {}, false }, + { "gobject-2.0", new String[] {}, false }, + { "gio-2.0", new String[] {}, false }, + { "gmodule-2.0", new String[] {}, false }, + { "gthread-2.0", new String[] {}, false }, + + // 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 }, + { "gstadaptivedemux-1.0", new String[] {}, false }, + { "gstapp-1.0", new String[] {}, false }, + { "gsttag-1.0", new String[] {}, false }, + { "gstvideo-1.0", new String[] {}, false }, + { "gstaudio-1.0", new String[] {}, false }, + { "gstpbutils-1.0", new String[] {}, false }, + { "gstplay-1.0", new String[] {}, false }, + { "gstplayer-1.0", new String[] {}, false }, + { "gstbadaudio-1.0", new String[] {}, false }, + { "gstbasecamerabinsrc-1.0", new String[] {}, false }, + { "gstcheck-1.0", new String[] {}, false }, + { "gstcodecparsers-1.0", new String[] {}, false }, + { "gstcontroller-1.0", new String[] {}, false }, + { "gstfft-1.0", new String[] {}, false }, + { "gstinsertbin-1.0", new String[] {}, false }, + { "gstisoff-1.0", new String[] {}, false }, + { "gstmpegts-1.0", new String[] {}, false }, + { "gstnet-1.0", new String[] {}, false }, + { "gstphotography-1.0", new String[] {}, false }, + { "gstallocators-1.0", new String[] {}, false }, + { "libgstcodecs-1.0", new String[] {}, false }, + { "gstriff-1.0", new String[] {}, false }, + { "gstrtp-1.0", new String[] {}, false }, + { "gstrtsp-1.0", new String[] {}, false }, + { "gstsdp-1.0", new String[] {}, false }, + { "gstsctp-1.0", new String[] {}, false }, + { "gstrtspserver-1.0", new String[] {}, false }, + { "gstvalidate-1.0", new String[] {}, false }, + { "gstvalidate-default-overrides-1.0", new String[] {}, false }, + { "gstwebrtc-1.0", new String[] {}, false }, + { "gsttranscoder-1.0", new String[] {}, false }, + + // External libraries + { "xml2", new String[] {}, false }, + { "avutil", new String[] {}, false }, + { "swresample", new String[] {}, false }, + { "swscale", new String[] {}, false }, + { "avcodec", new String[] {}, false }, + { "avformat", new String[] {}, false }, + { "avresample", new String[] {}, false }, + { "avfilter", new String[] {}, false }, + { "avdevice", new String[] {}, false }, + { "avtp", new String[] {}, false }, + { "cairo-gobject", new String[] {}, false }, + { "cairo-script-interpreter", new String[] {}, false }, + { "cairo", new String[] {}, false }, + { "dv", new String[] {}, false }, + { "fdk_aac", new String[] {}, false }, + { "fontconfig", new String[] {}, false }, + { "freetype", new String[] {}, false }, + { "fribidi", new String[] {}, false }, + { "ges-1.0", new String[] {}, false }, + { "harfbuzz-gobject", new String[] {}, false }, + { "harfbuzz", new String[] {}, false }, + { "harfbuzz-subset", new String[] {}, false }, + { "jpeg", new String[] {}, false }, + { "json-glib-1.0", new String[] {}, false }, + { "microdns", new String[] {}, false }, + { "mp3lame", new String[] {}, false }, + { "nice", new String[] {}, false }, + { "ogg", new String[] {}, false }, + { "openh264", new String[] {}, false }, + { "openjp2", new String[] {}, false }, + { "opus", new String[] {}, false }, + { "orc-0.4", new String[] {}, false }, + { "orc-test-0.4", new String[] {}, false }, + { "pango-1.0", new String[] {}, false }, + // { "pangocairo-1.0", new String[] {}, false }, // Seems broken in 1.20.3 + { "pangoft2-1.0", new String[] {}, false }, + { "pixman-1", new String[] {}, false }, + { "png16", new String[] {}, false }, + { "postproc", new String[] {}, false }, + { "psl", new String[] {}, false }, + { "soup-2.4", new String[] {}, false }, + { "soup-gnome-2.4", new String[] {}, false }, + { "sqlite3", new String[] {}, false }, + { "vorbisenc", new String[] {}, false }, + { "vorbisfile", new String[] {}, false }, + { "vorbis", new String[] {}, false } +}; + + static Object[][] dependencies; + + + private static final Map loadedMap = + new HashMap<>(); + + + private static final int RECURSIVE_LOAD_MAX_DEPTH = 5; + + + private LibraryLoader() { + } + + + private void preLoadLibs(int winBuildType) { + if (Platform.isWindows()) { + if (winBuildType == 0) { + System.err.println("Seems like you are trying to use GStreamer native libraries older than 1.20, which are not supported."); + return; + } else if (winBuildType == 1) { + dependencies = WINDOWS_MINGW_DEPENDENCIES; + } else if (winBuildType == 2) { + dependencies = WINDOWS_MSVC_DEPENDENCIES; + } + + } else if (Platform.isLinux()) { + dependencies = LINUX_DEPENDENCIES; + } else { + // No need for dependencies pre-loading on MacOS + return; + } + + for (Object[] a : dependencies) { + load(a[0].toString(), DummyLibrary.class, true, 0, (Boolean) a[2]); + } + } + + + static private String[] findDeps(String name) { + for (Object[] a : dependencies) { + if (name.equals(a[0])) { + return (String[]) a[1]; + } + } + + // library dependency load chain unspecified - probably client call + return new String[] { }; + } + + + public Object load(String name, Class clazz, boolean reqLib) { + return load(name, clazz, true, 0, reqLib); + } + + + private Object load(String name, Class clazz, boolean forceReload, + int depth, boolean reqLib) { + + assert depth < RECURSIVE_LOAD_MAX_DEPTH : String.format( + "recursive max load depth %s has been exceeded", depth); + + Object library = loadedMap.get(name); + + if (null == library || forceReload) { + + // Logger.getAnonymousLogger().info(String.format("%" + ((depth + 1) * 2) + // + "sloading %s", "->", name)); + + try { + String[] deps = findDeps(name); + + for (String lib : deps) { + load(lib, DummyLibrary.class, false, depth + 1, reqLib); + } + + library = loadLibrary(name, clazz, reqLib); + + if (library != null) { + loadedMap.put(name, library); + } + } catch (Exception e) { + if (reqLib) + throw new RuntimeException(String.format( + "can not load required library %s", name, e)); + else + System.out.println(String.format("can not load library %s", name, e)); + } + } + + return library; + } + + + private static Object loadLibrary(String name, Class clazz, + boolean reqLib) { + + // Logger.getAnonymousLogger().info(String.format("loading %s", name)); + + String[] nameFormats; + nameFormats = Platform.isWindows() ? new String[] { "lib%s", "lib%s-0", + "%s" } : new String[] { "%s-0", "%s" }; + + UnsatisfiedLinkError linkError = null; + + for (String fmt : nameFormats) { + try { + String s = String.format(fmt, name); + //System.out.println("Trying to load library file " + s); + Object obj = Native.loadLibrary(s, clazz); + //System.out.println("Loaded library " + s + " successfully!"); + return obj; + } catch (UnsatisfiedLinkError ex) { + linkError = ex; + } + } + + if (reqLib) + throw new UnsatisfiedLinkError( + String.format( + "can't load library %s (%1$s|lib%1$s|lib%1$s-0) with " + + "-Djna.library.path=%s. Last error:%s", + name, System.getProperty("jna.library.path"), linkError)); + else { + System.out.println(String.format( + "can't load library %s (%1$s|lib%1$s|lib%1$s-0) with " + + "-Djna.library.path=%s. Last error:%s", + name, System.getProperty("jna.library.path"), linkError)); + return null; + } + } + + + public static synchronized LibraryLoader getInstance(int winBuildType) { + if (null == instance) { + instance = new LibraryLoader(); + instance.preLoadLibs(winBuildType); + } + return instance; + } +} diff --git a/src/processing/video/Movie.java b/src/processing/video/Movie.java index 644b905..4ace469 100755 --- a/src/processing/video/Movie.java +++ b/src/processing/video/Movie.java @@ -53,7 +53,7 @@ * @usage application */ public class Movie extends PImage implements PConstants { - public static String[] supportedProtocols = { "http" }; + public static String[] supportedProtocols = { "http", "https" }; public String filename; public PlayBin playbin; diff --git a/src/processing/video/Video.java b/src/processing/video/Video.java index 4918de3..fd138b3 100755 --- a/src/processing/video/Video.java +++ b/src/processing/video/Video.java @@ -1,398 +1,424 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2012-22 The Processing Foundation - Copyright (c) 2011-12 Ben Fry and Casey Reas - GStreamer implementation ported from GSVideo library by Andres Colubri - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA -*/ - -package processing.video; - -import org.freedesktop.gstreamer.*; -import processing.core.PApplet; -import processing.core.PConstants; - -import java.io.File; -import java.nio.ByteOrder; -import java.nio.file.Paths; -import java.util.List; - -/** - * This class contains some basic functions used by the rest of the classes in - * this library. - */ -public class Video implements PConstants { - // Allows to set the amount of desired debug output from GStreamer, according to the following table: - // https://gstreamer.freedesktop.org/documentation/tutorials/basic/debugging-tools.html?gi-language=c#printing-debug-information - public static int DEBUG_LEVEL = 1; - - // Path that the video library will use to load the GStreamer base libraries - // and plugins from. They can be passed from the application using the - // gstreamer.library.path and gstreamer.plugin.path system variables (see - // comments in initImpl() below). - public static String gstreamerLibPath = ""; - public static String gstreamerPluginPath = ""; - - protected static boolean usingGStreamerSystemInstall = false; - - // OpenGL texture used as buffer sink by default, when the renderer is - // GL-based. This can improve performance significantly, since the video - // frames are automatically copied into the texture without passing through - // the pixels arrays, as well as having the color conversion into RGBA handled - // natively by GStreamer. - protected static boolean useGLBufferSink = true; - - protected static boolean defaultGLibContext = false; - - protected static long INSTANCES_COUNT = 0; - - protected static int bitsJVM; - static { - bitsJVM = PApplet.parseInt(System.getProperty("sun.arch.data.model")); - } - - - static protected void init() { - if (INSTANCES_COUNT == 0) { - initImpl(); - } - INSTANCES_COUNT++; - } - - - static protected void restart() { - removePlugins(); - Gst.deinit(); - initImpl(); - } - - - static protected void initImpl() { - // The video library loads the GStreamer libraries according to the following - // priority: - // 1) If the VM argument "gstreamer.library.path" exists, it will use it as the - // root location of the libraries. This is typically the case when running - // the library from Eclipse. - // 2) If the environmental variable is GSTREAMER_1_0_ROOT_X86(_64) is defined then - // will try to use its contents as the root path of the system install of GStreamer. - // 3) The bundled version of GStreamer will be used, if present. - // 4) If none of the above works, then will try to use default install locations of GStreamer - // on Windows and Mac, if they exist. - // In this way, priority is given to the system installation of GStreamer only if set in the - // environmental variables, otherwise will try to load the bundled GStreamer, and if it does not - // exist it will look for GStreamer in the system-wide locations. This gives the user the option - // to remove the bundled GStreamer libs to default to the system-wide installation. - String libPath = System.getProperty("gstreamer.library.path"); - if (libPath != null) { - gstreamerLibPath = libPath; - - // If the GStreamer installation referred by gstreamer.library.path is not - // a system installation, then the path containing the plugins needs to be - // specified separately, otherwise the plugins will be automatically - // loaded from the default location. The system property for the plugin - // path is "gstreamer.plugin.path" - String pluginPath = System.getProperty("gstreamer.plugin.path"); - if (pluginPath != null) { - gstreamerPluginPath = pluginPath; - } - - usingGStreamerSystemInstall = false; - } else { - String rootPath = ""; - if (bitsJVM == 64 && System.getenv("GSTREAMER_1_0_ROOT_X86_64") != null) { - // Get 64-bit root of GStreamer install - rootPath = System.getenv("GSTREAMER_1_0_ROOT_X86_64"); - } else if (bitsJVM == 32 && System.getenv("GSTREAMER_1_0_ROOT_X86") != null) { - // Get 32-bit root of GStreamer install - rootPath = System.getenv("GSTREAMER_1_0_ROOT_X86"); - } - - if (!rootPath.equals("")) { - if (PApplet.platform == MACOS) { - gstreamerLibPath = Paths.get(rootPath, "lib").toString(); - } else { - gstreamerLibPath = Paths.get(rootPath, "bin").toString(); - } - File path = new File(gstreamerLibPath); - if (path.exists()) { - // We have a system install of GStreamer - usingGStreamerSystemInstall = true; - buildSystemPaths(rootPath); - } else { - // The environmental variables contain invalid paths... - gstreamerLibPath = ""; - } - } - } - - if (libPath == null && !usingGStreamerSystemInstall) { - // No GStreamer path in the VM arguments, and not system-wide install in environmental variables, - // will try searching for the bundled GStreamer libs. - buildBundldedPaths(); - } - - if (gstreamerLibPath.equals("")) { - // Finally, no environmental variables defined and did not find bundled gstreamer, - // will try some default system-wide locations. - String rootPath = ""; - if (PApplet.platform == MACOS) { - rootPath = "/Library/Frameworks/GStreamer.framework/Versions/1.0"; - gstreamerLibPath = Paths.get(rootPath, "lib").toString(); - } else if (PApplet.platform == WINDOWS) { - if (bitsJVM == 64) { - rootPath = "C:\\gstreamer\\1.0\\x86_64"; - } else { - rootPath = "C:\\gstreamer\\1.0\\x86"; - } - gstreamerLibPath = Paths.get(rootPath, "bin").toString(); - } else if (PApplet.platform == LINUX) { - if (bitsJVM == 64) { - rootPath = "/lib/x86_64-linux-gnu"; - } else { - rootPath = "/lib/x86-linux-gnu"; - } - File gstlib = new File(rootPath, "libgstreamer-1.0.so.0"); - if (gstlib.exists()) { - gstreamerLibPath = Paths.get(rootPath).toString(); - } - } - - File path = new File(gstreamerLibPath); - if (path.exists()) { - // We have a system install of GStreamer - if (bitsJVM == 64) { - Environment.libc.setenv("GSTREAMER_1_0_ROOT_X86_64", gstreamerLibPath, true); - } else { - Environment.libc.setenv("GSTREAMER_1_0_ROOT_X86", gstreamerLibPath, true); - } - buildSystemPaths(rootPath); - } else { - System.err.println("We could not find a system-wide or bundled installation of GStreamer, but video might still work if GStreamer was placed somewhere else"); - } - usingGStreamerSystemInstall = true; - } - - if (!gstreamerLibPath.equals("")) { - // Should be safe because this is setting the jna.library.path, - // not java.library.path, and JNA is being provided by the video library. - // This will need to change if JNA is ever moved into more of a shared - // location (i.e. part of core) because this would overwrite the prop. - System.setProperty("jna.library.path", gstreamerLibPath); - } - - Environment.libc.setenv("GST_DEBUG", String.valueOf(DEBUG_LEVEL), true); - - if (!usingGStreamerSystemInstall) { - // Disable the use of gst-plugin-scanner on environments where we're - // not using the host system's installation of GStreamer - // the problem with gst-plugin-scanner is that the library expects it - // to exist at a specific location determined at build time - Environment.libc.setenv("GST_REGISTRY_FORK", "no", true); - - // Prevent globally installed libraries from being used on platforms - // where we ship GStreamer - if (!gstreamerPluginPath.equals("")) { - Environment.libc.setenv("GST_PLUGIN_SYSTEM_PATH_1_0", "", true); - } - } - - if (!usingGStreamerSystemInstall && (PApplet.platform == WINDOWS || PApplet.platform == LINUX)) { - // Pre-loading base GStreamer libraries on Windows and Linux, - // otherwise dynamic dependencies cannot be resolved. - LibraryLoader loader = LibraryLoader.getInstance(); - if (loader == null) { - System.err.println("Cannot load GStreamer libraries."); - } - } - - String[] args = { "" }; - Gst.setUseDefaultContext(defaultGLibContext); - Gst.init("Processing core video", args); - - if (!usingGStreamerSystemInstall) { - // Plugins are scanned explicitly from the bindings if using the - // local GStreamer - addPlugins(); - } - - // output GStreamer version, lib path, plugin path - // and whether a system install is being used - printGStreamerInfo(); - } - - static protected void printGStreamerInfo() { - String locInfo = ""; - if (usingGStreamerSystemInstall) locInfo = "system-wide"; - else locInfo = "bundled"; - System.out.println("Processing video library using " + locInfo + " GStreamer " + Gst.getVersion()); - } - - - static protected void addPlugins() { - if (!gstreamerPluginPath.equals("")) { - Registry reg = Registry.get(); - boolean res; - res = reg.scanPath(gstreamerPluginPath); - if (!res) { - System.err.println("Cannot load GStreamer plugins from " + gstreamerPluginPath); - } - } - } - - - static protected void removePlugins() { - Registry reg = Registry.get(); - List list = reg.getPluginList(); - for (Plugin plg : list) { - reg.removePlugin(plg); - } - } - - - /** - * Search for an item by checking folders listed in java.library.path - * for a specific name. - */ - @SuppressWarnings("SameParameterValue") - static private String searchLibraryPath(String what) { - String libraryPath = System.getProperty("java.library.path"); - // Should not be null, but cannot assume - if (libraryPath != null) { - String[] folders = PApplet.split(libraryPath, File.pathSeparatorChar); - // Usually, the most relevant paths will be at the front of the list, - // so hopefully this will not walk several entries. - for (String folder : folders) { - // Skip /lib and /usr/lib folders because they contain the system-wide GStreamer on Linux - // and they are on the Java library path. - if (folder.startsWith("/lib/") || folder.startsWith("/usr/lib/")) continue; - File file = new File(folder, what); - if (file.exists()) { - return file.getAbsolutePath(); - } - } - } - return null; - } - - - /** - * Search for an item by checking folders listed in java.class.path - * for a specific name. - */ - @SuppressWarnings("SameParameterValue") - static private String searchClassPath(String what) { - String classPath = System.getProperty("java.class.path"); - // Should not be null, but cannot assume - if (classPath != null) { - String[] entries = PApplet.split(classPath, File.pathSeparatorChar); - // Usually, the most relevant paths will be at the front of the list, - // so hopefully this will not walk several entries. - for (String entry : entries) { - File dir = new File(entry); - // If it's a .jar file, get its parent folder. This will lead to some - // double-checking of the same folder, but probably almost as expensive - // to keep track of folders we've already seen. - if (dir.isFile()) { - dir = dir.getParentFile(); - } - File file = new File(dir, what); - if (file.exists()) { - return file.getAbsolutePath(); - } - } - } - return null; - } - - static protected void buildSystemPaths(String rootPath) { - if (System.getenv("GST_PLUGIN_SYSTEM_PATH") != null) { - gstreamerPluginPath = System.getenv("GST_PLUGIN_SYSTEM_PATH"); - } else { - if (PApplet.platform == WINDOWS) { - gstreamerPluginPath = Paths.get(rootPath, "lib", "gstreamer-1.0").toString(); - } else { - gstreamerPluginPath = Paths.get(gstreamerLibPath, "gstreamer-1.0").toString(); } - } - File path = new File(gstreamerPluginPath); - if (!path.exists()) { - gstreamerPluginPath = ""; - } - } - - static protected void buildBundldedPaths() { - // look for the gstreamer-1.0 folder in the native library path - // (there are natives adjacent to it, so this will work) - gstreamerPluginPath = searchLibraryPath("gstreamer-1.0"); - if (gstreamerPluginPath == null) { - gstreamerPluginPath = searchClassPath("gstreamer-1.0"); - } - - if (gstreamerPluginPath == null) { - gstreamerPluginPath = ""; - gstreamerLibPath = ""; - usingGStreamerSystemInstall = true; - } else { - File gstreamerLibDir = new File(gstreamerPluginPath).getParentFile(); - gstreamerLibPath = gstreamerLibDir.getAbsolutePath(); - } - } - - - static protected float nanoSecToSecFrac(long nanosec) { - return (float)(nanosec / 1E9); - } - - - static protected long secToNanoLong(float sec) { - Double f = Double.valueOf(sec * 1E9); - return f.longValue(); - } - - - /** - * Reorders an OpenGL pixel array (RGBA) into ARGB. The array must be - * of size width * height. - * @param pixels int[] - */ - static protected void convertToARGB(int[] pixels, int width, int height) { - int t = 0; - int p = 0; - if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) { - // RGBA to ARGB conversion: shifting RGB 8 bits to the right, - // and placing A 24 bits to the left. - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int pixel = pixels[p++]; - pixels[t++] = (pixel >>> 8) | ((pixel << 24) & 0xFF000000); - } - } - } else { - // We have to convert ABGR into ARGB, so R and B must be swapped, - // A and G just brought back in. - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int pixel = pixels[p++]; - pixels[t++] = ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | - (pixel & 0xFF00FF00); - } - } - } - } -} +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-22 The Processing Foundation + Copyright (c) 2011-12 Ben Fry and Casey Reas + GStreamer implementation ported from GSVideo library by Andres Colubri + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.video; + +import org.freedesktop.gstreamer.*; +import processing.core.PApplet; +import processing.core.PConstants; + +import java.io.File; +import java.nio.ByteOrder; +import java.nio.file.Paths; +import java.util.List; + +/** + * This class contains some basic functions used by the rest of the classes in + * this library. + */ +public class Video implements PConstants { + // Allows to set the amount of desired debug output from GStreamer, according to the following table: + // https://gstreamer.freedesktop.org/documentation/tutorials/basic/debugging-tools.html?gi-language=c#printing-debug-information + public static int DEBUG_LEVEL = 1; + + // Path that the video library will use to load the GStreamer base libraries + // and plugins from. They can be passed from the application using the + // gstreamer.library.path and gstreamer.plugin.path system variables (see + // comments in initImpl() below). + public static String gstreamerLibPath = ""; + public static String gstreamerPluginPath = ""; + + protected static boolean usingGStreamerSystemInstall = false; + + // OpenGL texture used as buffer sink by default, when the renderer is + // GL-based. This can improve performance significantly, since the video + // frames are automatically copied into the texture without passing through + // the pixels arrays, as well as having the color conversion into RGBA handled + // natively by GStreamer. + protected static boolean useGLBufferSink = true; + + protected static boolean defaultGLibContext = false; + + protected static long INSTANCES_COUNT = 0; + + protected static int bitsJVM; + static { + bitsJVM = PApplet.parseInt(System.getProperty("sun.arch.data.model")); + } + + + static protected void init() { + if (INSTANCES_COUNT == 0) { + initImpl(); + } + INSTANCES_COUNT++; + } + + + static protected void restart() { + removePlugins(); + Gst.deinit(); + initImpl(); + } + + + static protected void initImpl() { + // The video library loads the GStreamer libraries according to the following + // priority: + // 1) If the VM argument "gstreamer.library.path" exists, it will use it as the + // root location of the libraries. This is typically the case when running + // the library from Eclipse. + // 2) If the environmental variable is GSTREAMER_1_0_ROOT_(MINGW/MSVC)_64 is defined then + // will try to use its contents as the root path of the system install of GStreamer. + // 3) The bundled version of GStreamer will be used, if present. + // 4) If none of the above works, then will try to use default install locations of GStreamer + // on Windows and Mac, if they exist. + // In this way, priority is given to the system installation of GStreamer only if set in the + // environmental variables, otherwise will try to load the bundled GStreamer, and if it does not + // exist it will look for GStreamer in the system-wide locations. This gives the user the option + // to remove the bundled GStreamer libs to default to the system-wide installation. + String libPath = System.getProperty("gstreamer.library.path"); + int winBuildType = 0; // 0: default build, 1: mingw, 2: msvc + if (libPath != null) { + gstreamerLibPath = libPath; + + // If the GStreamer installation referred by gstreamer.library.path is not + // a system installation, then the path containing the plugins needs to be + // specified separately, otherwise the plugins will be automatically + // loaded from the default location. The system property for the plugin + // path is "gstreamer.plugin.path" + String pluginPath = System.getProperty("gstreamer.plugin.path"); + if (pluginPath != null) { + gstreamerPluginPath = pluginPath; + } + + usingGStreamerSystemInstall = false; + } else { + String rootPath = ""; + if (bitsJVM == 64) { + // Get 64-bit root of GStreamer install + if (System.getenv("GSTREAMER_1_0_ROOT_X86_64") != null) { + winBuildType = 0; + rootPath = System.getenv("GSTREAMER_1_0_ROOT_X86_64"); + } else if (System.getenv("GSTREAMER_1_0_ROOT_MINGW_X86_64") != null) { + winBuildType = 1; + rootPath = System.getenv("GSTREAMER_1_0_ROOT_MINGW_X86_64"); + } else if (System.getenv("GSTREAMER_1_0_ROOT_MSVC_X86_64") != null) { + winBuildType = 2; + rootPath = System.getenv("GSTREAMER_1_0_ROOT_MSVC_X86_64"); + } + } + + if (!rootPath.equals("")) { + if (PApplet.platform == MACOS) { + gstreamerLibPath = Paths.get(rootPath, "lib").toString(); + } else { + gstreamerLibPath = Paths.get(rootPath, "bin").toString(); + } + File path = new File(gstreamerLibPath); + if (path.exists()) { + // We have a system install of GStreamer + usingGStreamerSystemInstall = true; + buildSystemPaths(rootPath); + } else { + // The environmental variables contain invalid paths... + gstreamerLibPath = ""; + } + } + } + + if (libPath == null && !usingGStreamerSystemInstall) { + // No GStreamer path in the VM arguments, and not system-wide install in environmental variables, + // will try searching for the bundled GStreamer libs. + if (buildBundldedPaths()) { + // Found bundled GStreamer libs, which in version 2.2 of the library are MSVC-built: + winBuildType = 2; + } + } + + if (gstreamerLibPath.equals("")) { + // Finally, no environmental variables defined and did not find bundled gstreamer, + // will try some default system-wide locations. + String rootPath = ""; + if (PApplet.platform == MACOS) { + rootPath = "/Library/Frameworks/GStreamer.framework/Versions/1.0"; + gstreamerLibPath = Paths.get(rootPath, "lib").toString(); + } else if (PApplet.platform == WINDOWS) { + if (bitsJVM == 64) { + if (new File("C:\\gstreamer\\1.0\\x86_64").exists()) { + winBuildType = 0; + rootPath = "C:\\gstreamer\\1.0\\x86_64"; + } else if (new File("C:\\gstreamer\\1.0\\mingw_x86_64").exists()) { + winBuildType = 1; + rootPath = "C:\\gstreamer\\1.0\\mingw_x86_64"; + } else if (new File("C:\\gstreamer\\1.0\\msvc_x86_64").exists()) { + winBuildType = 2; + rootPath = "C:\\gstreamer\\1.0\\msvc_x86_64"; + } + gstreamerLibPath = Paths.get(rootPath, "bin").toString(); + } + } else if (PApplet.platform == LINUX) { + if (bitsJVM == 64) { + rootPath = "/lib/x86_64-linux-gnu"; + } else { + rootPath = "/lib/x86-linux-gnu"; + } + File gstlib = new File(rootPath, "libgstreamer-1.0.so.0"); + if (gstlib.exists()) { + gstreamerLibPath = Paths.get(rootPath).toString(); + } + } + + File path = new File(gstreamerLibPath); + if (path.exists()) { + // We have a system install of GStreamer + if (bitsJVM == 64) { + if (winBuildType == 0) { + Environment.libc.setenv("GSTREAMER_1_0_ROOT_X86_64", gstreamerLibPath, true); + } else if (winBuildType == 1) { + Environment.libc.setenv("GSTREAMER_1_0_ROOT_MINGW_X86_64", gstreamerLibPath, true); + } else if (winBuildType == 2) { + Environment.libc.setenv("GSTREAMER_1_0_ROOT_MSVC_X86_64", gstreamerLibPath, true); + } + } + buildSystemPaths(rootPath); + } else { + System.err.println("We could not find a system-wide or bundled installation of GStreamer, but video might still work if GStreamer was placed somewhere else"); + } + usingGStreamerSystemInstall = true; + } + + if (!gstreamerLibPath.equals("")) { + // Should be safe because this is setting the jna.library.path, + // not java.library.path, and JNA is being provided by the video library. + // This will need to change if JNA is ever moved into more of a shared + // location (i.e. part of core) because this would overwrite the prop. + System.setProperty("jna.library.path", gstreamerLibPath); + } + + Environment.libc.setenv("GST_DEBUG", String.valueOf(DEBUG_LEVEL), true); + + if (!usingGStreamerSystemInstall) { + // Disable the use of gst-plugin-scanner on environments where we're + // not using the host system's installation of GStreamer + // the problem with gst-plugin-scanner is that the library expects it + // to exist at a specific location determined at build time + Environment.libc.setenv("GST_REGISTRY_FORK", "no", true); + + // Prevent globally installed libraries from being used on platforms + // where we ship GStreamer + if (!gstreamerPluginPath.equals("")) { + Environment.libc.setenv("GST_PLUGIN_SYSTEM_PATH_1_0", "", true); + } + } + + if (PApplet.platform == WINDOWS || (!usingGStreamerSystemInstall && PApplet.platform == LINUX)) { + // Pre-loading base GStreamer libraries on Windows and Linux, + // otherwise dynamic dependencies cannot be resolved. + LibraryLoader loader = LibraryLoader.getInstance(winBuildType); + if (loader == null) { + System.err.println("Cannot load GStreamer libraries."); + } + } + + String[] args = { "" }; + Gst.setUseDefaultContext(defaultGLibContext); + Gst.init("Processing core video", args); + + // Output GStreamer version, lib path, plugin path + // and whether a system install is being used + printGStreamerInfo(); + + if (!usingGStreamerSystemInstall) { + // Plugins are scanned explicitly from the bindings if using the + // local GStreamer + scanPlugins(); + } + } + + static protected void printGStreamerInfo() { + String locInfo = ""; + if (usingGStreamerSystemInstall) locInfo = "system-wide"; + else locInfo = "bundled"; + System.out.println("Processing video library using " + locInfo + " GStreamer " + Gst.getVersion()); + } + + + static protected void scanPlugins() { + if (!gstreamerPluginPath.equals("")) { + Registry reg = Registry.get(); + boolean res; + System.out.print("Scanning GStreamer plugins..."); + res = reg.scanPath(gstreamerPluginPath); + if (res) { + System.out.println(" Done."); + } else { + System.err.println("Cannot load GStreamer plugins from " + gstreamerPluginPath); + } + } + } + + + static protected void removePlugins() { + Registry reg = Registry.get(); + List list = reg.getPluginList(); + for (Plugin plg : list) { + reg.removePlugin(plg); + } + } + + + /** + * Search for an item by checking folders listed in java.library.path + * for a specific name. + */ + @SuppressWarnings("SameParameterValue") + static private String searchLibraryPath(String what) { + String libraryPath = System.getProperty("java.library.path"); + // Should not be null, but cannot assume + if (libraryPath != null) { + String[] folders = PApplet.split(libraryPath, File.pathSeparatorChar); + // Usually, the most relevant paths will be at the front of the list, + // so hopefully this will not walk several entries. + for (String folder : folders) { + // Skip /lib and /usr/lib folders because they contain the system-wide GStreamer on Linux + // and they are on the Java library path. + if (folder.startsWith("/lib/") || folder.startsWith("/usr/lib/")) continue; + File file = new File(folder, what); + if (file.exists()) { + return file.getAbsolutePath(); + } + } + } + return null; + } + + + /** + * Search for an item by checking folders listed in java.class.path + * for a specific name. + */ + @SuppressWarnings("SameParameterValue") + static private String searchClassPath(String what) { + String classPath = System.getProperty("java.class.path"); + // Should not be null, but cannot assume + if (classPath != null) { + String[] entries = PApplet.split(classPath, File.pathSeparatorChar); + // Usually, the most relevant paths will be at the front of the list, + // so hopefully this will not walk several entries. + for (String entry : entries) { + File dir = new File(entry); + // If it's a .jar file, get its parent folder. This will lead to some + // double-checking of the same folder, but probably almost as expensive + // to keep track of folders we've already seen. + if (dir.isFile()) { + dir = dir.getParentFile(); + } + File file = new File(dir, what); + if (file.exists()) { + return file.getAbsolutePath(); + } + } + } + return null; + } + + static protected void buildSystemPaths(String rootPath) { + if (System.getenv("GST_PLUGIN_SYSTEM_PATH") != null) { + gstreamerPluginPath = System.getenv("GST_PLUGIN_SYSTEM_PATH"); + } else { + if (PApplet.platform == WINDOWS) { + gstreamerPluginPath = Paths.get(rootPath, "lib", "gstreamer-1.0").toString(); + } else { + gstreamerPluginPath = Paths.get(gstreamerLibPath, "gstreamer-1.0").toString(); } + } + File path = new File(gstreamerPluginPath); + if (!path.exists()) { + gstreamerPluginPath = ""; + } + } + + static protected boolean buildBundldedPaths() { + // look for the gstreamer-1.0 folder in the native library path + // (there are natives adjacent to it, so this will work) + gstreamerPluginPath = searchLibraryPath("gstreamer-1.0"); + if (gstreamerPluginPath == null) { + gstreamerPluginPath = searchClassPath("gstreamer-1.0"); + } + + if (gstreamerPluginPath == null) { + gstreamerPluginPath = ""; + gstreamerLibPath = ""; + usingGStreamerSystemInstall = true; + return false; + } else { + File gstreamerLibDir = new File(gstreamerPluginPath).getParentFile(); + gstreamerLibPath = gstreamerLibDir.getAbsolutePath(); + return true; + } + } + + + static protected float nanoSecToSecFrac(long nanosec) { + return (float)(nanosec / 1E9); + } + + + static protected long secToNanoLong(float sec) { + Double f = Double.valueOf(sec * 1E9); + return f.longValue(); + } + + + /** + * Reorders an OpenGL pixel array (RGBA) into ARGB. The array must be + * of size width * height. + * @param pixels int[] + */ + static protected void convertToARGB(int[] pixels, int width, int height) { + int t = 0; + int p = 0; + if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) { + // RGBA to ARGB conversion: shifting RGB 8 bits to the right, + // and placing A 24 bits to the left. + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int pixel = pixels[p++]; + pixels[t++] = (pixel >>> 8) | ((pixel << 24) & 0xFF000000); + } + } + } else { + // We have to convert ABGR into ARGB, so R and B must be swapped, + // A and G just brought back in. + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int pixel = pixels[p++]; + pixels[t++] = ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | + (pixel & 0xFF00FF00); + } + } + } + } +}