diff --git a/build.gradle b/build.gradle index 27081e418..9cfb7e8c6 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,7 @@ allprojects { mavenCentral() mavenLocal() maven { url = "https://maven.photonvision.org/repository/internal/" } + maven { url = "https://maven.photonvision.org/repository/snapshots/" } } wpilibRepositories.addAllReleaseRepositories(it) wpilibRepositories.addAllDevelopmentRepositories(it) @@ -26,6 +27,7 @@ ext { openCVversion = "4.8.0-2" joglVersion = "2.4.0-rc-20200307" javalinVersion = "5.6.2" + photonGlDriverLibVersion = "dev-v2023.1.0-6-g5e6f7fa" frcYear = "2024" pubVersion = versionString diff --git a/photon-core/build.gradle b/photon-core/build.gradle index d5de01491..1a9841452 100644 --- a/photon-core/build.gradle +++ b/photon-core/build.gradle @@ -13,6 +13,9 @@ dependencies { implementation 'org.zeroturnaround:zt-zip:1.14' implementation "org.xerial:sqlite-jdbc:3.41.0.0" + + implementation "org.photonvision:photon-libcamera-gl-driver-jni:$photonGlDriverLibVersion:linuxarm64" + implementation "org.photonvision:photon-libcamera-gl-driver-java:$photonGlDriverLibVersion" } task writeCurrentVersion { diff --git a/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java b/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java index d44d3e6cd..601a33f08 100644 --- a/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java +++ b/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java @@ -27,7 +27,7 @@ import org.photonvision.PhotonVersion; import org.photonvision.common.hardware.Platform; import org.photonvision.common.networking.NetworkUtils; import org.photonvision.common.util.SerializationUtils; -import org.photonvision.raspi.LibCameraJNI; +import org.photonvision.raspi.LibCameraJNILoader; import org.photonvision.vision.processes.VisionModule; import org.photonvision.vision.processes.VisionModuleManager; import org.photonvision.vision.processes.VisionSource; @@ -136,7 +136,7 @@ public class PhotonConfiguration { generalSubmap.put("version", PhotonVersion.versionString); generalSubmap.put( "gpuAcceleration", - LibCameraJNI.isSupported() + LibCameraJNILoader.isSupported() ? "Zerocopy Libcamera Working" : ""); // TODO add support for other types of GPU accel generalSubmap.put("hardwareModel", hardwareConfig.deviceName); diff --git a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java deleted file mode 100644 index d97d10268..000000000 --- a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) Photon Vision. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.photonvision.raspi; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import org.photonvision.common.logging.LogGroup; -import org.photonvision.common.logging.Logger; - -public class LibCameraJNI { - private static boolean libraryLoaded = false; - private static final Logger logger = new Logger(LibCameraJNI.class, LogGroup.Camera); - - public static synchronized void forceLoad() throws IOException { - if (libraryLoaded) return; - - try { - File libDirectory = Path.of("lib/").toFile(); - if (!libDirectory.exists()) { - Files.createDirectory(libDirectory.toPath()).toFile(); - } - - // We always extract the shared object (we could hash each so, but that's a lot of work) - URL resourceURL = LibCameraJNI.class.getResource("/nativelibraries/libphotonlibcamera.so"); - File libFile = Path.of("lib/libphotonlibcamera.so").toFile(); - try (InputStream in = resourceURL.openStream()) { - if (libFile.exists()) Files.delete(libFile.toPath()); - Files.copy(in, libFile.toPath()); - } catch (Exception e) { - logger.error("Could not extract the native library!"); - } - System.load(libFile.getAbsolutePath()); - - libraryLoaded = true; - logger.info("Successfully loaded libpicam shared object"); - } catch (UnsatisfiedLinkError e) { - logger.error("Couldn't load libpicam shared object"); - e.printStackTrace(); - } - } - - public enum SensorModel { - Disconnected, - OV5647, // Picam v1 - IMX219, // Picam v2 - IMX708, // Picam v3 - IMX477, // Picam HQ - OV9281, - OV7251, - Unknown; - - public String getFriendlyName() { - switch (this) { - case Disconnected: - return "Disconnected Camera"; - case OV5647: - return "Camera Module v1"; - case IMX219: - return "Camera Module v2"; - case IMX708: - return "Camera Module v3"; - case IMX477: - return "HQ Camera"; - case OV9281: - return "OV9281"; - case OV7251: - return "OV7251"; - case Unknown: - default: - return "Unknown Camera"; - } - } - } - - public static SensorModel getSensorModel(long r_ptr) { - int model = getSensorModelRaw(r_ptr); - return SensorModel.values()[model]; - } - - public static SensorModel getSensorModel(String name) { - int model = getSensorModelRaw(name); - return SensorModel.values()[model]; - } - - public static boolean isSupported() { - return libraryLoaded - // && getSensorModel() != PicamJNI.SensorModel.Disconnected - // && Platform.isRaspberryPi() - && isLibraryWorking(); - } - - private static native boolean isLibraryWorking(); - - public static native int getSensorModelRaw(long r_ptr); - - public static native int getSensorModelRaw(String name); - - // ======================================================== // - - /** - * Creates a new runner with a given width/height/fps - * - * @param the path / name of the camera as given from libcamera. - * @param width Camera video mode width in pixels - * @param height Camera video mode height in pixels - * @param fps Camera video mode FPS - * @return the runner pointer for the camera. - */ - public static native long createCamera(String name, int width, int height, int rotation); - - /** - * Starts the camera thresholder and display threads running. Make sure that this function is - * called synchronously with stopCamera and returnFrame! - */ - public static native boolean startCamera(long r_ptr); - - /** Stops the camera runner. Make sure to call prior to destroying the camera! */ - public static native boolean stopCamera(long r_ptr); - - // Destroy all native resources associated with a camera. Ensure stop is called prior! - public static native boolean destroyCamera(long r_ptr); - - // ======================================================== // - - // Set thresholds on [0..1] - public static native boolean setThresholds( - long r_ptr, - double hl, - double sl, - double vl, - double hu, - double su, - double vu, - boolean hueInverted); - - public static native boolean setAutoExposure(long r_ptr, boolean doAutoExposure); - - // Exposure time, in microseconds - public static native boolean setExposure(long r_ptr, int exposureUs); - - // Set brightness on [-1, 1] - public static native boolean setBrightness(long r_ptr, double brightness); - - // Unknown ranges for red and blue AWB gain - public static native boolean setAwbGain(long r_ptr, double red, double blue); - - /** - * Get the time when the first pixel exposure was started, in the same timebase as libcamera gives - * the frame capture time. Units are nanoseconds. - */ - public static native long getFrameCaptureTime(long p_ptr); - - /** - * Get the current time, in the same timebase as libcamera gives the frame capture time. Units are - * nanoseconds. - */ - public static native long getLibcameraTimestamp(); - - public static native long setFramesToCopy(long r_ptr, boolean copyIn, boolean copyOut); - - // Analog gain multiplier to apply to all color channels, on [1, Big Number] - public static native boolean setAnalogGain(long r_ptr, double analog); - - /** Block until a new frame is available from native code. */ - public static native long awaitNewFrame(long r_ptr); - - /** - * Get a pointer to the most recent color mat generated. Call this immediately after - * awaitNewFrame, and call only once per new frame! - */ - public static native long takeColorFrame(long pair_ptr); - - /** - * Get a pointer to the most recent processed mat generated. Call this immediately after - * awaitNewFrame, and call only once per new frame! - */ - public static native long takeProcessedFrame(long pair_ptr); - - /** - * Set the GPU processing type we should do. Enum of [none, HSV, greyscale, adaptive threshold]. - */ - public static native boolean setGpuProcessType(long r_ptr, int type); - - public static native int getGpuProcessType(long p_ptr); - - /** Release a pair pointer back to the libcamera driver code to be filled again */ - public static native boolean releasePair(long p_ptr); - - /** Get an array containing the names/ids/paths of all connected CSI cameras from libcamera. */ - public static native String[] getCameraNames(); -} diff --git a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNILoader.java b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNILoader.java new file mode 100644 index 000000000..f4d0b3192 --- /dev/null +++ b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNILoader.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.photonvision.raspi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import org.photonvision.common.logging.LogGroup; +import org.photonvision.common.logging.Logger; + +public class LibCameraJNILoader { + private static boolean libraryLoaded = false; + private static final Logger logger = new Logger(LibCameraJNILoader.class, LogGroup.Camera); + + public static synchronized void forceLoad() throws IOException { + if (libraryLoaded) return; + + var libraryName = "photonlibcamera"; + + try { + // We always extract the shared object (we could hash each so, but that's a lot of work) + var arch_name = "linuxarm64"; + var nativeLibName = System.mapLibraryName(libraryName); + var resourcePath = "/nativelibraries/" + arch_name + "/" + nativeLibName; + var in = LibCameraJNILoader.class.getResourceAsStream(resourcePath); + + if (in == null) { + logger.error("Failed to find internal native library at path " + resourcePath); + libraryLoaded = false; + return; + } + + // It's important that we don't mangle the names of these files on Windows at least + File temp = new File(System.getProperty("java.io.tmpdir"), nativeLibName); + FileOutputStream fos = new FileOutputStream(temp); + + int read = -1; + byte[] buffer = new byte[1024]; + while ((read = in.read(buffer)) != -1) { + fos.write(buffer, 0, read); + } + fos.close(); + in.close(); + + System.load(temp.getAbsolutePath()); + + logger.info("Successfully loaded shared object " + temp.getName()); + + } catch (UnsatisfiedLinkError e) { + logger.error("Couldn't load shared object " + libraryName, e); + e.printStackTrace(); + // logger.error(System.getProperty("java.library.path")); + } + libraryLoaded = true; + } + + public static boolean isSupported() { + return libraryLoaded && LibCameraJNI.isSupported(); + } +} diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java index a84e6aba4..9837fd546 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java @@ -33,6 +33,7 @@ import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.common.util.TimedTaskManager; import org.photonvision.raspi.LibCameraJNI; +import org.photonvision.raspi.LibCameraJNILoader; import org.photonvision.vision.camera.CameraInfo; import org.photonvision.vision.camera.CameraQuirk; import org.photonvision.vision.camera.CameraType; @@ -98,7 +99,7 @@ public class VisionSourceManager { */ protected List getConnectedCSICameras() { List cameraInfos = new ArrayList(); - if (LibCameraJNI.isSupported()) + if (LibCameraJNILoader.isSupported()) for (String path : LibCameraJNI.getCameraNames()) { String name = LibCameraJNI.getSensorModel(path).getFriendlyName(); cameraInfos.add( diff --git a/photon-server/src/main/java/org/photonvision/Main.java b/photon-server/src/main/java/org/photonvision/Main.java index daf8c243c..f4e67d870 100644 --- a/photon-server/src/main/java/org/photonvision/Main.java +++ b/photon-server/src/main/java/org/photonvision/Main.java @@ -37,7 +37,7 @@ import org.photonvision.common.logging.Logger; import org.photonvision.common.networking.NetworkManager; import org.photonvision.common.util.TestUtils; import org.photonvision.common.util.numbers.IntegerCouple; -import org.photonvision.raspi.LibCameraJNI; +import org.photonvision.raspi.LibCameraJNILoader; import org.photonvision.server.Server; import org.photonvision.vision.camera.FileVisionSource; import org.photonvision.vision.opencv.CVMat; @@ -311,7 +311,7 @@ public class Main { try { if (Platform.isRaspberryPi()) { - LibCameraJNI.forceLoad(); + LibCameraJNILoader.forceLoad(); } } catch (IOException e) { logger.error("Failed to load libcamera-JNI!", e); diff --git a/photon-server/src/main/resources/nativelibraries/libphotonlibcamera.so b/photon-server/src/main/resources/nativelibraries/libphotonlibcamera.so deleted file mode 100644 index fe65d5813..000000000 Binary files a/photon-server/src/main/resources/nativelibraries/libphotonlibcamera.so and /dev/null differ diff --git a/photon-server/src/main/resources/nativelibraries/libpicam.so b/photon-server/src/main/resources/nativelibraries/libpicam.so deleted file mode 100755 index 24d50c953..000000000 Binary files a/photon-server/src/main/resources/nativelibraries/libpicam.so and /dev/null differ