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