X Tutup
/* -*- 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); } } } } }
X Tutup