diff --git a/.gitignore b/.gitignore
index e48269c..36aada0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@
build
.cache/
compile_commands.json
+photon_sysroot*
+build-pi/*
diff --git a/.styleguide b/.styleguide
new file mode 100644
index 0000000..ff0e63b
--- /dev/null
+++ b/.styleguide
@@ -0,0 +1,33 @@
+cppHeaderFileInclude {
+ \.h$
+ \.hpp$
+ \.inc$
+ \.inl$
+}
+
+cppSrcFileInclude {
+ \.cpp$
+}
+
+modifiableFileExclude {
+ \.jpg$
+ \.jpeg$
+ \.png$
+ \.gif$
+ \.so$
+ \.dll$
+ \.webp$
+ \.ico$
+ gradlew
+}
+
+includeProject {
+ ^photonLib/
+}
+
+includeOtherLibs {
+ ^frc/
+ ^networktables/
+ ^units/
+ ^wpi/
+}
diff --git a/.styleguide-license b/.styleguide-license
new file mode 100644
index 0000000..bcc3fc0
--- /dev/null
+++ b/.styleguide-license
@@ -0,0 +1,16 @@
+/*
+ * 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 .
+ */
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..e08420a
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,40 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "java",
+ "name": "LibCameraJNI",
+ "request": "launch",
+ "mainClass": "org.photonvision.raspi.LibCameraJNI",
+ "projectName": "photon-libcamera-gl-driver_ef1a9c31"
+ },
+ {
+ "name": "(gdb) Launch",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "${workspaceFolder}/build/libcamera_meme",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "${fileDirname}",
+ "environment": [],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ },
+ {
+ "description": "Set Disassembly Flavor to Intel",
+ "text": "-gdb-set disassembly-flavor intel",
+ "ignoreFailures": true
+ }
+ ],
+ "preLaunchTask": "Build libcamera_meme"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..40551ff
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,22 @@
+{
+ "tasks": [
+ {
+ "label": "Build libcamera_meme",
+ "type": "shell",
+ "command": "cmake",
+ "args": [
+ "--build",
+ "build",
+ "--target",
+ "libcamera_meme"
+ ],
+ "options": {
+ "cwd": "${workspaceFolder}/"
+ },
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ },
+ ]
+}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 73a38f3..01d58f1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.25.1)
+cmake_minimum_required(VERSION 3.18)
project(libcamera_meme)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -25,23 +25,26 @@ endif ()
SET(FAST_FLAGS "-Ofast -ftree-vectorize -fPIC")
add_definitions(${FAST_FLAGS})
-add_library(photonlibcamera SHARED concurrent_blocking_queue.h
- camera_grabber.cpp dma_buf_alloc.cpp
- gl_hsv_thresholder.cpp
- libcamera_opengl_utility.cpp
- libcamera_jni.cpp
- camera_manager.cpp
- camera_runner.cpp
- camera_model.cpp
- headless_opengl.cpp
+
+add_library(photonlibcamera SHARED
+ src/camera_grabber.cpp
+ src/dma_buf_alloc.cpp
+ src/gl_hsv_thresholder.cpp
+ src/libcamera_opengl_utility.cpp
+ src/camera_manager.cpp
+ src/camera_runner.cpp
+ src/camera_model.cpp
+ src/headless_opengl.cpp
+ src/libcamera_jni.cpp
)
target_compile_definitions(photonlibcamera PUBLIC EGL_NO_X11=1)
-target_include_directories(photonlibcamera PUBLIC ${OPENGL_INCLUDE_DIRS} ${LIBDRM_INCLUDE_DIRS} ${LIBCAMERA_INCLUDE_DIRS}
+target_include_directories(photonlibcamera PUBLIC include ${OPENGL_INCLUDE_DIRS} ${LIBDRM_INCLUDE_DIRS} ${LIBCAMERA_INCLUDE_DIRS}
${LIBGBM_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ${JNI_INCLUDE_DIRS})
target_link_libraries(photonlibcamera PUBLIC OpenGL::GL OpenGL::EGL Threads::Threads ${LIBCAMERA_LINK_LIBRARIES}
${LIBGBM_LINK_LIBRARIES} ${OpenCV_LIBS} ${LIBDRM_LINK_LIBRARIES})
+target_compile_options(photonlibcamera PRIVATE -Wall -Wextra -Wpedantic -Werror)
add_executable(libcamera_meme main.cpp)
target_include_directories(libcamera_meme PUBLIC ${OPENGL_INCLUDE_DIRS} ${LIBDRM_INCLUDE_DIRS} ${LIBCAMERA_INCLUDE_DIRS}
${LIBGBM_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ${JNI_INCLUDE_DIRS})
-target_link_libraries(libcamera_meme photonlibcamera)
\ No newline at end of file
+target_link_libraries(libcamera_meme photonlibcamera)
diff --git a/blocking_future.h b/blocking_future.h
deleted file mode 100644
index 7660caf..0000000
--- a/blocking_future.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-
-template class BlockingFuture {
- public:
- BlockingFuture() = default;
-
- void set(T &&item) {
- std::unique_lock lock(m_mutex);
- m_data = std::make_optional<>(std::forward(item));
- lock.unlock();
- m_cond.notify_one();
- }
-
- T take() {
- std::unique_lock lock(m_mutex);
- m_cond.wait(lock, [&] { return m_data.has_value(); });
-
- auto item = std::move(m_data.value());
- m_data.reset();
- return item;
- }
-
- private:
- std::optional m_data;
- std::mutex m_mutex;
- std::condition_variable m_cond;
-};
\ No newline at end of file
diff --git a/camera_manager.cpp b/camera_manager.cpp
deleted file mode 100644
index 20ee5e4..0000000
--- a/camera_manager.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "camera_manager.h"
-
-std::vector> GetAllCameraIDs() {
- static libcamera::CameraManager *camera_manager;
- if (!camera_manager) {
- camera_manager = new libcamera::CameraManager();
- camera_manager->start();
- }
- std::vector> cams =
- camera_manager->cameras();
- return cams;
-}
diff --git a/camera_manager.h b/camera_manager.h
deleted file mode 100644
index 6fe4b30..0000000
--- a/camera_manager.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-
-std::vector> GetAllCameraIDs();
diff --git a/camera_model.cpp b/camera_model.cpp
deleted file mode 100644
index 938b6f0..0000000
--- a/camera_model.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "camera_model.h"
-#include
-
-CameraModel stringToModel(const std::string& model) {
- printf("Checking model: %s\n", model.c_str());
- const char* famname = model.c_str();
- if (!strcmp(famname, "ov5647")) return OV5647;
- else if (!strcmp(famname, "imx219")) return IMX219;
- else if (!strcmp(famname, "imx708")) return IMX708;
- else if (!strcmp(famname, "imx477")) return IMX477;
- else if (!strcmp(famname, "ov9281")) return OV9281;
- else if (!strcmp(famname, "ov7251")) return OV7251;
- else if (!strcmp(famname, "Disconnected")) return Disconnected;
- else return Unknown;
-}
diff --git a/camera_model.h b/camera_model.h
deleted file mode 100644
index 1822b22..0000000
--- a/camera_model.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include
-
-enum CameraModel {
- Disconnected = 0,
- OV5647, // Picam v1
- IMX219, // Picam v2
- IMX708, // Picam v3
- IMX477, // Picam HQ
- OV9281,
- OV7251,
- Unknown
-};
-
-CameraModel stringToModel(const std::string& model);
diff --git a/dma_buf_alloc.h b/dma_buf_alloc.h
deleted file mode 100644
index bdd0e11..0000000
--- a/dma_buf_alloc.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include
-#include
-
-class DmaBufAlloc {
- public:
- explicit DmaBufAlloc(const std::string &heap_name);
- ~DmaBufAlloc();
-
- // Allocates a DMA-BUF of size len. The returned
- // fd can be closed using `close`.
- int alloc_buf_fd(std::size_t len);
-
- private:
- int m_heap_fd;
-};
diff --git a/glerror.h b/glerror.h
deleted file mode 100644
index f8ace34..0000000
--- a/glerror.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include
-#include
-
-#include
-#include
-
-#define GLERROR() glerror(__LINE__)
-#define EGLERROR() eglerror(__LINE__)
-
-inline void glerror(int line) {
- GLenum error = glGetError();
- if (error != GL_NO_ERROR) {
- std::string output;
- output.resize(128);
- snprintf(output.data(), 128, "GL error detected on line %d: 0x%04x\n",
- line, error);
- std::cout << output << std::endl;
- throw std::runtime_error(output);
- }
-}
-
-inline void eglerror(int line) {
- EGLint error = eglGetError();
- if (error != EGL_SUCCESS) {
- std::string output;
- output.resize(128);
- snprintf(output.data(), 128, "EGL error detected on line %d: 0x%04x\n",
- line, error);
- std::cout << output << std::endl;
- throw std::runtime_error(output);
- }
-}
\ No newline at end of file
diff --git a/headless_opengl.h b/headless_opengl.h
deleted file mode 100644
index 78bfeeb..0000000
--- a/headless_opengl.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct HeadlessData {
- int gbmFd;
- struct gbm_device *gbmDevice;
-
- EGLDisplay display;
- EGLContext context;
-};
-
-struct HeadlessData createHeadless();
-void destroyHeadless(struct HeadlessData status);
-
-#ifdef __cplusplus
-}
-#endif
\ No newline at end of file
diff --git a/include/blocking_future.h b/include/blocking_future.h
new file mode 100644
index 0000000..fee0b18
--- /dev/null
+++ b/include/blocking_future.h
@@ -0,0 +1,49 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+template class BlockingFuture {
+ public:
+ BlockingFuture() = default;
+
+ void set(T &&item) {
+ std::unique_lock lock(m_mutex);
+ m_data = std::make_optional<>(std::forward(item));
+ lock.unlock();
+ m_cond.notify_one();
+ }
+
+ T take() {
+ std::unique_lock lock(m_mutex);
+ m_cond.wait(lock, [&] { return m_data.has_value(); });
+
+ auto item = std::move(m_data.value());
+ m_data.reset();
+ return item;
+ }
+
+ private:
+ std::optional m_data;
+ std::mutex m_mutex;
+ std::condition_variable m_cond;
+};
diff --git a/camera_grabber.h b/include/camera_grabber.h
similarity index 67%
rename from camera_grabber.h
rename to include/camera_grabber.h
index a37d1c5..49a0717 100644
--- a/camera_grabber.h
+++ b/include/camera_grabber.h
@@ -1,13 +1,31 @@
+/*
+ * 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 .
+ */
+
#pragma once
#include
#include
-#include "camera_model.h"
-
#include
#include
#include
+#include
+
+#include "camera_model.h"
struct CameraSettings {
int32_t exposureTimeUs = 10000;
@@ -31,7 +49,7 @@ class CameraGrabber {
void setOnData(std::function onData);
void resetOnData();
- inline const CameraModel model() { return m_model; }
+ inline CameraModel model() const { return m_model; }
inline CameraSettings &cameraSettings() { return m_settings; }
@@ -50,7 +68,8 @@ class CameraGrabber {
std::vector> m_requests;
std::shared_ptr m_camera;
CameraModel m_model;
- std::optional> m_cameraExposureProfiles;
+ std::optional>
+ m_cameraExposureProfiles;
std::unique_ptr m_config;
std::optional> m_onData;
@@ -58,6 +77,5 @@ class CameraGrabber {
CameraSettings m_settings{};
bool running = false;
-
void setControls(libcamera::Request *request);
};
diff --git a/include/camera_manager.h b/include/camera_manager.h
new file mode 100644
index 0000000..5978197
--- /dev/null
+++ b/include/camera_manager.h
@@ -0,0 +1,27 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+#include
+#include
+
+#include
+#include
+#include
+
+std::vector> GetAllCameraIDs();
diff --git a/include/camera_model.h b/include/camera_model.h
new file mode 100644
index 0000000..e1082ce
--- /dev/null
+++ b/include/camera_model.h
@@ -0,0 +1,33 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+#include
+
+enum CameraModel {
+ Disconnected = 0,
+ OV5647, // Picam v1
+ IMX219, // Picam v2
+ IMX708, // Picam v3
+ IMX477, // Picam HQ
+ OV9281,
+ OV7251,
+ Unknown
+};
+
+CameraModel stringToModel(const std::string &model);
diff --git a/camera_runner.h b/include/camera_runner.h
similarity index 69%
rename from camera_runner.h
rename to include/camera_runner.h
index ada44cb..947540c 100644
--- a/camera_runner.h
+++ b/include/camera_runner.h
@@ -1,30 +1,49 @@
+/*
+ * 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 .
+ */
+
#pragma once
-#include "blocking_future.h"
-#include "camera_grabber.h"
-#include "concurrent_blocking_queue.h"
-#include "dma_buf_alloc.h"
-#include "gl_hsv_thresholder.h"
-#include "libcamera_opengl_utility.h"
+#include
#include
-#include
#include
#include
#include
+#include
#include
+#include "blocking_future.h"
+#include "camera_grabber.h"
+#include "concurrent_blocking_queue.h"
+#include "dma_buf_alloc.h"
+#include "gl_hsv_thresholder.h"
+#include "libcamera_opengl_utility.h"
+
struct MatPair {
cv::Mat color;
cv::Mat processed;
- long captureTimestamp; // In libcamera time units, hopefully uS? TODO actually implement
+ int64_t captureTimestamp; // In libcamera time units, hopefully uS? TODO
+ // actually implement
int32_t frameProcessingType; // enum value of shader run on the image
MatPair() = default;
explicit MatPair(int width, int height)
- : color(height, width, CV_8UC3),
- processed(height, width, CV_8UC1) {}
+ : color(height, width, CV_8UC3), processed(height, width, CV_8UC1) {}
};
// Note: destructing this class without calling `stop` if `start` was called
@@ -37,7 +56,7 @@ class CameraRunner {
inline CameraGrabber &cameraGrabber() { return grabber; }
inline GlHsvThresholder &thresholder() { return m_thresholder; }
- inline const CameraModel model() { return grabber.model(); }
+ inline CameraModel model() const { return grabber.model(); }
void setCopyOptions(bool copyInput, bool copyOutput);
// Note: all following functions must be protected by mutual exclusion.
@@ -55,9 +74,7 @@ class CameraRunner {
void requestShaderIdx(int idx);
-
private:
-
struct GpuQueueData {
int fd;
ProcessType type;
@@ -81,7 +98,6 @@ class CameraRunner {
std::thread threshold;
std::thread display;
-
std::atomic m_shaderIdx = 0;
std::atomic m_copyInput;
diff --git a/concurrent_blocking_queue.h b/include/concurrent_blocking_queue.h
similarity index 65%
rename from concurrent_blocking_queue.h
rename to include/concurrent_blocking_queue.h
index ab712e8..d462911 100644
--- a/concurrent_blocking_queue.h
+++ b/include/concurrent_blocking_queue.h
@@ -1,9 +1,27 @@
+/*
+ * 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 .
+ */
+
#pragma once
#include
#include
#include
#include
+#include
template class ConcurrentBlockingQueue {
public:
diff --git a/include/dma_buf_alloc.h b/include/dma_buf_alloc.h
new file mode 100644
index 0000000..eedfff8
--- /dev/null
+++ b/include/dma_buf_alloc.h
@@ -0,0 +1,34 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+#include
+#include
+
+class DmaBufAlloc {
+ public:
+ explicit DmaBufAlloc(const std::string &heap_name);
+ ~DmaBufAlloc();
+
+ // Allocates a DMA-BUF of size len. The returned
+ // fd can be closed using `close`.
+ int alloc_buf_fd(size_t len);
+
+ private:
+ int m_heap_fd;
+};
diff --git a/gl_hsv_thresholder.h b/include/gl_hsv_thresholder.h
similarity index 69%
rename from gl_hsv_thresholder.h
rename to include/gl_hsv_thresholder.h
index 2bf02e2..50af3d2 100644
--- a/gl_hsv_thresholder.h
+++ b/include/gl_hsv_thresholder.h
@@ -1,7 +1,25 @@
+/*
+ * 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 .
+ */
+
#pragma once
+#include
+
#include
-#include
#include
#include
#include
@@ -16,11 +34,13 @@
#include "headless_opengl.h"
-enum class ProcessType: int32_t {
+enum class ProcessType : int32_t {
None = 0,
Hsv,
Gray,
Adaptive,
+ Gray_passthrough,
+ NUM_PROCESS_TYPES
};
class GlHsvThresholder {
@@ -56,7 +76,6 @@ class GlHsvThresholder {
void setHsvThresholds(double hl, double sl, double vl, double hu, double su,
double vu, bool hueInverted);
-
private:
int m_width;
int m_height;
@@ -72,7 +91,7 @@ class GlHsvThresholder {
GLuint m_min_max_framebuffer = 0;
std::vector m_programs = {};
- HeadlessData m_status{};
+ HeadlessData m_status;
EGLDisplay m_display;
EGLContext m_context;
diff --git a/gl_shader_source.h b/include/gl_shader_source.h
similarity index 78%
rename from gl_shader_source.h
rename to include/gl_shader_source.h
index 306e437..d0784db 100644
--- a/gl_shader_source.h
+++ b/include/gl_shader_source.h
@@ -1,3 +1,24 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+// clang-format off
+
static constexpr const char *VERTEX_SOURCE =
"#version 100\n"
""
@@ -9,6 +30,7 @@ static constexpr const char *VERTEX_SOURCE =
" gl_Position = vec4(vertex, 0.0, 1.0);"
"}";
+
static constexpr const char *NONE_FRAGMENT_SOURCE =
"#version 100\n"
"#extension GL_OES_EGL_image_external : require\n"
@@ -25,6 +47,7 @@ static constexpr const char *NONE_FRAGMENT_SOURCE =
" gl_FragColor = vec4(color.bgr, 0);"
"}";
+
static constexpr const char *HSV_FRAGMENT_SOURCE =
"#version 100\n"
"#extension GL_OES_EGL_image_external : require\n"
@@ -67,6 +90,25 @@ static constexpr const char *HSV_FRAGMENT_SOURCE =
" gl_FragColor = vec4(col.bgr, int(inRange(rgb2hsv(col))));"
"}";
+
+static constexpr const char *GRAY_PASSTHROUGH_FRAGMENT_SOURCE =
+ "#version 100\n"
+ "#extension GL_OES_EGL_image_external : require\n"
+ ""
+ "precision lowp float;"
+ "precision lowp int;"
+ ""
+ "varying vec2 texcoord;"
+ ""
+ "uniform samplerExternalOES tex;"
+ ""
+ "void main(void) {"
+ // We get in (gray, gray, gray), I think. So just copy the R channel
+ " vec3 gray_gray_gray = texture2D(tex, texcoord).rgb;"
+ " gl_FragColor = vec4(gray_gray_gray.bgr, gray_gray_gray[0]);"
+ "}";
+
+
static constexpr const char *GRAY_FRAGMENT_SOURCE =
"#version 100\n"
"#extension GL_OES_EGL_image_external : require\n"
@@ -86,6 +128,7 @@ static constexpr const char *GRAY_FRAGMENT_SOURCE =
" gl_FragColor = vec4(color.bgr, gammaGray);"
"}";
+
static constexpr const char *TILING_FRAGMENT_SOURCE =
"#version 100\n"
""
@@ -110,6 +153,7 @@ static constexpr const char *TILING_FRAGMENT_SOURCE =
" gl_FragColor = vec4(max_so_far, min_so_far, 0.0, 0.0);"
"}";
+
static constexpr const char *THRESHOLDING_FRAGMENT_SOURCE =
"#version 100\n"
""
@@ -142,3 +186,5 @@ static constexpr const char *THRESHOLDING_FRAGMENT_SOURCE =
" }"
" gl_FragColor = vec4(color.bgr, output_);"
"}";
+
+// clang-format on
diff --git a/include/glerror.h b/include/glerror.h
new file mode 100644
index 0000000..e834b6b
--- /dev/null
+++ b/include/glerror.h
@@ -0,0 +1,52 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+
+#define GLERROR() glerror(__LINE__)
+#define EGLERROR() eglerror(__LINE__)
+
+inline void glerror(int line) {
+ GLenum error = glGetError();
+ if (error != GL_NO_ERROR) {
+ std::string output;
+ output.resize(128);
+ std::snprintf(output.data(), output.size(),
+ "GL error detected on line %d: 0x%04x\n", line, error);
+ std::cout << output << std::endl;
+ throw std::runtime_error(output);
+ }
+}
+
+inline void eglerror(int line) {
+ EGLint error = eglGetError();
+ if (error != EGL_SUCCESS) {
+ std::string output;
+ output.resize(128);
+ std::snprintf(output.data(), output.size(),
+ "EGL error detected on line %d: 0x%04x\n", line, error);
+ std::cout << output << std::endl;
+ throw std::runtime_error(output);
+ }
+}
diff --git a/include/headless_opengl.h b/include/headless_opengl.h
new file mode 100644
index 0000000..4406789
--- /dev/null
+++ b/include/headless_opengl.h
@@ -0,0 +1,39 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct HeadlessData {
+ int gbmFd;
+ struct gbm_device *gbmDevice;
+
+ EGLDisplay display;
+ EGLContext context;
+};
+
+struct HeadlessData createHeadless(void);
+void destroyHeadless(struct HeadlessData status);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/include/latch.hpp b/include/latch.hpp
new file mode 100644
index 0000000..3e55bdd
--- /dev/null
+++ b/include/latch.hpp
@@ -0,0 +1,65 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+#include
+#include
+#include
+
+// A poor substitute for C++20's latch
+// This one is blocking on more operations, but
+// exposes a similar API
+
+class Latch {
+ public:
+ explicit Latch(std::ptrdiff_t expected) : expected(expected) {}
+
+ ~Latch() = default;
+ Latch(const Latch &) = delete;
+ Latch &operator=(const Latch &) = delete;
+
+ void count_down() {
+ std::unique_lock lock(mut);
+ expected -= 1;
+ if (expected == 0) {
+ lock.unlock();
+ cv.notify_all();
+ }
+ }
+
+ void wait() {
+ std::unique_lock lock(mut);
+ cv.wait(lock, [this] { return expected == 0; });
+ }
+
+ void arrive_and_wait() {
+ std::unique_lock lock(mut);
+ expected -= 1;
+ if (expected == 0) {
+ lock.unlock();
+ cv.notify_all();
+ } else {
+ cv.wait(lock, [this] { return expected == 0; });
+ }
+ }
+
+ private:
+ std::ptrdiff_t expected;
+ std::condition_variable cv;
+ std::mutex mut;
+};
diff --git a/include/libcamera_jni.hpp b/include/libcamera_jni.hpp
new file mode 100644
index 0000000..671190e
--- /dev/null
+++ b/include/libcamera_jni.hpp
@@ -0,0 +1,162 @@
+/*
+ * 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 .
+ */
+
+/* DO NOT EDIT THIS std::FILE - it is machine generated */
+#include
+
+/* Header for class org_photonvision_raspi_LibCameraJNI */
+
+#ifndef PHOTON_LIBCAMERA_GL_DRIVER_INCLUDE_LIBCAMERA_JNI_HPP_
+#define PHOTON_LIBCAMERA_GL_DRIVER_INCLUDE_LIBCAMERA_JNI_HPP_
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: getSensorModelRaw
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jint JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_getSensorModelRaw(JNIEnv *, jclass,
+ jstring);
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: isVCSMSupported
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_isLibraryWorking(JNIEnv *, jclass);
+
+/*
+ * Class: test
+ * Method: getCameraNames
+ * Signature: ()[Ljava/lang/String;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_getCameraNames(JNIEnv *, jclass);
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: createCamera
+ * Signature: (III)Z
+ */
+JNIEXPORT jlong JNICALL Java_org_photonvision_raspi_LibCameraJNI_createCamera(
+ JNIEnv *, jclass, jstring, jint, jint, jint);
+
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_startCamera(JNIEnv *, jclass, jlong);
+
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_stopCamera(JNIEnv *, jclass, jlong);
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: destroyCamera
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_destroyCamera(JNIEnv *, jclass, jlong);
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setThresholds
+ * Signature: (DDDDDD)V
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setThresholds(JNIEnv *, jclass, jlong,
+ jdouble, jdouble,
+ jdouble, jdouble,
+ jdouble, jdouble,
+ jboolean);
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setExposure
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setExposure(
+ JNIEnv *, jclass, jlong, jint);
+
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setAutoExposure(
+ JNIEnv *env, jclass, jlong, jboolean doAutoExposure);
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setBrightness
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setBrightness(JNIEnv *, jclass, jlong,
+ jdouble);
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setGain
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setAnalogGain(JNIEnv *, jclass, jlong,
+ jdouble);
+
+JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setAwbGain(
+ JNIEnv *, jclass, jlong, jdouble red, jdouble blue);
+
+JNIEXPORT jlong JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp(JNIEnv *,
+ jclass);
+
+JNIEXPORT jlong JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime(JNIEnv *, jclass,
+ jlong);
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: grabFrame
+ * Signature: (Z)J
+ */
+JNIEXPORT jlong JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_awaitNewFrame(JNIEnv *, jclass, jlong);
+
+JNIEXPORT jlong JNICALL Java_org_photonvision_raspi_LibCameraJNI_takeColorFrame(
+ JNIEnv *, jclass, jlong);
+
+JNIEXPORT jlong JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_takeProcessedFrame(JNIEnv *, jclass,
+ jlong);
+
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setFramesToCopy(JNIEnv *, jclass,
+ jlong, jboolean copyIn,
+ jboolean copyOut);
+
+JNIEXPORT jint JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_getGpuProcessType(JNIEnv *, jclass,
+ jlong);
+
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType(JNIEnv *, jclass,
+ jlong, jint);
+
+JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_releasePair(
+ JNIEnv *env, jclass, jlong pair_);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+#endif // PHOTON_LIBCAMERA_GL_DRIVER_INCLUDE_LIBCAMERA_JNI_HPP_
diff --git a/include/libcamera_opengl_utility.h b/include/libcamera_opengl_utility.h
new file mode 100644
index 0000000..13cb41b
--- /dev/null
+++ b/include/libcamera_opengl_utility.h
@@ -0,0 +1,30 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+// These headers are weird for some reason
+
+// clang-format off
+#include
+
+#include
+
+// clang-format on
+
+EGLint rangeFromColorspace(const libcamera::ColorSpace &colorSpace);
+EGLint encodingFromColorspace(const libcamera::ColorSpace &colorSpace);
diff --git a/latch.hpp b/latch.hpp
deleted file mode 100644
index 11f52e6..0000000
--- a/latch.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma onec
-
-#include
-#include
-#include
-
-// A poor substitute for C++20's latch
-// This one is blocking on more operations, but
-// exposes a similar API
-
-class Latch {
-public:
- explicit Latch(ptrdiff_t expected): expected(expected) {};
-
- ~Latch() = default;
- Latch(const Latch&) = delete;
- Latch& operator=(const Latch&) = delete;
-
- void count_down() {
- std::unique_lock lock(mut);
- expected -= 1;
- if (expected == 0) {
- lock.unlock();
- cv.notify_all();
- }
- }
-
- void wait() {
- std::unique_lock lock(mut);
- cv.wait(lock, [this] { return expected == 0; });
- }
-
- void arrive_and_wait() {
- std::unique_lock lock(mut);
- expected -= 1;
- if (expected == 0) {
- lock.unlock();
- cv.notify_all();
- } else {
- cv.wait(lock, [this] { return expected == 0; });
- }
- }
-private:
- ptrdiff_t expected;
- std::condition_variable cv;
- std::mutex mut;
-};
\ No newline at end of file
diff --git a/libcamera_jni.cpp b/libcamera_jni.cpp
deleted file mode 100644
index 81a9e34..0000000
--- a/libcamera_jni.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2022 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 .
- */
-
-#include "libcamera_jni.hpp" // Generated
-#include
-
-#include "camera_manager.h"
-#include "camera_runner.h"
-
-static CameraRunner *runner = nullptr;
-
-extern "C" {
-
-#include
-
-// We use jlongs like pointers, so they better be large enough
-static_assert(sizeof(void *) <= sizeof(jlong));
-
-JNIEXPORT jboolean
-Java_org_photonvision_raspi_LibCameraJNI_isLibraryWorking(JNIEnv *env, jclass) {
- // TODO
- return true;
-}
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_createCamera(JNIEnv *env, jclass,
- jint width, jint height,
- jint rotation) {
-
- std::vector> cameras = GetAllCameraIDs();
-
- // Yeet all USB cameras (I hope)
- auto rem = std::remove_if(cameras.begin(), cameras.end(), [](auto &cam) {
- return cam->id().find("/usb") != std::string::npos;
- });
- cameras.erase(rem, cameras.end());
-
- if (cameras.empty()) {
- runner = 0;
- return false;
- }
-
- // Otherwise, just create the first camera left
- runner = new CameraRunner(width, height, rotation, cameras[0]);
- return true;
-}
-
-JNIEXPORT jint Java_org_photonvision_raspi_LibCameraJNI_getSensorModelRaw(
- JNIEnv *env, jclass clazz) {
-
- bool runner_exists = runner != 0;
- if (!runner_exists) {
- Java_org_photonvision_raspi_LibCameraJNI_createCamera(env, clazz,
- 320, 240, 30);
- }
-
- if (!runner) {
- return 0;
- }
-
- jint model = runner->model();
-
- if (!runner_exists) {
- Java_org_photonvision_raspi_LibCameraJNI_destroyCamera(env, clazz);
- }
-
- return model;
-}
-
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_startCamera(JNIEnv *, jclass) {
- if (!runner) {
- return false;
- }
-
- runner->start();
- return true;
-}
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_stopCamera(JNIEnv *, jclass) {
- if (!runner) {
- return false;
- }
-
- runner->stop();
- return true;
-}
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_destroyCamera(JNIEnv *env, jclass) {
- if (!runner) {
- return false;
- }
-
- delete runner;
- runner = nullptr;
- return true;
-}
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setThresholds(JNIEnv *env, jclass,
- jdouble hl, jdouble sl,
- jdouble vl, jdouble hu,
- jdouble su, jdouble vu,
- jboolean hueInverted) {
- if (!runner) {
- return false;
- }
-
- // printf("Setting HSV to %f-%f %f-%f %f-%f\n", hl, hu, sl, su, vl, vu);
-
- // TODO hue inversion
- runner->thresholder().setHsvThresholds(hl, sl, vl, hu, su, vu, hueInverted);
- return true;
-}
-
-JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setExposure(
- JNIEnv *env, jclass, jint exposure) {
- if (!runner) {
- return false;
- }
-
- runner->cameraGrabber().cameraSettings().exposureTimeUs = exposure;
- return true;
-}
-
-JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setAutoExposure(
- JNIEnv *env, jclass, jboolean doAutoExposure) {
- if (!runner) {
- return false;
- }
-
- runner->cameraGrabber().cameraSettings().doAutoExposure = doAutoExposure;
- return true;
-}
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setBrightness(JNIEnv *env, jclass,
- jdouble brightness) {
- if (!runner) {
- return false;
- }
-
- runner->cameraGrabber().cameraSettings().brightness = brightness;
- return true;
-}
-
-JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setAwbGain(
- JNIEnv *env, jclass, jdouble red, jdouble blue) {
- if (!runner) {
- return false;
- }
-
- printf("Setting red %f blue %f\n", (float)red, (float)blue);
-
- runner->cameraGrabber().cameraSettings().awbRedGain = red;
- runner->cameraGrabber().cameraSettings().awbBlueGain = blue;
- return true;
-}
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setAnalogGain(JNIEnv *env, jclass,
- jdouble analog) {
- if (!runner) {
- return false;
- }
-
- runner->cameraGrabber().cameraSettings().analogGain = analog;
- return true;
-}
-
-JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setRotation(
- JNIEnv *env, jclass, jint rotationOrdinal) {
- if (!runner) {
- return false;
- }
-
- // int rotation = (rotationOrdinal + 3) * 90; // Degrees
- // TODO
- return true;
-}
-
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setFramesToCopy(JNIEnv *, jclass, jboolean copyIn, jboolean copyOut) {
- if (!runner) {
- return false;
- }
-
- runner->setCopyOptions(copyIn, copyOut);
- return true;
-}
-
-static MatPair pair = {};
-
-JNIEXPORT jlong JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime(JNIEnv *env, jclass) {
- return pair.captureTimestamp;
-}
-
-JNIEXPORT jlong JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp(JNIEnv *env, jclass) {
- timespec ts;
- clock_gettime(CLOCK_BOOTTIME, &ts);
- uint64_t now_nsec = (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
- return (jlong)now_nsec;
-}
-
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_awaitNewFrame(JNIEnv *env, jclass) {
- if (!runner) {
- // NULL
- return false;
- }
-
- pair = runner->outgoing.take();
- return true;
-}
-
-JNIEXPORT jlong JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_takeColorFrame(JNIEnv *env, jclass) {
- if (!runner) {
- // NULL
- return 0;
- }
-
- return reinterpret_cast(new cv::Mat(std::move(pair.color)));
-}
-
-JNIEXPORT jlong JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_takeProcessedFrame(JNIEnv *env,
- jclass) {
- if (!runner) {
- // NULL
- return 0;
- }
-
- return reinterpret_cast(new cv::Mat(std::move(pair.processed)));
-}
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType(JNIEnv *env, jclass,
- jint idx) {
- if (!runner) {
- return false;
- }
-
- runner->requestShaderIdx(idx);
-
- return true;
-}
-
-JNIEXPORT jint JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_getGpuProcessType(JNIEnv *, jclass) {
- return pair.frameProcessingType;
-}
-
-} // extern "C"
diff --git a/libcamera_jni.hpp b/libcamera_jni.hpp
deleted file mode 100644
index 939e1e5..0000000
--- a/libcamera_jni.hpp
+++ /dev/null
@@ -1,127 +0,0 @@
-
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include
-/* Header for class org_photonvision_raspi_LibCameraJNI */
-
-#ifndef _Included_org_photonvision_raspi_LibCameraJNI
-#define _Included_org_photonvision_raspi_LibCameraJNI
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class: org_photonvision_raspi_LibCameraJNI
- * Method: getSensorModelRaw
- * Signature: ()Ljava/lang/String;
- */
-JNIEXPORT jint JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_getSensorModelRaw(JNIEnv *, jclass);
-
-/*
- * Class: org_photonvision_raspi_LibCameraJNI
- * Method: isVCSMSupported
- * Signature: ()Z
- */
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_isLibraryWorking(JNIEnv *, jclass);
-
-/*
- * Class: org_photonvision_raspi_LibCameraJNI
- * Method: createCamera
- * Signature: (III)Z
- */
-JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_createCamera(
- JNIEnv *, jclass, jint, jint, jint);
-
-JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_startCamera(
- JNIEnv *, jclass);
-
-JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_stopCamera(
- JNIEnv *, jclass);
-
-/*
- * Class: org_photonvision_raspi_LibCameraJNI
- * Method: destroyCamera
- * Signature: ()Z
- */
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_destroyCamera(JNIEnv *, jclass);
-
-/*
- * Class: org_photonvision_raspi_LibCameraJNI
- * Method: setThresholds
- * Signature: (DDDDDD)V
- */
-JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setThresholds(
- JNIEnv *, jclass, jdouble, jdouble, jdouble, jdouble, jdouble, jdouble, jboolean);
-
-/*
- * Class: org_photonvision_raspi_LibCameraJNI
- * Method: setExposure
- * Signature: (I)Z
- */
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setExposure(JNIEnv *, jclass, jint);
-
-JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setAutoExposure(
- JNIEnv *env, jclass, jboolean doAutoExposure);
-
-/*
- * Class: org_photonvision_raspi_LibCameraJNI
- * Method: setBrightness
- * Signature: (I)Z
- */
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setBrightness(JNIEnv *, jclass, jdouble);
-
-/*
- * Class: org_photonvision_raspi_LibCameraJNI
- * Method: setGain
- * Signature: (I)Z
- */
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setAnalogGain(JNIEnv *, jclass, jdouble);
-
-JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setAwbGain(
- JNIEnv *, jclass, jdouble red, jdouble blue);
-
-/*
- * Class: org_photonvision_raspi_LibCameraJNI
- * Method: setRotation
- * Signature: (I)Z
- */
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setRotation(JNIEnv *, jclass, jint);
-
-JNIEXPORT jlong JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp(JNIEnv *, jclass);
-
-JNIEXPORT jlong JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime(JNIEnv *, jclass);
-
-/*
- * Class: org_photonvision_raspi_LibCameraJNI
- * Method: grabFrame
- * Signature: (Z)J
- */
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_awaitNewFrame(JNIEnv *, jclass);
-
-JNIEXPORT jlong JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_takeColorFrame(JNIEnv *, jclass);
-
-JNIEXPORT jlong JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_takeProcessedFrame(JNIEnv *, jclass);
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setFramesToCopy(JNIEnv *, jclass, jboolean copyIn, jboolean copyOut);
-
-JNIEXPORT jint JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_getGpuProcessType(JNIEnv *, jclass);
-
-JNIEXPORT jboolean JNICALL
-Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType(JNIEnv *, jclass, jint);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/libcamera_opengl_utility.h b/libcamera_opengl_utility.h
deleted file mode 100644
index ca3974d..0000000
--- a/libcamera_opengl_utility.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-// These headers are weird for some reason
-
-// clang-format off
-#include
-#include
-// clang-format on
-
-EGLint rangeFromColorspace(const libcamera::ColorSpace &colorSpace);
-EGLint encodingFromColorspace(const libcamera::ColorSpace &colorSpace);
diff --git a/main.cpp b/main.cpp
index 40ac6b8..e4702e6 100644
--- a/main.cpp
+++ b/main.cpp
@@ -6,70 +6,99 @@
#include
#include
+#include
+
+#include "camera_manager.h"
+
+#include
+#include
+#include
+
+#include "headless_opengl.h"
+#include "glerror.h"
+
+#define GLERROR() glerror(__LINE__)
+#define EGLERROR() eglerror(__LINE__)
-enum class ProcessType_: int32_t {
- None = 0,
- Hsv,
- Gray,
- Adaptive,
-};
void test_res(int width, int height) {
- int rotation = 180;
- Java_org_photonvision_raspi_LibCameraJNI_createCamera(nullptr, nullptr,
- width, height, rotation);
- // Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType(nullptr, nullptr, 1);
- Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType(nullptr, nullptr, (jint)ProcessType_::Hsv);
- Java_org_photonvision_raspi_LibCameraJNI_setFramesToCopy(nullptr, nullptr, true, true);
- Java_org_photonvision_raspi_LibCameraJNI_startCamera(nullptr, nullptr);
-
- Java_org_photonvision_raspi_LibCameraJNI_setExposure(nullptr, nullptr, 80 * 800);
- Java_org_photonvision_raspi_LibCameraJNI_setBrightness(nullptr, nullptr, 0.0);
- Java_org_photonvision_raspi_LibCameraJNI_setAnalogGain(nullptr, nullptr, 20);
- Java_org_photonvision_raspi_LibCameraJNI_setAutoExposure(nullptr, nullptr, true);
-
- auto start = std::chrono::steady_clock::now();
-
- while (std::chrono::steady_clock::now() - start < std::chrono::seconds(3)) {
- bool ready = Java_org_photonvision_raspi_LibCameraJNI_awaitNewFrame(nullptr, nullptr);
- if (ready) {
- static int i = 0;
-
- cv::Mat color_mat = *(cv::Mat*)Java_org_photonvision_raspi_LibCameraJNI_takeColorFrame(nullptr, nullptr);
- cv::Mat threshold_mat = *(cv::Mat*)Java_org_photonvision_raspi_LibCameraJNI_takeProcessedFrame(nullptr, nullptr);
-
- uint64_t captureTime = Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime(nullptr, nullptr);
- uint64_t now = Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp(nullptr, nullptr);
- printf("now %lu capture %lu latency %f\n", now, captureTime, (double)(now - captureTime) / 1000000.0);
-
- i++;
- static char arr[50];
- snprintf(arr,sizeof(arr),"color_%i.png", i);
- cv::imwrite(arr, color_mat);
- snprintf(arr,sizeof(arr),"thresh_%i.png", i);
- cv::imwrite(arr, threshold_mat);
- }
+
+ std::vector> cameras = GetAllCameraIDs();
+
+ // Yeet all USB cameras (I hope)
+ auto rem = std::remove_if(cameras.begin(), cameras.end(), [](auto &cam) {
+ return cam->id().find("/usb") != std::string::npos;
+ });
+ cameras.erase(rem, cameras.end());
+
+ for (const auto& cam : cameras) {
+ printf("Camera at: %s\n", cam->id().c_str());
}
- Java_org_photonvision_raspi_LibCameraJNI_stopCamera(nullptr, nullptr);
- Java_org_photonvision_raspi_LibCameraJNI_destroyCamera(nullptr, nullptr);
+ std::vector runners;
+
+ int rotation = 0;
+
+ for (auto& c : cameras) {
+ auto r = new CameraRunner(width, height, rotation, c);
+ runners.push_back(r);
+ r->start();
+ r->setCopyOptions(true, true);
+ r->requestShaderIdx((int)ProcessType::Gray_passthrough);
+
+ r->cameraGrabber().cameraSettings().exposureTimeUs = 100000;
+ r->cameraGrabber().cameraSettings().analogGain = 4;
+ r->cameraGrabber().cameraSettings().brightness = 0.0;
- std::this_thread::sleep_for(std::chrono::milliseconds(2000));
+ printf("Started %s!\n", c->id().c_str());
+ }
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ for (int i = 0; i < 50; i++) {
+ int j = 0;
+ for (auto r : runners) {
+ auto pair = r->outgoing.take();
+ auto color_mat = cv::Mat(std::move(pair.color));
+ auto processed_mat = cv::Mat(std::move(pair.processed));
+
+ auto now = Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp(nullptr, NULL);
+ auto then = Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime(nullptr, NULL, (long int)&pair);
+
+ printf("now %li then %li dt %i\n", now, then, now-then);
+
+
+ if (i % 30 == 0) {
+ printf("saving cam %i idx %i\n", j, i);
+ cv::Mat bgr;
+ cv::cvtColor(processed_mat, bgr, cv::COLOR_GRAY2BGR);
+ static char arr[50];
+ snprintf(arr,sizeof(arr),"color_cam%i_%i_%ix%i.png", j, i, width, height);
+ cv::imwrite(arr, color_mat);
+ snprintf(arr,sizeof(arr),"thresh_cam%i_%i_%ix%i.png", j, i, width, height);
+ cv::imwrite(arr, bgr);
+ }
+
+ color_mat.release();
+ processed_mat.release();
+
+ j++;
+ }
+ }
+
+ printf("Destroying all!\n");
+ for (auto& r : runners) {
+ r->stop();
+ delete r;
+ }
}
int main() {
- // test_res(1920, 1080);
- test_res(320, 240);
- // test_res(640, 480);
- // test_res(960, 720);
- // // test_res(2592, 1944);
- // test_res(2592/2, 1944/2);
- // test_res(1920, 1080);
- // test_res(320, 240);
- // test_res(640, 480);
- // test_res(960, 720);
- // // test_res(2592, 1944);
- // test_res(2592/2, 1944/2);
+
+ for (int i = 0; i < 1; i++) {
+ test_res(1280, 800);
+ // test_res(1280/2, 800/2);
+ // test_res(640, 480);
+ }
std::cout << "Done" << std::endl;
diff --git a/camera_grabber.cpp b/src/camera_grabber.cpp
similarity index 66%
rename from camera_grabber.cpp
rename to src/camera_grabber.cpp
index bdd5695..df239cb 100644
--- a/camera_grabber.cpp
+++ b/src/camera_grabber.cpp
@@ -1,19 +1,37 @@
-#include "camera_grabber.h"
+/*
+ * 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 .
+ */
-#include
-#include
+#include "camera_grabber.h"
#include
#include
+#include
+#include
+
CameraGrabber::CameraGrabber(std::shared_ptr camera,
int width, int height, int rotation)
- : m_buf_allocator(camera), m_camera(std::move(camera)), m_cameraExposureProfiles(std::nullopt) {
+ : m_buf_allocator(camera), m_camera(std::move(camera)),
+ m_cameraExposureProfiles(std::nullopt) {
if (m_camera->acquire()) {
throw std::runtime_error("failed to acquire camera");
}
-
+
// Determine model
auto &cprp = m_camera->properties();
auto model = cprp.get(libcamera::properties::Model);
@@ -23,27 +41,30 @@ CameraGrabber::CameraGrabber(std::shared_ptr camera,
m_model = Unknown;
}
- std::cout << "Model " << m_model << std::endl;
+ std::cout << "Model " << m_model << std::endl;
auto config = m_camera->generateConfiguration(
{libcamera::StreamRole::VideoRecording});
-
+
// print active arrays
- if (m_camera->properties().contains(libcamera::properties::PIXEL_ARRAY_ACTIVE_AREAS)) {
- printf("Active areas:\n");
- auto rects = m_camera->properties().get(libcamera::properties::PixelArrayActiveAreas);
+ if (m_camera->properties().contains(
+ libcamera::properties::PIXEL_ARRAY_ACTIVE_AREAS)) {
+ std::printf("Active areas:\n");
+ auto rects = m_camera->properties().get(
+ libcamera::properties::PixelArrayActiveAreas);
if (rects.has_value()) {
- for(const auto rect : rects.value()) {
+ for (const auto rect : rects.value()) {
std::cout << rect.toString() << std::endl;
}
}
- } else
- printf("No active areas\n");
+ } else {
+ std::printf("No active areas??\n");
+ }
config->at(0).size.width = width;
config->at(0).size.height = height;
- printf("Rotation = %i\n", rotation);
+ std::printf("Rotation = %i\n", rotation);
if (rotation == 180) {
config->orientation = libcamera::Orientation::Rotate180;
} else {
@@ -58,7 +79,7 @@ CameraGrabber::CameraGrabber(std::shared_ptr camera,
throw std::runtime_error("failed to configure stream");
}
- std::cout << config->at(0).toString() << std::endl;
+ std::cout << "Selected configuration: " << config->at(0).toString() << std::endl;
auto stream = config->at(0).stream();
if (m_buf_allocator.allocate(stream) < 0) {
@@ -69,7 +90,7 @@ CameraGrabber::CameraGrabber(std::shared_ptr camera,
for (const auto &buffer : m_buf_allocator.buffers(stream)) {
auto request = m_camera->createRequest();
- auto &controls = request->controls();
+ // auto &controls = request->controls();
// controls.set(libcamera::controls::FrameDurationLimits,
// {static_cast(8333), static_cast(8333)});
// controls.set(libcamera::controls::ExposureTime, 10000);
@@ -123,32 +144,34 @@ void CameraGrabber::setControls(libcamera::Request *request) {
controls_.set(controls::AwbEnable, false); // AWB disabled
}
controls_.set(controls::AnalogueGain,
- m_settings.analogGain); // Analog gain, min 1 max big number?
+ m_settings.analogGain); // Analog gain, min 1 max big number?
if (m_model != OV9281) {
controls_.set(controls::ColourGains,
- libcamera::Span{
- {m_settings.awbRedGain,
- m_settings.awbBlueGain}}); // AWB gains, red and blue,
- // unknown range
+ libcamera::Span{
+ {m_settings.awbRedGain,
+ m_settings.awbBlueGain}}); // AWB gains, red and
+ // blue, unknown range
}
- // Note about brightness: -1 makes everything look deep fried, 0 is probably best for most things
+ // Note about brightness: -1 makes everything look deep fried, 0 is probably
+ // best for most things
controls_.set(libcamera::controls::Brightness,
- m_settings.brightness); // -1 to 1, 0 means unchanged
+ m_settings.brightness); // -1 to 1, 0 means unchanged
controls_.set(controls::Contrast,
- m_settings.contrast); // Nominal 1
+ m_settings.contrast); // Nominal 1
if (m_model != OV9281) {
controls_.set(controls::Saturation,
- m_settings.saturation); // Nominal 1, 0 would be greyscale
+ m_settings.saturation); // Nominal 1, 0 would be greyscale
}
if (m_settings.doAutoExposure) {
controls_.set(controls::AeEnable,
- true); // Auto exposure disabled
+ true); // Auto exposure disabled
- controls_.set(controls::AeMeteringMode, controls::MeteringCentreWeighted);
+ controls_.set(controls::AeMeteringMode,
+ controls::MeteringCentreWeighted);
if (m_model == OV9281) {
controls_.set(controls::AeExposureMode, controls::ExposureNormal);
} else {
@@ -159,25 +182,24 @@ void CameraGrabber::setControls(libcamera::Request *request) {
// seconds * 1e6 = uS
constexpr const int MIN_FRAME_TIME = 1e6 / 250;
constexpr const int MAX_FRAME_TIME = 1e6 / 15;
- controls_.set(
- libcamera::controls::FrameDurationLimits,
- libcamera::Span{
- {MIN_FRAME_TIME, MAX_FRAME_TIME}});
+ controls_.set(libcamera::controls::FrameDurationLimits,
+ libcamera::Span{
+ {MIN_FRAME_TIME, MAX_FRAME_TIME}});
} else {
controls_.set(controls::AeEnable,
- false); // Auto exposure disabled
+ false); // Auto exposure disabled
controls_.set(controls::ExposureTime,
- m_settings.exposureTimeUs); // in microseconds
+ m_settings.exposureTimeUs); // in microseconds
controls_.set(
libcamera::controls::FrameDurationLimits,
libcamera::Span{
{m_settings.exposureTimeUs,
- m_settings.exposureTimeUs}}); // Set default to zero, we have
- // specified the exposure time
+ m_settings.exposureTimeUs}}); // Set default to zero, we have
+ // specified the exposure time
}
controls_.set(controls::ExposureValue, 0);
-
+
if (m_model != OV7251 && m_model != OV9281) {
controls_.set(controls::Sharpness, 1);
}
@@ -203,7 +225,6 @@ void CameraGrabber::stop() {
m_camera->stop();
}
-
void CameraGrabber::setOnData(
std::function onData) {
m_onData = std::move(onData);
diff --git a/src/camera_manager.cpp b/src/camera_manager.cpp
new file mode 100644
index 0000000..b2c9185
--- /dev/null
+++ b/src/camera_manager.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 .
+ */
+
+#include "camera_manager.h"
+
+std::vector> GetAllCameraIDs() {
+ static libcamera::CameraManager *camera_manager;
+ if (!camera_manager) {
+ camera_manager = new libcamera::CameraManager();
+ camera_manager->start();
+ }
+ std::vector> cams =
+ camera_manager->cameras();
+ return cams;
+}
diff --git a/src/camera_model.cpp b/src/camera_model.cpp
new file mode 100644
index 0000000..19b4df1
--- /dev/null
+++ b/src/camera_model.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 .
+ */
+
+#include "camera_model.h"
+
+#include
+
+CameraModel stringToModel(const std::string &model) {
+ std::printf("Checking model: %s\n", model.c_str());
+ const char *famname = model.c_str();
+ if (!strcmp(famname, "ov5647"))
+ return OV5647;
+ else if (!strcmp(famname, "imx219"))
+ return IMX219;
+ else if (!strcmp(famname, "imx708"))
+ return IMX708;
+ else if (!strcmp(famname, "imx477"))
+ return IMX477;
+ else if (!strcmp(famname, "ov9281"))
+ return OV9281;
+ else if (!strcmp(famname, "ov7251"))
+ return OV7251;
+ else if (!strcmp(famname, "Disconnected"))
+ return Disconnected;
+ else
+ return Unknown;
+}
diff --git a/camera_runner.cpp b/src/camera_runner.cpp
similarity index 74%
rename from camera_runner.cpp
rename to src/camera_runner.cpp
index 43bfbe1..986366f 100644
--- a/camera_runner.cpp
+++ b/src/camera_runner.cpp
@@ -1,3 +1,20 @@
+/*
+ * 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 .
+ */
+
#include "camera_runner.h"
#include
@@ -13,17 +30,15 @@ using latch = std::latch;
using latch = Latch;
#endif
-#include
-#include
-
-#include
#include
-
-#include
+#include
#include
+#include
+#include
+#include
-using namespace std::chrono;
-using namespace std::chrono_literals;
+using steady_clock = std::chrono::steady_clock;
+using namespace std::literals::chrono_literals;
static double approxRollingAverage(double avg, double new_sample) {
avg -= avg / 50;
@@ -35,9 +50,8 @@ static double approxRollingAverage(double avg, double new_sample) {
CameraRunner::CameraRunner(int width, int height, int rotation,
std::shared_ptr cam)
: m_camera(std::move(cam)), m_width(width), m_height(height),
- grabber(m_camera, m_width, m_height, rotation), m_thresholder(m_width, m_height),
- allocer("/dev/dma_heap/linux,cma") {
-
+ grabber(m_camera, m_width, m_height, rotation),
+ m_thresholder(m_width, m_height), allocer("/dev/dma_heap/linux,cma") {
grabber.setOnData(
[&](libcamera::Request *request) { camera_queue.push(request); });
@@ -53,9 +67,7 @@ CameraRunner::~CameraRunner() {
}
}
-void CameraRunner::requestShaderIdx(int idx) {
- m_shaderIdx = idx;
-}
+void CameraRunner::requestShaderIdx(int idx) { m_shaderIdx = idx; }
void CameraRunner::setCopyOptions(bool copyIn, bool copyOut) {
m_copyInput = copyIn;
@@ -75,10 +87,9 @@ void CameraRunner::start() {
start_frame_grabber.count_down();
while (true) {
- // printf("Threshold thread!\n");
+ // std::printf("Threshold thread!\n");
auto request = camera_queue.pop();
-
if (!request) {
break;
}
@@ -96,26 +107,27 @@ void CameraRunner::start() {
static_cast(stride / 2)},
}};
+
auto begintime = steady_clock::now();
auto type = static_cast(m_shaderIdx.load());
- int out = m_thresholder.testFrame(yuv_data,
- encodingFromColorspace(colorspace),
- rangeFromColorspace(colorspace),
- type);
+ int out = m_thresholder.testFrame(
+ yuv_data, encodingFromColorspace(colorspace),
+ rangeFromColorspace(colorspace), type);
if (out != 0) {
/*
From libcamera docs:
- The timestamp, expressed in nanoseconds, represents a monotonically
- increasing counter since the system boot time, as defined by the
- Linux-specific CLOCK_BOOTTIME clock id.
+ The timestamp, expressed in nanoseconds, represents a
+ monotonically increasing counter since the system boot time, as
+ defined by the Linux-specific CLOCK_BOOTTIME clock id.
*/
- uint64_t sensorTimestamp = static_cast(request->metadata()
- .get(libcamera::controls::SensorTimestamp)
- .value_or(0));
+ uint64_t sensorTimestamp = static_cast(
+ request->metadata()
+ .get(libcamera::controls::SensorTimestamp)
+ .value_or(0));
gpu_queue.push({out, type, sensorTimestamp});
}
@@ -123,9 +135,10 @@ void CameraRunner::start() {
std::chrono::duration elapsedMillis =
steady_clock::now() - begintime;
if (elapsedMillis > 0.9ms) {
- // gpuTimeAvgMs =
- // approxRollingAverage(gpuTimeAvgMs, elapsedMillis.count());
- // std::cout << "GLProcess: " << elapsedMillis.count() << std::endl;
+ gpuTimeAvgMs =
+ approxRollingAverage(gpuTimeAvgMs, elapsedMillis.count());
+ // std::cout << "GLProcess: " << elapsedMillis.count() <<
+ // std::endl;
}
{
@@ -148,13 +161,13 @@ void CameraRunner::start() {
mmaped.emplace(fd, static_cast(mmap_ptr));
}
- double copyTimeAvgMs = 0;
+ // double copyTimeAvgMs = 0;
double fpsTimeAvgMs = 0;
start_frame_grabber.count_down();
auto lastTime = steady_clock::now();
while (true) {
- // printf("Display thread!\n");
+ // std::printf("Display thread!\n");
auto data = gpu_queue.pop();
if (data.fd == -1) {
break;
@@ -174,7 +187,6 @@ void CameraRunner::start() {
auto input_ptr = mmaped.at(data.fd);
int bound = m_width * m_height;
-
{
struct dma_buf_sync dma_sync {};
dma_sync.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW;
@@ -194,7 +206,7 @@ void CameraRunner::start() {
processed_out_buf[i] = input_ptr[i * 4 + 3];
}
}
-
+
{
struct dma_buf_sync dma_sync {};
dma_sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW;
@@ -212,13 +224,13 @@ void CameraRunner::start() {
// approxRollingAverage(copyTimeAvgMs, elapsedMillis.count());
// std::cout << "Copy: " << copyTimeAvgMs << std::endl;
- // auto now = steady_clock::now();
- // std::chrono::duration elapsed =
- // (now - lastTime);
- // fpsTimeAvgMs = approxRollingAverage(fpsTimeAvgMs, elapsed.count());
- // printf("Delta %.2f FPS: %.2f\n", fpsTimeAvgMs,
- // 1000.0 / fpsTimeAvgMs);
- // lastTime = now;
+ auto now = steady_clock::now();
+ std::chrono::duration elapsed =
+ (now - lastTime);
+ fpsTimeAvgMs = approxRollingAverage(fpsTimeAvgMs, elapsed.count());
+ // std::printf("Delta %.2f FPS: %.2f\n", fpsTimeAvgMs, 1000.0 /
+ // fpsTimeAvgMs);
+ lastTime = now;
}
for (const auto &[fd, pointer] : mmaped) {
@@ -235,7 +247,7 @@ void CameraRunner::start() {
}
void CameraRunner::stop() {
- printf("stopping all\n");
+ std::printf("stopping all\n");
// stop the camera
{
std::lock_guard lock{camera_stop_mutex};
@@ -247,8 +259,8 @@ void CameraRunner::stop() {
threshold.join();
// push sentinel value to stop display thread
- gpu_queue.push({-1, ProcessType::None});
+ gpu_queue.push({-1, ProcessType::None, 0});
display.join();
- printf("stopped all\n");
+ std::printf("stopped all\n");
}
diff --git a/dma_buf_alloc.cpp b/src/dma_buf_alloc.cpp
similarity index 50%
rename from dma_buf_alloc.cpp
rename to src/dma_buf_alloc.cpp
index 11f2188..42ed671 100644
--- a/dma_buf_alloc.cpp
+++ b/src/dma_buf_alloc.cpp
@@ -1,13 +1,29 @@
-#include "dma_buf_alloc.h"
+/*
+ * 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 .
+ */
-#include
+#include "dma_buf_alloc.h"
#include
-#include
-
#include
#include
#include
+#include
+
+#include
DmaBufAlloc::DmaBufAlloc(const std::string &heap_name) {
int heap_fd = open(heap_name.c_str(), O_RDWR, 0);
@@ -19,7 +35,7 @@ DmaBufAlloc::DmaBufAlloc(const std::string &heap_name) {
DmaBufAlloc::~DmaBufAlloc() { close(m_heap_fd); }
-int DmaBufAlloc::alloc_buf_fd(std::size_t len) {
+int DmaBufAlloc::alloc_buf_fd(size_t len) {
struct dma_heap_allocation_data alloc = {};
alloc.len = len;
alloc.fd_flags = O_CLOEXEC | O_RDWR;
diff --git a/eglinfo.c b/src/eglinfo.c
similarity index 57%
rename from eglinfo.c
rename to src/eglinfo.c
index 2681c33..e3a4cd7 100644
--- a/eglinfo.c
+++ b/src/eglinfo.c
@@ -1,5 +1,22 @@
-#include
+/*
+ * 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 .
+ */
+
#include
+#include
#include
// adjustments to paths might be needed
@@ -7,17 +24,11 @@
#include
#include
-static EGLint const attribute_list[] = {
- EGL_RED_SIZE, 1,
- EGL_GREEN_SIZE, 1,
- EGL_BLUE_SIZE, 1,
- EGL_NONE
-};
+static EGLint const attribute_list[] = {EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1, EGL_NONE};
-static EGLint const context_attribute_list[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE
-};
+static EGLint const context_attribute_list[] = {EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE};
struct gl_limit {
GLint name;
@@ -25,28 +36,25 @@ struct gl_limit {
int num_args;
};
#define EXPAND(x) x, #x
-struct gl_limit limits[] = {
- {EXPAND(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS), 1},
- {EXPAND(GL_MAX_CUBE_MAP_TEXTURE_SIZE), 1},
- {EXPAND(GL_MAX_FRAGMENT_UNIFORM_VECTORS), 1},
- {EXPAND(GL_MAX_RENDERBUFFER_SIZE), 1},
- {EXPAND(GL_MAX_TEXTURE_IMAGE_UNITS), 1},
- {EXPAND(GL_MAX_TEXTURE_SIZE), 1},
- {EXPAND(GL_MAX_VARYING_VECTORS), 1},
- {EXPAND(GL_MAX_VERTEX_ATTRIBS), 1},
- {EXPAND(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS), 1},
- {EXPAND(GL_MAX_VERTEX_UNIFORM_VECTORS), 1},
- {EXPAND(GL_MAX_VIEWPORT_DIMS), 2},
- {0, NULL}
-};
+struct gl_limit limits[] = {{EXPAND(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS), 1},
+ {EXPAND(GL_MAX_CUBE_MAP_TEXTURE_SIZE), 1},
+ {EXPAND(GL_MAX_FRAGMENT_UNIFORM_VECTORS), 1},
+ {EXPAND(GL_MAX_RENDERBUFFER_SIZE), 1},
+ {EXPAND(GL_MAX_TEXTURE_IMAGE_UNITS), 1},
+ {EXPAND(GL_MAX_TEXTURE_SIZE), 1},
+ {EXPAND(GL_MAX_VARYING_VECTORS), 1},
+ {EXPAND(GL_MAX_VERTEX_ATTRIBS), 1},
+ {EXPAND(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS), 1},
+ {EXPAND(GL_MAX_VERTEX_UNIFORM_VECTORS), 1},
+ {EXPAND(GL_MAX_VIEWPORT_DIMS), 2},
+ {0, NULL}};
#include "headless_opengl.h"
-int main(int argc, char ** argv)
-{
+int main(int argc, char **argv) {
struct HeadlessData status = createHeadless();
-
+
EGLDisplay display = status.display;
// EGLConfig config;
EGLContext context = status.context;
@@ -97,8 +105,7 @@ int main(int argc, char ** argv)
printf(" %s = %d\n", limits[i].string, param[0]);
else
printf(" %s = %d, %d\n", limits[i].string, param[0], param[1]);
-
}
return EXIT_SUCCESS;
-}
\ No newline at end of file
+}
diff --git a/gl_hsv_thresholder.cpp b/src/gl_hsv_thresholder.cpp
similarity index 87%
rename from gl_hsv_thresholder.cpp
rename to src/gl_hsv_thresholder.cpp
index 7023095..2575f78 100644
--- a/gl_hsv_thresholder.cpp
+++ b/src/gl_hsv_thresholder.cpp
@@ -1,22 +1,42 @@
+/*
+ * 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 .
+ */
+
#include "gl_hsv_thresholder.h"
-#include "glerror.h"
-#include "gl_shader_source.h"
-#include
-#include
#include
#include
#include
+#include
+#include
+
+#include "gl_shader_source.h"
+#include "glerror.h"
+
#define GLERROR() glerror(__LINE__)
#define EGLERROR() eglerror(__LINE__)
GLuint make_shader(GLenum type, const char *source) {
auto shader = glCreateShader(type);
- void *ctx = eglGetCurrentContext();
- // printf("Shader idx: %i context ptr: %lu\n", (int) shader, (size_t)ctx);
+ // void *ctx = eglGetCurrentContext();
+ // std::printf("Shader idx: %i context ptr: %lu\n", (int) shader,
+ // (size_t)ctx);
if (!shader) {
throw std::runtime_error("failed to create shader");
@@ -37,6 +57,7 @@ GLuint make_shader(GLenum type, const char *source) {
glGetShaderInfoLog(shader, log_size, nullptr, out.data());
glDeleteShader(shader);
+ printf("Shader:\n%s\n", source);
throw std::runtime_error("failed to compile shader with error: " + out);
}
@@ -78,26 +99,30 @@ GlHsvThresholder::GlHsvThresholder(int width, int height)
m_status = createHeadless();
m_context = m_status.context;
m_display = m_status.display;
-
}
GlHsvThresholder::~GlHsvThresholder() {
- for (auto& program : m_programs)
+ for (auto &program : m_programs)
glDeleteProgram(program);
glDeleteBuffers(1, &m_quad_vbo);
- for (const auto [key, value]: m_framebuffers) {
+ for (const auto &[key, value] : m_framebuffers) {
glDeleteFramebuffers(1, &value);
}
destroyHeadless(m_status);
}
-static void on_gl_error(EGLenum error,const char *command,EGLint messageType,EGLLabelKHR threadLabel,EGLLabelKHR objectLabel,const char* message)
-{
-
- printf("Error111: %s\n", message);
-
-}
+// static void on_gl_error(EGLenum error,const char *command,EGLint
+// messageType,EGLLabelKHR threadLabel,EGLLabelKHR objectLabel,const char*
+// message)
+// {
+// (void) error;
+// (void) command;
+// (void) messageType;
+// (void) threadLabel;
+// (void) objectLabel;
+// std::printf("Error111: %s\n", message);
+// }
void GlHsvThresholder::start(const std::vector &output_buf_fds) {
static auto glEGLImageTargetTexture2DOES =
@@ -107,14 +132,14 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) {
(PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
static auto eglDestroyImageKHR =
(PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR");
- static auto glDebugMessageCallbackKHR =
- (PFNEGLDEBUGMESSAGECONTROLKHRPROC)eglGetProcAddress("glDebugMessageCallbackKHR");
if (!eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_context)) {
throw std::runtime_error("failed to bind egl context");
}
EGLERROR();
+ // static auto glDebugMessageCallbackKHR =
+ // (PFNEGLDEBUGMESSAGECONTROLKHRPROC)eglGetProcAddress("glDebugMessageCallbackKHR");
// glEnable(GL_DEBUG_OUTPUT_KHR);
// GLERROR();
// glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
@@ -122,12 +147,13 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) {
// glDebugMessageCallbackKHR(on_gl_error, nullptr);
// GLERROR();
- m_programs.reserve(5);
+ m_programs.reserve((int)ProcessType::NUM_PROCESS_TYPES);
m_programs[0] = make_program(VERTEX_SOURCE, NONE_FRAGMENT_SOURCE);
m_programs[1] = make_program(VERTEX_SOURCE, HSV_FRAGMENT_SOURCE);
m_programs[2] = make_program(VERTEX_SOURCE, GRAY_FRAGMENT_SOURCE);
m_programs[3] = make_program(VERTEX_SOURCE, TILING_FRAGMENT_SOURCE);
m_programs[4] = make_program(VERTEX_SOURCE, THRESHOLDING_FRAGMENT_SOURCE);
+ m_programs[5] = make_program(VERTEX_SOURCE, GRAY_PASSTHROUGH_FRAGMENT_SOURCE);
for (auto fd : output_buf_fds) {
GLuint out_tex;
@@ -165,7 +191,7 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) {
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
GLERROR();
- eglDestroyImageKHR(m_display, image);
+ eglDestroyImageKHR(m_display, image);
GLERROR();
GLuint framebuffer;
@@ -216,7 +242,8 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) {
GLERROR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
GLERROR();
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, nullptr);
GLERROR();
m_grayscale_texture = grayscale_texture;
@@ -228,10 +255,12 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) {
GLERROR();
glBindFramebuffer(GL_FRAMEBUFFER, grayscale_buffer);
GLERROR();
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_grayscale_texture, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, m_grayscale_texture, 0);
GLERROR();
- if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
+ GL_FRAMEBUFFER_COMPLETE) {
throw std::runtime_error("failed to complete grayscale_buffer");
}
@@ -252,7 +281,8 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) {
GLERROR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
GLERROR();
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width / 4, m_height / 4, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width / 4, m_height / 4, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, nullptr);
GLERROR();
m_min_max_texture = min_max_texture;
@@ -264,10 +294,12 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) {
GLERROR();
glBindFramebuffer(GL_FRAMEBUFFER, min_max_framebuffer);
GLERROR();
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_min_max_texture, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, m_min_max_texture, 0);
GLERROR();
- if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
+ GL_FRAMEBUFFER_COMPLETE) {
throw std::runtime_error("failed to complete grayscale_buffer");
}
@@ -276,7 +308,8 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) {
}
void GlHsvThresholder::release() {
- if (!eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
+ if (!eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT)) {
throw std::runtime_error("failed to bind egl context");
}
}
@@ -305,7 +338,7 @@ int GlHsvThresholder::testFrame(
// std::cout << "yes framebuffer" << std::endl;
m_renderable.pop();
} else {
- std::cout << "no framebuffer, skipping" << std::endl;
+ // std::cout << "no framebuffer, skipping" << std::endl;
return 0;
}
}
@@ -350,7 +383,6 @@ int GlHsvThresholder::testFrame(
std::to_string(yuv_plane_data[0].fd));
}
-
GLuint texture;
glGenTextures(1, &texture);
GLERROR();
@@ -374,6 +406,8 @@ int GlHsvThresholder::testFrame(
initial_program = m_programs[1];
} else if (type == ProcessType::Gray || type == ProcessType::Adaptive) {
initial_program = m_programs[2];
+ } else if (type == ProcessType::Gray_passthrough) {
+ initial_program = m_programs[5];
}
glUseProgram(initial_program);
diff --git a/headless_opengl.cpp b/src/headless_opengl.cpp
similarity index 82%
rename from headless_opengl.cpp
rename to src/headless_opengl.cpp
index 64ac6f6..e5965e8 100644
--- a/headless_opengl.cpp
+++ b/src/headless_opengl.cpp
@@ -1,5 +1,21 @@
+/*
+ * 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 .
+ */
+
#include "headless_opengl.h"
-#include "glerror.h"
#include
#include
@@ -9,6 +25,8 @@
#include
+#include "glerror.h"
+
// The following code related to DRM/GBM was adapted from the following sources:
// https://github.com/eyelash/tutorials/blob/master/drm-gbm.c
// and
@@ -40,10 +58,8 @@ static const EGLint configAttribs[] = {EGL_RED_SIZE,
EGL_NONE};
static const EGLint contextAttribs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
- EGL_NONE
-};
+ EGL_CONTEXT_CLIENT_VERSION, 2, EGL_CONTEXT_FLAGS_KHR,
+ EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL_NONE};
HeadlessData createHeadless() {
std::vector paths = {"/dev/dri/card1", "/dev/dri/card0"};
@@ -83,7 +99,7 @@ HeadlessData createHeadless() {
eglBindAPI(EGL_OPENGL_ES_API);
EGLERROR();
- printf("Initialized EGL version: %d.%d\n", major, minor);
+ std::printf("Initialized EGL version: %d.%d\n", major, minor);
EGLint count;
EGLint numConfigs;
@@ -135,4 +151,4 @@ void destroyHeadless(HeadlessData status) {
eglTerminate(status.display);
gbm_device_destroy(status.gbmDevice);
close(status.gbmFd);
-}
\ No newline at end of file
+}
diff --git a/src/libcamera_jni.cpp b/src/libcamera_jni.cpp
new file mode 100644
index 0000000..ef21f57
--- /dev/null
+++ b/src/libcamera_jni.cpp
@@ -0,0 +1,477 @@
+/*
+ * 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 .
+ */
+
+/*
+ * Copyright (C) 2022 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 .
+ */
+
+#include "libcamera_jni.hpp" // Generated
+
+#include
+
+#include
+
+#include "camera_manager.h"
+#include "camera_model.h"
+#include "camera_runner.h"
+#include "headless_opengl.h"
+
+extern "C" {
+
+#include
+
+// We use jlongs like pointers, so they better be large enough
+static_assert(sizeof(void *) <= sizeof(jlong));
+
+JNIEXPORT jboolean
+Java_org_photonvision_raspi_LibCameraJNI_isLibraryWorking(JNIEnv *, jclass) {
+ // todo
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: getCameraNames
+ * Signature: ()[Ljava/lang/Object;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_getCameraNames
+ (JNIEnv *env, jclass)
+{
+ std::vector> cameras = GetAllCameraIDs();
+
+ // Yeet all USB cameras (I hope)
+ auto rem = std::remove_if(cameras.begin(), cameras.end(), [](auto &cam) {
+ return cam->id().find("/usb") != std::string::npos;
+ });
+ cameras.erase(rem, cameras.end());
+
+ jobjectArray ret;
+
+ // https://stackoverflow.com/a/21768693
+ ret = (jobjectArray)env->NewObjectArray(
+ cameras.size(), env->FindClass("java/lang/String"), NULL);
+ for (unsigned int i = 0; i < cameras.size(); i++)
+ env->SetObjectArrayElement(ret, i,
+ env->NewStringUTF(cameras[i]->id().c_str()));
+
+ return (ret);
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: createCamera
+ * Signature: (Ljava/lang/String;III)J
+ */
+JNIEXPORT jlong JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_createCamera
+ (JNIEnv *env, jclass, jstring name, jint width, jint height, jint rotation)
+{
+ std::vector> cameras = GetAllCameraIDs();
+
+ const char *c_name = env->GetStringUTFChars(name, 0);
+
+ jlong ret = 0;
+
+ // Find our camera by name
+ for (auto &c : cameras) {
+ if (std::strcmp(c->id().c_str(), c_name) == 0) {
+ ret = reinterpret_cast(
+ new CameraRunner(width, height, rotation, c));
+ break;
+ }
+ }
+
+ env->ReleaseStringUTFChars(name, c_name);
+
+ return ret;
+}
+
+JNIEXPORT jint Java_org_photonvision_raspi_LibCameraJNI_getSensorModelRaw(
+ JNIEnv *env, jclass, jstring name) {
+
+ std::vector> cameras = GetAllCameraIDs();
+
+ const char *c_name = env->GetStringUTFChars(name, 0);
+
+ // printf("Got in name: %s\n", c_name);
+
+ jint model_enum = Unknown;
+ for (auto &c : cameras) {
+ // Find the camera we want to be talking about
+ if (std::strcmp(c->id().c_str(), c_name) == 0) {
+
+ // Determine model
+ auto &cprp = c->properties();
+ auto model = cprp.get(libcamera::properties::Model);
+ // printf("Checking camera at path %s w/model %s\n", c->id().c_str(), model.value().c_str());
+ if (model) {
+ model_enum = reinterpret_cast(stringToModel(model.value()));
+ }
+
+ break;
+ }
+ }
+
+ // printf("Got model %i\n", model_enum);
+
+
+ env->ReleaseStringUTFChars(name, c_name);
+
+ return model_enum;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: startCamera
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_startCamera
+ (JNIEnv *, jclass, jlong runner_)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+
+ runner->start();
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: stopCamera
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_stopCamera
+ (JNIEnv *, jclass, jlong runner_)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return false;
+ }
+
+ runner->stop();
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: destroyCamera
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_destroyCamera
+ (JNIEnv *, jclass, jlong runner_)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return false;
+ }
+
+ delete runner;
+ runner = nullptr;
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setThresholds
+ * Signature: (JDDDDDDZ)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setThresholds
+ (JNIEnv *, jclass, jlong runner_, jdouble hl, jdouble sl, jdouble vl,
+ jdouble hu, jdouble su, jdouble vu, jboolean hueInverted)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return false;
+ }
+
+ // std::printf("Setting HSV to %f-%f %f-%f %f-%f\n", hl, hu, sl, su, vl,
+ // vu);
+
+ // TODO hue inversion
+ runner->thresholder().setHsvThresholds(hl, sl, vl, hu, su, vu, hueInverted);
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setExposure
+ * Signature: (JI)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setExposure
+ (JNIEnv *, jclass, jlong runner_, jint exposure)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return false;
+ }
+
+ runner->cameraGrabber().cameraSettings().exposureTimeUs = exposure;
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setAutoExposure
+ * Signature: (JZ)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setAutoExposure
+ (JNIEnv *, jclass, jlong runner_, jboolean doAutoExposure)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return false;
+ }
+
+ runner->cameraGrabber().cameraSettings().doAutoExposure = doAutoExposure;
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setBrightness
+ * Signature: (JD)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setBrightness
+ (JNIEnv *, jclass, jlong runner_, jdouble brightness)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return false;
+ }
+
+ runner->cameraGrabber().cameraSettings().brightness = brightness;
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setAwbGain
+ * Signature: (JDD)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setAwbGain
+ (JNIEnv *, jclass, jlong runner_, jdouble red, jdouble blue)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return false;
+ }
+
+ runner->cameraGrabber().cameraSettings().awbRedGain = red;
+ runner->cameraGrabber().cameraSettings().awbBlueGain = blue;
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setAnalogGain
+ * Signature: (JD)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setAnalogGain
+ (JNIEnv *, jclass, jlong runner_, jdouble analog)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return false;
+ }
+
+ runner->cameraGrabber().cameraSettings().analogGain = analog;
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setFramesToCopy
+ * Signature: (JZZ)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setFramesToCopy
+ (JNIEnv *, jclass, jlong runner_, jboolean copyIn, jboolean copyOut)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return false;
+ }
+
+ runner->setCopyOptions(copyIn, copyOut);
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: getLibcameraTimestamp
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp
+ (JNIEnv *, jclass)
+{
+ timespec ts;
+ clock_gettime(CLOCK_BOOTTIME, &ts);
+ uint64_t now_nsec =
+ static_cast(ts.tv_sec) * 1000000000ULL + ts.tv_nsec;
+ return (jlong)now_nsec;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: awaitNewFrame
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_awaitNewFrame
+ (JNIEnv *, jclass, jlong runner_)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return 0;
+ }
+
+ MatPair *pair = new MatPair();
+ *pair = runner->outgoing.take();
+ return reinterpret_cast(pair);
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: takeColorFrame
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_takeColorFrame
+ (JNIEnv *, jclass, jlong pair_)
+{
+ MatPair *pair = reinterpret_cast(pair_);
+ if (!pair) {
+ return 0;
+ }
+
+ return reinterpret_cast(new cv::Mat(std::move(pair->color)));
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: takeProcessedFrame
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_takeProcessedFrame
+ (JNIEnv *, jclass, jlong pair_)
+{
+ MatPair *pair = reinterpret_cast(pair_);
+ if (!pair) {
+ return 0;
+ }
+
+ return reinterpret_cast(new cv::Mat(std::move(pair->processed)));
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: getFrameCaptureTime
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime
+ (JNIEnv *, jclass, jlong pair_)
+{
+ MatPair *pair = reinterpret_cast(pair_);
+ if (!pair) {
+ // NULL
+ return 0;
+ }
+
+ return pair->captureTimestamp;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: releasePair
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_releasePair
+ (JNIEnv *, jclass, jlong pair_)
+{
+ MatPair *pair = reinterpret_cast(pair_);
+ if (!pair) {
+ // NULL
+ return false;
+ }
+
+ delete pair;
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: setGpuProcessType
+ * Signature: (JI)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType
+ (JNIEnv *, jclass, jlong runner_, jint idx)
+{
+ CameraRunner *runner = reinterpret_cast(runner_);
+ if (!runner) {
+ return false;
+ }
+
+ runner->requestShaderIdx(idx);
+
+ return true;
+}
+
+/*
+ * Class: org_photonvision_raspi_LibCameraJNI
+ * Method: getGpuProcessType
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_photonvision_raspi_LibCameraJNI_getGpuProcessType
+ (JNIEnv *, jclass, jlong pair_)
+{
+ MatPair *pair = reinterpret_cast(pair_);
+ if (!pair) {
+ // NULL
+ return 0;
+ }
+ return pair->frameProcessingType;
+}
+
+} // extern "C"
diff --git a/libcamera_opengl_utility.cpp b/src/libcamera_opengl_utility.cpp
similarity index 56%
rename from libcamera_opengl_utility.cpp
rename to src/libcamera_opengl_utility.cpp
index d562d12..4dc3443 100644
--- a/libcamera_opengl_utility.cpp
+++ b/src/libcamera_opengl_utility.cpp
@@ -1,9 +1,26 @@
-#include "libcamera_opengl_utility.h"
+/*
+ * 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 .
+ */
-#include
+#include "libcamera_opengl_utility.h"
#include
+#include
+
EGLint rangeFromColorspace(const libcamera::ColorSpace &colorSpace) {
if (colorSpace.range == libcamera::ColorSpace::Range::Full) {
return EGL_YUV_FULL_RANGE_EXT;