From 119769d5728ad5ba7ec66b50730e26ce06882303 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 15 Jun 2022 10:14:22 -0600 Subject: [PATCH 01/49] chore: Style and lint checking --- .clang-format | 2 + .editorconfig | 9 +++ .gitignore | 1 + .rustfmt.toml | 1 + build.rs | 23 ++++---- libcamera-bridge/core.cpp | 48 ++++++++-------- libcamera-bridge/core.hpp | 22 +++---- makefile | 6 ++ rustfmt.toml | 0 src/bridge.rs | 118 ++++++++++++++++++++------------------ src/lib.rs | 37 ++++++------ 11 files changed, 148 insertions(+), 119 deletions(-) create mode 100644 .clang-format create mode 100644 .editorconfig create mode 100644 .rustfmt.toml create mode 100644 makefile create mode 100644 rustfmt.toml diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f4edc74 --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +IndentWidth: 2 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4960585 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore index d0b9098..0115f2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .idea .vscode +*.bak~ /target Cargo.lock diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..b196eaa --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +tab_spaces = 2 diff --git a/build.rs b/build.rs index 7697fd7..3907e15 100644 --- a/build.rs +++ b/build.rs @@ -1,15 +1,16 @@ fn main() { - cxx_build::bridge("src/bridge.rs") - .file("libcamera-bridge/core.cpp") - .flag_if_supported("-std=c++17") - .include("/usr/local/include/libcamera") - .compile("libcamera-bridge"); + cxx_build::bridge("src/bridge.rs") + .file("libcamera-bridge/core.cpp") + .flag_if_supported("-std=c++17") + .include("/usr/local/include/libcamera") + .include("/usr/include/libcamera") + .compile("libcamera-bridge"); - println!("cargo:rerun-if-changed=src/bridge.rs"); - println!("cargo:rerun-if-changed=libcamera-bridge/core.cpp"); - println!("cargo:rerun-if-changed=libcamera-bridge/core.hpp"); + println!("cargo:rerun-if-changed=src/bridge.rs"); + println!("cargo:rerun-if-changed=libcamera-bridge/core.cpp"); + println!("cargo:rerun-if-changed=libcamera-bridge/core.hpp"); - // link libcamera - println!("cargo:rustc-link-lib=dylib=camera"); - println!("cargo:rustc-link-lib=dylib=camera-base"); + // link libcamera + println!("cargo:rustc-link-lib=dylib=camera"); + println!("cargo:rustc-link-lib=dylib=camera-base"); } diff --git a/libcamera-bridge/core.cpp b/libcamera-bridge/core.cpp index ebdf127..6628ad4 100644 --- a/libcamera-bridge/core.cpp +++ b/libcamera-bridge/core.cpp @@ -1,48 +1,48 @@ -#include #include "./core.hpp" #include "libcamera-rs/src/bridge.rs.h" +#include void CameraManager::start() { - int res = libcamera::CameraManager::start(); + int res = libcamera::CameraManager::start(); - if (res < 0) { - throw res; - } + if (res < 0) { + throw res; + } - return; + return; } rust::String CameraManager::version() { - return libcamera::CameraManager::version(); + return libcamera::CameraManager::version(); } rust::Vec CameraManager::cameras() const { - auto cameras = libcamera::CameraManager::cameras(); - rust::Vec rust_cameras; + auto cameras = libcamera::CameraManager::cameras(); + rust::Vec rust_cameras; - for (auto camera : cameras) { - rust_cameras.push_back(BridgeCamera { .inner = camera }); - } + for (auto camera : cameras) { + rust_cameras.push_back(BridgeCamera{.inner = camera}); + } - return rust_cameras; + return rust_cameras; } -std::unique_ptr -make_camera_manager() { +std::unique_ptr make_camera_manager() { return std::make_unique(); } -libcamera::Camera& -get_mut_camera(std::shared_ptr& cam) { - return *cam.get(); +libcamera::Camera &get_mut_camera(std::shared_ptr &cam) { + return *cam.get(); } -std::unique_ptr generate_camera_configuration(libcamera::Camera& cam, const rust::Vec& roles) { - std::vector cpp_roles; +std::unique_ptr +generate_camera_configuration(libcamera::Camera &cam, + const rust::Vec &roles) { + std::vector cpp_roles; - for (auto role : roles) { - cpp_roles.push_back(role); - } + for (auto role : roles) { + cpp_roles.push_back(role); + } - return cam.generateConfiguration(cpp_roles); + return cam.generateConfiguration(cpp_roles); } diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 9e5aa94..1162a31 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -1,23 +1,23 @@ #pragma once -#include "libcamera/camera_manager.h" #include "libcamera/camera.h" +#include "libcamera/camera_manager.h" #include "rust/cxx.h" using CameraConfigurationStatus = libcamera::CameraConfiguration::Status; struct BridgeCamera; -class CameraManager: public libcamera::CameraManager { - public: - void start(); - rust::String version(); - rust::Vec cameras() const; +class CameraManager : public libcamera::CameraManager { +public: + void start(); + rust::String version(); + rust::Vec cameras() const; }; -std::unique_ptr -make_camera_manager(); +std::unique_ptr make_camera_manager(); -libcamera::Camera& -get_mut_camera(std::shared_ptr& cam); +libcamera::Camera &get_mut_camera(std::shared_ptr &cam); -std::unique_ptr generate_camera_configuration(libcamera::Camera& cam, const rust::Vec& roles); +std::unique_ptr +generate_camera_configuration(libcamera::Camera &cam, + const rust::Vec &roles); diff --git a/makefile b/makefile new file mode 100644 index 0000000..f9c830d --- /dev/null +++ b/makefile @@ -0,0 +1,6 @@ +lint: + cargo clippy + +fmt: + clang-format -style=file -i libcamera-bridge/* + cargo fmt diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..e69de29 diff --git a/src/bridge.rs b/src/bridge.rs index b09daa2..1bf2a47 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -1,85 +1,91 @@ -use std::pin::Pin; use cxx::{SharedPtr, UniquePtr}; +use std::pin::Pin; #[cxx::bridge] pub mod ffi { - struct BridgeCamera { - inner: SharedPtr, - } - - #[namespace = "libcamera"] - #[repr(i32)] - #[derive(Debug)] - enum StreamRole { - Raw, - StillCapture, - VideoRecording, - Viewfinder, - } + struct BridgeCamera { + inner: SharedPtr, + } + + #[namespace = "libcamera"] + #[repr(i32)] + #[derive(Debug)] + enum StreamRole { + Raw, + StillCapture, + VideoRecording, + Viewfinder, + } + + #[namespace = "libcamera"] + #[repr(i32)] + #[derive(Debug)] + enum CameraConfigurationStatus { + Valid, + Adjusted, + Invalid, + } + + unsafe extern "C++" { + include!("libcamera/stream.h"); #[namespace = "libcamera"] - #[repr(i32)] - #[derive(Debug)] - enum CameraConfigurationStatus { - Valid, - Adjusted, - Invalid, - } + type StreamRole; - unsafe extern "C++" { - include!("libcamera/stream.h"); + include!("libcamera/camera.h"); - #[namespace = "libcamera"] - type StreamRole; - - include!("libcamera/camera.h"); - - #[namespace = "libcamera"] - type CameraConfiguration; + #[namespace = "libcamera"] + type CameraConfiguration; - pub fn validate(self: Pin<&mut CameraConfiguration>) -> CameraConfigurationStatus; + pub fn validate(self: Pin<&mut CameraConfiguration>) -> CameraConfigurationStatus; - #[namespace = "libcamera"] - type Camera; + #[namespace = "libcamera"] + type Camera; - pub fn id(self: &Camera) -> &CxxString; - pub fn acquire(self: Pin<&mut Camera>) -> i32; + pub fn id(self: &Camera) -> &CxxString; + pub fn acquire(self: Pin<&mut Camera>) -> i32; - pub(crate) fn generate_camera_configuration(cam: Pin<&mut Camera>, roles: &Vec) -> UniquePtr; + pub(crate) fn generate_camera_configuration( + cam: Pin<&mut Camera>, + roles: &Vec, + ) -> UniquePtr; - include!("libcamera-rs/libcamera-bridge/core.hpp"); + include!("libcamera-rs/libcamera-bridge/core.hpp"); - type CameraConfigurationStatus; + type CameraConfigurationStatus; - pub fn get_mut_camera(cam: &mut SharedPtr) -> Pin<&mut Camera>; + pub fn get_mut_camera(cam: &mut SharedPtr) -> Pin<&mut Camera>; - type CameraManager; + type CameraManager; - pub fn make_camera_manager() -> UniquePtr; - pub fn start(self: Pin<&mut CameraManager>) -> Result<()>; - pub fn stop(self: Pin<&mut CameraManager>); - pub fn version(self: Pin<&mut CameraManager>) -> String; - pub fn cameras(self: &CameraManager) -> Vec; - pub fn get(self: Pin<&mut CameraManager>, id: &CxxString) -> SharedPtr; - } + pub fn make_camera_manager() -> UniquePtr; + pub fn start(self: Pin<&mut CameraManager>) -> Result<()>; + pub fn stop(self: Pin<&mut CameraManager>); + pub fn version(self: Pin<&mut CameraManager>) -> String; + pub fn cameras(self: &CameraManager) -> Vec; + pub fn get(self: Pin<&mut CameraManager>, id: &CxxString) -> SharedPtr; + } } impl ffi::Camera { - pub fn generate_configuration(self: Pin<&mut Self>, roles: &Vec) -> UniquePtr { - ffi::generate_camera_configuration(self, roles) - } + pub fn generate_configuration( + self: Pin<&mut Self>, + roles: &Vec, + ) -> UniquePtr { + ffi::generate_camera_configuration(self, roles) + } } pub trait MutFromSharedPtr { - type Target; + type Target; - fn pin_mut(&mut self) -> Pin<&mut Self::Target>; + fn pin_mut(&mut self) -> Pin<&mut Self::Target>; } impl MutFromSharedPtr for SharedPtr { - type Target = ffi::Camera; + type Target = ffi::Camera; - fn pin_mut(&mut self) -> Pin<&mut Self::Target> { - ffi::get_mut_camera(self) - } + fn pin_mut(&mut self) -> Pin<&mut Self::Target> { + ffi::get_mut_camera(self) + } } diff --git a/src/lib.rs b/src/lib.rs index de65f68..0c0a16f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,27 +4,30 @@ pub use bridge::ffi; #[cfg(test)] mod tests { - use crate::ffi; - use crate::bridge::MutFromSharedPtr; + use crate::bridge::MutFromSharedPtr; + use crate::ffi; - #[test] - fn it_works() { - let mut manager = ffi::make_camera_manager(); - manager.pin_mut().version(); - } + #[test] + fn it_works() { + let mut manager = ffi::make_camera_manager(); + manager.pin_mut().version(); + } - #[test] - fn generate_configuration() { - let mut manager = ffi::make_camera_manager(); - manager.pin_mut().start().unwrap(); + #[test] + fn generate_configuration() { + let mut manager = ffi::make_camera_manager(); + manager.pin_mut().start().unwrap(); - let mut cameras = manager.pin_mut().cameras(); - let camera = &mut cameras[0]; + let mut cameras = manager.pin_mut().cameras(); + let camera = &mut cameras[0]; - let roles = vec![ffi::StreamRole::StillCapture]; + let roles = vec![ffi::StreamRole::StillCapture]; - let mut config = camera.inner.pin_mut().generate_configuration(&roles); + let mut config = camera.inner.pin_mut().generate_configuration(&roles); - assert_eq!(config.pin_mut().validate(), ffi::CameraConfigurationStatus::Valid); - } + assert_eq!( + config.pin_mut().validate(), + ffi::CameraConfigurationStatus::Valid + ); + } } From 2625e727753d6da94e19a8ec86690f880fc6e267 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 15 Jun 2022 14:38:08 -0600 Subject: [PATCH 02/49] More bindings --- .gitmodules | 3 ++ build.rs | 2 +- libcamera | 1 + libcamera-bridge/core.cpp | 46 +++++++++++++++++++++--- libcamera-bridge/core.hpp | 15 ++++++++ makefile | 10 ++++-- src/bridge.rs | 76 +++++++++++++++++++++++++-------------- src/bridge/test.rs | 56 +++++++++++++++++++++++++++++ src/lib.rs | 30 ---------------- 9 files changed, 176 insertions(+), 63 deletions(-) create mode 100644 .gitmodules create mode 160000 libcamera create mode 100644 src/bridge/test.rs diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..74c1c37 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libcamera"] + path = libcamera + url = https://git.libcamera.org/libcamera/libcamera.git diff --git a/build.rs b/build.rs index 3907e15..bd38788 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,7 @@ fn main() { .file("libcamera-bridge/core.cpp") .flag_if_supported("-std=c++17") .include("/usr/local/include/libcamera") - .include("/usr/include/libcamera") + .include("libcamera/build/include/libcamera") .compile("libcamera-bridge"); println!("cargo:rerun-if-changed=src/bridge.rs"); diff --git a/libcamera b/libcamera new file mode 160000 index 0000000..1735d17 --- /dev/null +++ b/libcamera @@ -0,0 +1 @@ +Subproject commit 1735d176bc6139ae76dae0c84ab15bda5884764d diff --git a/libcamera-bridge/core.cpp b/libcamera-bridge/core.cpp index 6628ad4..ad0eeba 100644 --- a/libcamera-bridge/core.cpp +++ b/libcamera-bridge/core.cpp @@ -1,7 +1,13 @@ #include "./core.hpp" #include "libcamera-rs/src/bridge.rs.h" +#include "libcamera/formats.h" +#include "libcamera/geometry.h" #include +std::unique_ptr make_camera_manager() { + return std::make_unique(); +} + void CameraManager::start() { int res = libcamera::CameraManager::start(); @@ -27,10 +33,6 @@ rust::Vec CameraManager::cameras() const { return rust_cameras; } -std::unique_ptr make_camera_manager() { - return std::make_unique(); -} - libcamera::Camera &get_mut_camera(std::shared_ptr &cam) { return *cam.get(); } @@ -46,3 +48,39 @@ generate_camera_configuration(libcamera::Camera &cam, return cam.generateConfiguration(cpp_roles); } + +void configure_camera(libcamera::Camera &cam, + libcamera::CameraConfiguration &conf) { + int res = cam.configure(&conf); + + if (res < 0) { + throw res; + } + + return; +} + +const libcamera::PixelFormat & +get_default_pixel_format(DefaultPixelFormat format) { + switch (format) { + case DefaultPixelFormat::Rgb888: + return libcamera::formats::RGB888; + default: + return libcamera::formats::RGB888; + } +} + +void set_stream_pixel_format(libcamera::StreamConfiguration &conf, + const libcamera::PixelFormat &format) { + conf.pixelFormat = format; +} + +void set_stream_size(libcamera::StreamConfiguration &conf, unsigned int width, + unsigned int height) { + conf.size = libcamera::Size(width, height); +} + +void set_stream_buffer_count(libcamera::StreamConfiguration &conf, + unsigned int buffers) { + conf.bufferCount = buffers; +} diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 1162a31..ee55d6d 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -1,12 +1,15 @@ #pragma once #include "libcamera/camera.h" #include "libcamera/camera_manager.h" +#include "libcamera/stream.h" #include "rust/cxx.h" using CameraConfigurationStatus = libcamera::CameraConfiguration::Status; struct BridgeCamera; +enum class DefaultPixelFormat; + class CameraManager : public libcamera::CameraManager { public: void start(); @@ -21,3 +24,15 @@ libcamera::Camera &get_mut_camera(std::shared_ptr &cam); std::unique_ptr generate_camera_configuration(libcamera::Camera &cam, const rust::Vec &roles); +void configure_camera(libcamera::Camera &cam, + libcamera::CameraConfiguration &conf); + +const libcamera::PixelFormat & +get_default_pixel_format(DefaultPixelFormat format); + +void set_stream_pixel_format(libcamera::StreamConfiguration &conf, + const libcamera::PixelFormat &format); +void set_stream_size(libcamera::StreamConfiguration &conf, unsigned int width, + unsigned int height); +void set_stream_buffer_count(libcamera::StreamConfiguration &conf, + unsigned int buffers); diff --git a/makefile b/makefile index f9c830d..31c40d8 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,12 @@ -lint: +c: check +chk: check +lint: check +check: cargo clippy + cargo test -- --test-threads=1 -fmt: +f: format +fmt: format +format: clang-format -style=file -i libcamera-bridge/* cargo fmt diff --git a/src/bridge.rs b/src/bridge.rs index 1bf2a47..d73aa59 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -1,6 +1,9 @@ -use cxx::{SharedPtr, UniquePtr}; +use cxx::SharedPtr; use std::pin::Pin; +#[cfg(test)] +mod test; + #[cxx::bridge] pub mod ffi { struct BridgeCamera { @@ -26,53 +29,74 @@ pub mod ffi { Invalid, } + #[repr(i32)] + #[derive(Debug)] + enum DefaultPixelFormat { + Rgb888, + Bgr888, + Yuv420, + Mjpeg, + } + unsafe extern "C++" { include!("libcamera/stream.h"); - - #[namespace = "libcamera"] - type StreamRole; - include!("libcamera/camera.h"); + include!("libcamera-rs/libcamera-bridge/core.hpp"); - #[namespace = "libcamera"] - type CameraConfiguration; + // Camera Manager + type CameraManager; - pub fn validate(self: Pin<&mut CameraConfiguration>) -> CameraConfigurationStatus; + pub fn make_camera_manager() -> UniquePtr; + pub fn start(self: Pin<&mut CameraManager>) -> Result<()>; + pub fn stop(self: Pin<&mut CameraManager>); + pub fn version(self: Pin<&mut CameraManager>) -> String; + pub fn cameras(self: &CameraManager) -> Vec; + pub fn get(self: Pin<&mut CameraManager>, id: &CxxString) -> SharedPtr; + // Camera #[namespace = "libcamera"] type Camera; + pub fn get_mut_camera(cam: &mut SharedPtr) -> Pin<&mut Camera>; + pub fn id(self: &Camera) -> &CxxString; pub fn acquire(self: Pin<&mut Camera>) -> i32; + pub fn release(self: Pin<&mut Camera>) -> i32; + pub fn stop(self: Pin<&mut Camera>) -> i32; - pub(crate) fn generate_camera_configuration( + pub fn generate_camera_configuration( cam: Pin<&mut Camera>, roles: &Vec, ) -> UniquePtr; - include!("libcamera-rs/libcamera-bridge/core.hpp"); + pub fn configure_camera(cam: Pin<&mut Camera>, config: Pin<&mut CameraConfiguration>); + + // Camera Configuration + #[namespace = "libcamera"] + type CameraConfiguration; + + pub fn at(self: Pin<&mut CameraConfiguration>, index: u32) -> Pin<&mut StreamConfiguration>; + pub fn validate(self: Pin<&mut CameraConfiguration>) -> CameraConfigurationStatus; type CameraConfigurationStatus; - pub fn get_mut_camera(cam: &mut SharedPtr) -> Pin<&mut Camera>; + #[namespace = "libcamera"] + type StreamConfiguration; - type CameraManager; + pub fn set_stream_pixel_format( + stream: Pin<&mut StreamConfiguration>, + format: Pin<&PixelFormat>, + ); + pub fn set_stream_size(stream: Pin<&mut StreamConfiguration>, width: u32, height: u32); + pub fn set_stream_buffer_count(stream: Pin<&mut StreamConfiguration>, buffer_count: u32); - pub fn make_camera_manager() -> UniquePtr; - pub fn start(self: Pin<&mut CameraManager>) -> Result<()>; - pub fn stop(self: Pin<&mut CameraManager>); - pub fn version(self: Pin<&mut CameraManager>) -> String; - pub fn cameras(self: &CameraManager) -> Vec; - pub fn get(self: Pin<&mut CameraManager>, id: &CxxString) -> SharedPtr; - } -} + #[namespace = "libcamera"] + type StreamRole; + + #[namespace = "libcamera"] + type PixelFormat; -impl ffi::Camera { - pub fn generate_configuration( - self: Pin<&mut Self>, - roles: &Vec, - ) -> UniquePtr { - ffi::generate_camera_configuration(self, roles) + pub fn get_default_pixel_format(format: DefaultPixelFormat) -> Pin<&'static PixelFormat>; } } diff --git a/src/bridge/test.rs b/src/bridge/test.rs new file mode 100644 index 0000000..1303c58 --- /dev/null +++ b/src/bridge/test.rs @@ -0,0 +1,56 @@ +use crate::bridge::MutFromSharedPtr; +use crate::ffi; + +#[test] +fn it_works() { + let mut manager = ffi::make_camera_manager(); + manager.pin_mut().version(); +} + +#[test] +fn generate_configuration() { + let mut manager = ffi::make_camera_manager(); + manager.pin_mut().start().unwrap(); + + let mut cameras = manager.pin_mut().cameras(); + let camera = &mut cameras[0]; + + let roles = vec![ffi::StreamRole::StillCapture]; + + let mut config = ffi::generate_camera_configuration(camera.inner.pin_mut(), &roles); + + assert_eq!( + config.pin_mut().validate(), + ffi::CameraConfigurationStatus::Valid + ); +} + +#[test] +fn init_camera() { + let mut manager = ffi::make_camera_manager(); + manager.pin_mut().start().unwrap(); + + let mut cameras = manager.pin_mut().cameras(); + let camera = &mut cameras[0]; + + camera.inner.pin_mut().acquire(); + + let roles = vec![ffi::StreamRole::StillCapture]; + let mut config = ffi::generate_camera_configuration(camera.inner.pin_mut(), &roles); + + ffi::set_stream_pixel_format( + config.pin_mut().at(0), + ffi::get_default_pixel_format(ffi::DefaultPixelFormat::Rgb888), + ); + ffi::set_stream_size(config.pin_mut().at(0), 640, 480); + ffi::set_stream_buffer_count(config.pin_mut().at(0), 4); + + assert_ne!( + config.pin_mut().validate(), + ffi::CameraConfigurationStatus::Invalid + ); + + ffi::configure_camera(camera.inner.pin_mut(), config.pin_mut()); + + camera.inner.pin_mut().release(); +} diff --git a/src/lib.rs b/src/lib.rs index 0c0a16f..3ce1880 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,33 +1,3 @@ mod bridge; pub use bridge::ffi; - -#[cfg(test)] -mod tests { - use crate::bridge::MutFromSharedPtr; - use crate::ffi; - - #[test] - fn it_works() { - let mut manager = ffi::make_camera_manager(); - manager.pin_mut().version(); - } - - #[test] - fn generate_configuration() { - let mut manager = ffi::make_camera_manager(); - manager.pin_mut().start().unwrap(); - - let mut cameras = manager.pin_mut().cameras(); - let camera = &mut cameras[0]; - - let roles = vec![ffi::StreamRole::StillCapture]; - - let mut config = camera.inner.pin_mut().generate_configuration(&roles); - - assert_eq!( - config.pin_mut().validate(), - ffi::CameraConfigurationStatus::Valid - ); - } -} From 9656d570e3cc151da02a73023b98aaf60bce6c5a Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 15 Jun 2022 15:52:47 -0600 Subject: [PATCH 03/49] feat: Signal callbacks and frame buffer allocator --- libcamera-bridge/core.cpp | 31 ++++++++++++- libcamera-bridge/core.hpp | 28 +++++++++++- src/bridge.rs | 95 +++++++++++++++++++++++++++++++++++++++ src/bridge/test.rs | 6 +++ 4 files changed, 156 insertions(+), 4 deletions(-) diff --git a/libcamera-bridge/core.cpp b/libcamera-bridge/core.cpp index ad0eeba..1ffc845 100644 --- a/libcamera-bridge/core.cpp +++ b/libcamera-bridge/core.cpp @@ -12,7 +12,7 @@ void CameraManager::start() { int res = libcamera::CameraManager::start(); if (res < 0) { - throw res; + throw(CameraError)(-res); } return; @@ -54,12 +54,39 @@ void configure_camera(libcamera::Camera &cam, int res = cam.configure(&conf); if (res < 0) { - throw res; + throw(CameraError)(-res); } return; } +void connect_camera_buffer_completed( + libcamera::Camera &cam, + rust::Fn + callback) { + cam.bufferCompleted.connect(&cam, [&callback](libcamera::Request *request, + libcamera::FrameBuffer *fb) { + callback(*request, *fb); + }); +} + +void connect_camera_request_completed( + libcamera::Camera &cam, + rust::Fn callback) { + cam.requestCompleted.connect( + &cam, [&callback](libcamera::Request *request) { callback(*request); }); +} + +void connect_camera_disconnected(libcamera::Camera &cam, + rust::Fn callback) { + cam.disconnected.connect(&cam, [&callback]() { callback(); }); +} + +std::unique_ptr +make_frame_buffer_allocator(const std::shared_ptr &cam) { + return std::make_unique(cam); +} + const libcamera::PixelFormat & get_default_pixel_format(DefaultPixelFormat format) { switch (format) { diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index ee55d6d..59e50b0 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -1,6 +1,7 @@ #pragma once #include "libcamera/camera.h" #include "libcamera/camera_manager.h" +#include "libcamera/framebuffer_allocator.h" #include "libcamera/stream.h" #include "rust/cxx.h" @@ -10,6 +11,8 @@ struct BridgeCamera; enum class DefaultPixelFormat; +// Camera Manager + class CameraManager : public libcamera::CameraManager { public: void start(); @@ -19,6 +22,8 @@ class CameraManager : public libcamera::CameraManager { std::unique_ptr make_camera_manager(); +// Camera + libcamera::Camera &get_mut_camera(std::shared_ptr &cam); std::unique_ptr @@ -27,8 +32,22 @@ generate_camera_configuration(libcamera::Camera &cam, void configure_camera(libcamera::Camera &cam, libcamera::CameraConfiguration &conf); -const libcamera::PixelFormat & -get_default_pixel_format(DefaultPixelFormat format); +void connect_camera_buffer_completed( + libcamera::Camera &cam, + rust::Fn + callback); +void connect_camera_request_completed( + libcamera::Camera &cam, + rust::Fn callback); +void connect_camera_disconnected(libcamera::Camera &cam, + rust::Fn callback); + +// Frame Buffers + +std::unique_ptr +make_frame_buffer_allocator(const std::shared_ptr &cam); + +// Camera Configuration void set_stream_pixel_format(libcamera::StreamConfiguration &conf, const libcamera::PixelFormat &format); @@ -36,3 +55,8 @@ void set_stream_size(libcamera::StreamConfiguration &conf, unsigned int width, unsigned int height); void set_stream_buffer_count(libcamera::StreamConfiguration &conf, unsigned int buffers); + +// Misc. Types + +const libcamera::PixelFormat & +get_default_pixel_format(DefaultPixelFormat format); diff --git a/src/bridge.rs b/src/bridge.rs index d73aa59..59b4ce7 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -38,9 +38,83 @@ pub mod ffi { Mjpeg, } + #[repr(i32)] + #[derive(Debug)] + enum CameraError { + /// Operation not permitted + EPerm = 1, + /// No such file or directory + ENoEnt = 2, + /// No such process + ESrch = 3, + /// Interrupted system call + EIntr = 4, + /// I/O error + EIo = 5, + /// No such device or address + ENxIo = 6, + /// Argument list too long + E2Big = 7, + /// EXec format error + ENoexec = 8, + /// Bad file number + EBadF = 9, + /// No child processes + EChild = 10, + /// Try again + EAgain = 11, + /// Out of memory + ENoMem = 12, + /// Permission denied + EAcces = 13, + /// Bad address + EFault = 14, + /// Block device required + ENotBlk = 15, + /// Device or resource busy + EBusy = 16, + /// File exists + EExist = 17, + /// Cross-device link + EXDev = 18, + /// No such device + ENoDev = 19, + /// Not a directory + ENotDir = 20, + /// Is a directory + EIsDir = 21, + /// Invalid argument + EInval = 22, + /// File table overflow + ENFile = 23, + /// Too many open files + EMFile = 24, + /// Not a typewriter + ENotTy = 25, + /// Text file busy + ETxtBsy = 26, + /// File too large + EFBig = 27, + /// No space left on device + ENoSpc = 28, + /// Illegal seek + ESPipe = 29, + /// Read-only file system + ERoFs = 30, + /// Too many links + EMLink = 31, + /// Broken pipe + EPipe = 32, + /// Math argument out of domain of func + EDom = 33, + /// Math result not representable + ERange = 34, + } + unsafe extern "C++" { include!("libcamera/stream.h"); include!("libcamera/camera.h"); + include!("libcamera/framebuffer_allocator.h"); include!("libcamera-rs/libcamera-bridge/core.hpp"); // Camera Manager @@ -71,6 +145,19 @@ pub mod ffi { pub fn configure_camera(cam: Pin<&mut Camera>, config: Pin<&mut CameraConfiguration>); + pub fn connect_camera_buffer_completed( + cam: Pin<&mut Camera>, + callback: fn(request: &Request, frame_buffer: &FrameBuffer), + ); + pub fn connect_camera_request_completed(cam: Pin<&mut Camera>, callback: fn(request: &Request)); + pub fn connect_camera_disconnected(cam: Pin<&mut Camera>, callback: fn()); + + // Frame Buffers + #[namespace = "libcamera"] + type FrameBufferAllocator; + + pub fn make_frame_buffer_allocator(cam: &SharedPtr) -> UniquePtr; + // Camera Configuration #[namespace = "libcamera"] type CameraConfiguration; @@ -90,6 +177,8 @@ pub mod ffi { pub fn set_stream_size(stream: Pin<&mut StreamConfiguration>, width: u32, height: u32); pub fn set_stream_buffer_count(stream: Pin<&mut StreamConfiguration>, buffer_count: u32); + // Misc. Types + #[namespace = "libcamera"] type StreamRole; @@ -97,6 +186,12 @@ pub mod ffi { type PixelFormat; pub fn get_default_pixel_format(format: DefaultPixelFormat) -> Pin<&'static PixelFormat>; + + #[namespace = "libcamera"] + type Request; + + #[namespace = "libcamera"] + type FrameBuffer; } } diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 1303c58..6dc8486 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -52,5 +52,11 @@ fn init_camera() { ffi::configure_camera(camera.inner.pin_mut(), config.pin_mut()); + ffi::connect_camera_request_completed(camera.inner.pin_mut(), |_req| { + println!("Request Completed"); + }); + + let _allocator = ffi::make_frame_buffer_allocator(&camera.inner); + camera.inner.pin_mut().release(); } From f2869611415ff1655e3e0fdc8a361dd5be8081f8 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 15 Jun 2022 16:06:40 -0600 Subject: [PATCH 04/49] chore: Minor restructure and cleanup --- build.rs | 4 ++ libcamera-bridge/camera.cpp | 52 ++++++++++++++ libcamera-bridge/camera_manager.cpp | 34 ++++++++++ libcamera-bridge/config.cpp | 18 +++++ libcamera-bridge/core.cpp | 102 +--------------------------- libcamera-bridge/frame_buffers.cpp | 6 ++ 6 files changed, 116 insertions(+), 100 deletions(-) create mode 100644 libcamera-bridge/camera.cpp create mode 100644 libcamera-bridge/camera_manager.cpp create mode 100644 libcamera-bridge/config.cpp create mode 100644 libcamera-bridge/frame_buffers.cpp diff --git a/build.rs b/build.rs index bd38788..1663405 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,10 @@ fn main() { cxx_build::bridge("src/bridge.rs") .file("libcamera-bridge/core.cpp") + .file("libcamera-bridge/camera_manager.cpp") + .file("libcamera-bridge/camera.cpp") + .file("libcamera-bridge/frame_buffers.cpp") + .file("libcamera-bridge/config.cpp") .flag_if_supported("-std=c++17") .include("/usr/local/include/libcamera") .include("libcamera/build/include/libcamera") diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp new file mode 100644 index 0000000..d761cd8 --- /dev/null +++ b/libcamera-bridge/camera.cpp @@ -0,0 +1,52 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +libcamera::Camera &get_mut_camera(std::shared_ptr &cam) { + return *cam.get(); +} + +std::unique_ptr +generate_camera_configuration(libcamera::Camera &cam, + const rust::Vec &roles) { + std::vector cpp_roles; + + for (auto role : roles) { + cpp_roles.push_back(role); + } + + return cam.generateConfiguration(cpp_roles); +} + +void configure_camera(libcamera::Camera &cam, + libcamera::CameraConfiguration &conf) { + int res = cam.configure(&conf); + + if (res < 0) { + throw(CameraError)(-res); + } + + return; +} + +void connect_camera_buffer_completed( + libcamera::Camera &cam, + rust::Fn + callback) { + cam.bufferCompleted.connect(&cam, [&callback](libcamera::Request *request, + libcamera::FrameBuffer *fb) { + callback(*request, *fb); + }); +} + +void connect_camera_request_completed( + libcamera::Camera &cam, + rust::Fn callback) { + cam.requestCompleted.connect( + &cam, [&callback](libcamera::Request *request) { callback(*request); }); +} + +void connect_camera_disconnected(libcamera::Camera &cam, + rust::Fn callback) { + cam.disconnected.connect(&cam, [&callback]() { callback(); }); +} diff --git a/libcamera-bridge/camera_manager.cpp b/libcamera-bridge/camera_manager.cpp new file mode 100644 index 0000000..e9ce6b5 --- /dev/null +++ b/libcamera-bridge/camera_manager.cpp @@ -0,0 +1,34 @@ +#include "./core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +#include + +std::unique_ptr make_camera_manager() { + return std::make_unique(); +} + +void CameraManager::start() { + int res = libcamera::CameraManager::start(); + + if (res < 0) { + throw(CameraError)(-res); + } + + return; +} + +rust::String CameraManager::version() { + return libcamera::CameraManager::version(); +} + +rust::Vec CameraManager::cameras() const { + auto cameras = libcamera::CameraManager::cameras(); + rust::Vec rust_cameras; + + for (auto camera : cameras) { + rust_cameras.push_back(BridgeCamera{.inner = camera}); + } + + return rust_cameras; +} diff --git a/libcamera-bridge/config.cpp b/libcamera-bridge/config.cpp new file mode 100644 index 0000000..22e4c10 --- /dev/null +++ b/libcamera-bridge/config.cpp @@ -0,0 +1,18 @@ +#include "./core.hpp" + +#include "libcamera/geometry.h" + +void set_stream_pixel_format(libcamera::StreamConfiguration &conf, + const libcamera::PixelFormat &format) { + conf.pixelFormat = format; +} + +void set_stream_size(libcamera::StreamConfiguration &conf, unsigned int width, + unsigned int height) { + conf.size = libcamera::Size(width, height); +} + +void set_stream_buffer_count(libcamera::StreamConfiguration &conf, + unsigned int buffers) { + conf.bufferCount = buffers; +} diff --git a/libcamera-bridge/core.cpp b/libcamera-bridge/core.cpp index 1ffc845..e76b2e8 100644 --- a/libcamera-bridge/core.cpp +++ b/libcamera-bridge/core.cpp @@ -1,91 +1,8 @@ #include "./core.hpp" -#include "libcamera-rs/src/bridge.rs.h" -#include "libcamera/formats.h" -#include "libcamera/geometry.h" -#include - -std::unique_ptr make_camera_manager() { - return std::make_unique(); -} - -void CameraManager::start() { - int res = libcamera::CameraManager::start(); - - if (res < 0) { - throw(CameraError)(-res); - } - - return; -} - -rust::String CameraManager::version() { - return libcamera::CameraManager::version(); -} - -rust::Vec CameraManager::cameras() const { - auto cameras = libcamera::CameraManager::cameras(); - rust::Vec rust_cameras; - - for (auto camera : cameras) { - rust_cameras.push_back(BridgeCamera{.inner = camera}); - } - - return rust_cameras; -} - -libcamera::Camera &get_mut_camera(std::shared_ptr &cam) { - return *cam.get(); -} - -std::unique_ptr -generate_camera_configuration(libcamera::Camera &cam, - const rust::Vec &roles) { - std::vector cpp_roles; - - for (auto role : roles) { - cpp_roles.push_back(role); - } - return cam.generateConfiguration(cpp_roles); -} - -void configure_camera(libcamera::Camera &cam, - libcamera::CameraConfiguration &conf) { - int res = cam.configure(&conf); - - if (res < 0) { - throw(CameraError)(-res); - } - - return; -} - -void connect_camera_buffer_completed( - libcamera::Camera &cam, - rust::Fn - callback) { - cam.bufferCompleted.connect(&cam, [&callback](libcamera::Request *request, - libcamera::FrameBuffer *fb) { - callback(*request, *fb); - }); -} - -void connect_camera_request_completed( - libcamera::Camera &cam, - rust::Fn callback) { - cam.requestCompleted.connect( - &cam, [&callback](libcamera::Request *request) { callback(*request); }); -} - -void connect_camera_disconnected(libcamera::Camera &cam, - rust::Fn callback) { - cam.disconnected.connect(&cam, [&callback]() { callback(); }); -} +#include "libcamera-rs/src/bridge.rs.h" -std::unique_ptr -make_frame_buffer_allocator(const std::shared_ptr &cam) { - return std::make_unique(cam); -} +#include "libcamera/formats.h" const libcamera::PixelFormat & get_default_pixel_format(DefaultPixelFormat format) { @@ -96,18 +13,3 @@ get_default_pixel_format(DefaultPixelFormat format) { return libcamera::formats::RGB888; } } - -void set_stream_pixel_format(libcamera::StreamConfiguration &conf, - const libcamera::PixelFormat &format) { - conf.pixelFormat = format; -} - -void set_stream_size(libcamera::StreamConfiguration &conf, unsigned int width, - unsigned int height) { - conf.size = libcamera::Size(width, height); -} - -void set_stream_buffer_count(libcamera::StreamConfiguration &conf, - unsigned int buffers) { - conf.bufferCount = buffers; -} diff --git a/libcamera-bridge/frame_buffers.cpp b/libcamera-bridge/frame_buffers.cpp new file mode 100644 index 0000000..35b5693 --- /dev/null +++ b/libcamera-bridge/frame_buffers.cpp @@ -0,0 +1,6 @@ +#include "./core.hpp" + +std::unique_ptr +make_frame_buffer_allocator(const std::shared_ptr &cam) { + return std::make_unique(cam); +} From c4e744418200f5651b7d974e797c3f8c201ea2bf Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 15 Jun 2022 16:59:14 -0600 Subject: [PATCH 05/49] feat: Allocate Buffers --- libcamera-bridge/camera.cpp | 2 -- libcamera-bridge/camera_manager.cpp | 2 -- libcamera-bridge/config.cpp | 5 +++++ libcamera-bridge/core.hpp | 7 +++++++ libcamera-bridge/frame_buffers.cpp | 14 ++++++++++++++ src/bridge.rs | 10 ++++++++++ src/bridge/test.rs | 10 +++++++++- 7 files changed, 45 insertions(+), 5 deletions(-) diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index d761cd8..4a2b955 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -25,8 +25,6 @@ void configure_camera(libcamera::Camera &cam, if (res < 0) { throw(CameraError)(-res); } - - return; } void connect_camera_buffer_completed( diff --git a/libcamera-bridge/camera_manager.cpp b/libcamera-bridge/camera_manager.cpp index e9ce6b5..16a0057 100644 --- a/libcamera-bridge/camera_manager.cpp +++ b/libcamera-bridge/camera_manager.cpp @@ -14,8 +14,6 @@ void CameraManager::start() { if (res < 0) { throw(CameraError)(-res); } - - return; } rust::String CameraManager::version() { diff --git a/libcamera-bridge/config.cpp b/libcamera-bridge/config.cpp index 22e4c10..dba0eb3 100644 --- a/libcamera-bridge/config.cpp +++ b/libcamera-bridge/config.cpp @@ -16,3 +16,8 @@ void set_stream_buffer_count(libcamera::StreamConfiguration &conf, unsigned int buffers) { conf.bufferCount = buffers; } + +libcamera::Stream & +get_stream_from_configuration(libcamera::StreamConfiguration &conf) { + return *conf.stream(); +} diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 59e50b0..b37c793 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -47,6 +47,10 @@ void connect_camera_disconnected(libcamera::Camera &cam, std::unique_ptr make_frame_buffer_allocator(const std::shared_ptr &cam); +unsigned int +allocate_frame_buffer_stream(libcamera::FrameBufferAllocator &alloc, + libcamera::Stream &stream); + // Camera Configuration void set_stream_pixel_format(libcamera::StreamConfiguration &conf, @@ -56,6 +60,9 @@ void set_stream_size(libcamera::StreamConfiguration &conf, unsigned int width, void set_stream_buffer_count(libcamera::StreamConfiguration &conf, unsigned int buffers); +libcamera::Stream & +get_stream_from_configuration(libcamera::StreamConfiguration &conf); + // Misc. Types const libcamera::PixelFormat & diff --git a/libcamera-bridge/frame_buffers.cpp b/libcamera-bridge/frame_buffers.cpp index 35b5693..432c56a 100644 --- a/libcamera-bridge/frame_buffers.cpp +++ b/libcamera-bridge/frame_buffers.cpp @@ -1,6 +1,20 @@ #include "./core.hpp" +#include "libcamera-rs/src/bridge.rs.h" + std::unique_ptr make_frame_buffer_allocator(const std::shared_ptr &cam) { return std::make_unique(cam); } + +unsigned int +allocate_frame_buffer_stream(libcamera::FrameBufferAllocator &alloc, + libcamera::Stream &stream) { + int buffers = alloc.allocate(&stream); + + if (buffers < 0) { + throw(CameraError)(-buffers); + } + + return (unsigned int)buffers; +} diff --git a/src/bridge.rs b/src/bridge.rs index 59b4ce7..e786cde 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -158,6 +158,11 @@ pub mod ffi { pub fn make_frame_buffer_allocator(cam: &SharedPtr) -> UniquePtr; + pub fn allocate_frame_buffer_stream( + alloc: Pin<&mut FrameBufferAllocator>, + stream: Pin<&mut Stream>, + ) -> Result; + // Camera Configuration #[namespace = "libcamera"] type CameraConfiguration; @@ -177,6 +182,11 @@ pub mod ffi { pub fn set_stream_size(stream: Pin<&mut StreamConfiguration>, width: u32, height: u32); pub fn set_stream_buffer_count(stream: Pin<&mut StreamConfiguration>, buffer_count: u32); + pub fn get_stream_from_configuration(conf: Pin<&mut StreamConfiguration>) -> Pin<&mut Stream>; + + #[namespace = "libcamera"] + type Stream; + // Misc. Types #[namespace = "libcamera"] diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 6dc8486..954f58d 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -56,7 +56,15 @@ fn init_camera() { println!("Request Completed"); }); - let _allocator = ffi::make_frame_buffer_allocator(&camera.inner); + let mut allocator = ffi::make_frame_buffer_allocator(&camera.inner); + + let buffer_count = ffi::allocate_frame_buffer_stream( + allocator.pin_mut(), + ffi::get_stream_from_configuration(config.pin_mut().at(0)), + ) + .unwrap(); + + assert_eq!(buffer_count, 4); camera.inner.pin_mut().release(); } From b2bbcfbb90ee80703b30769277a4ae1fd7b743d0 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Thu, 16 Jun 2022 11:17:40 -0600 Subject: [PATCH 06/49] More work on getting frame buffers to work --- libcamera-bridge/camera.cpp | 8 ++++++++ libcamera-bridge/config.cpp | 6 ++++++ libcamera-bridge/core.hpp | 10 ++++++++++ libcamera-bridge/frame_buffers.cpp | 5 +++++ src/bridge.rs | 31 ++++++++++++++++++++++++++---- src/bridge/test.rs | 28 +++++++++++++++++++++------ 6 files changed, 78 insertions(+), 10 deletions(-) diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index 4a2b955..1b83676 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -6,6 +6,14 @@ libcamera::Camera &get_mut_camera(std::shared_ptr &cam) { return *cam.get(); } +void start_camera(libcamera::Camera &cam, libcamera::ControlList &controls) { + cam.start(&controls); +} + +void queue_camera_request(libcamera::Camera &cam, libcamera::Request &req) { + cam.queueRequest(&req); +} + std::unique_ptr generate_camera_configuration(libcamera::Camera &cam, const rust::Vec &roles) { diff --git a/libcamera-bridge/config.cpp b/libcamera-bridge/config.cpp index dba0eb3..baea12e 100644 --- a/libcamera-bridge/config.cpp +++ b/libcamera-bridge/config.cpp @@ -1,5 +1,7 @@ #include "./core.hpp" +#include "libcamera-rs/src/bridge.rs.h" + #include "libcamera/geometry.h" void set_stream_pixel_format(libcamera::StreamConfiguration &conf, @@ -21,3 +23,7 @@ libcamera::Stream & get_stream_from_configuration(libcamera::StreamConfiguration &conf) { return *conf.stream(); } + +std::unique_ptr new_control_list() { + return std::make_unique(); +} diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index b37c793..8d97e73 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -8,6 +8,7 @@ using CameraConfigurationStatus = libcamera::CameraConfiguration::Status; struct BridgeCamera; +struct BridgeStreamConfiguration; enum class DefaultPixelFormat; @@ -26,6 +27,10 @@ std::unique_ptr make_camera_manager(); libcamera::Camera &get_mut_camera(std::shared_ptr &cam); +void start_camera(libcamera::Camera &cam, libcamera::ControlList &controls); + +void queue_camera_request(libcamera::Camera &cam, libcamera::Request &req); + std::unique_ptr generate_camera_configuration(libcamera::Camera &cam, const rust::Vec &roles); @@ -51,6 +56,9 @@ unsigned int allocate_frame_buffer_stream(libcamera::FrameBufferAllocator &alloc, libcamera::Stream &stream); +void add_request_buffer(libcamera::Request &req, libcamera::Stream &stream, + libcamera::FrameBuffer &buffer); + // Camera Configuration void set_stream_pixel_format(libcamera::StreamConfiguration &conf, @@ -63,6 +71,8 @@ void set_stream_buffer_count(libcamera::StreamConfiguration &conf, libcamera::Stream & get_stream_from_configuration(libcamera::StreamConfiguration &conf); +std::unique_ptr new_control_list(); + // Misc. Types const libcamera::PixelFormat & diff --git a/libcamera-bridge/frame_buffers.cpp b/libcamera-bridge/frame_buffers.cpp index 432c56a..f238171 100644 --- a/libcamera-bridge/frame_buffers.cpp +++ b/libcamera-bridge/frame_buffers.cpp @@ -18,3 +18,8 @@ allocate_frame_buffer_stream(libcamera::FrameBufferAllocator &alloc, return (unsigned int)buffers; } + +void add_request_buffer(libcamera::Request &req, libcamera::Stream &stream, + libcamera::FrameBuffer &buffer) { + req.addBuffer(&stream, &buffer); +} diff --git a/src/bridge.rs b/src/bridge.rs index e786cde..84a0616 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -10,6 +10,10 @@ pub mod ffi { inner: SharedPtr, } + struct BridgeStreamConfiguration { + inner: SharedPtr, + } + #[namespace = "libcamera"] #[repr(i32)] #[derive(Debug)] @@ -138,6 +142,13 @@ pub mod ffi { pub fn release(self: Pin<&mut Camera>) -> i32; pub fn stop(self: Pin<&mut Camera>) -> i32; + // FIXME: Safety + pub fn start_camera(cam: Pin<&mut Camera>, control_list: Pin<&mut ControlList>) -> Result<()>; + + #[cxx_name = "createRequest"] + pub fn create_request(self: Pin<&mut Camera>, cookie: u64) -> UniquePtr; + pub fn queue_camera_request(cam: Pin<&mut Camera>, req: Pin<&mut Request>) -> Result<()>; + pub fn generate_camera_configuration( cam: Pin<&mut Camera>, roles: &Vec, @@ -163,12 +174,25 @@ pub mod ffi { stream: Pin<&mut Stream>, ) -> Result; + #[namespace = "libcamera"] + type Request; + + pub fn add_request_buffer( + req: Pin<&mut Request>, + stream: Pin<&mut Stream>, + buffer: Pin<&mut FrameBuffer>, + ); + + #[namespace = "libcamera"] + type Stream; + // Camera Configuration #[namespace = "libcamera"] type CameraConfiguration; pub fn at(self: Pin<&mut CameraConfiguration>, index: u32) -> Pin<&mut StreamConfiguration>; pub fn validate(self: Pin<&mut CameraConfiguration>) -> CameraConfigurationStatus; + pub fn size(self: &CameraConfiguration) -> usize; type CameraConfigurationStatus; @@ -185,7 +209,9 @@ pub mod ffi { pub fn get_stream_from_configuration(conf: Pin<&mut StreamConfiguration>) -> Pin<&mut Stream>; #[namespace = "libcamera"] - type Stream; + type ControlList; + + pub fn new_control_list() -> UniquePtr; // Misc. Types @@ -197,9 +223,6 @@ pub mod ffi { pub fn get_default_pixel_format(format: DefaultPixelFormat) -> Pin<&'static PixelFormat>; - #[namespace = "libcamera"] - type Request; - #[namespace = "libcamera"] type FrameBuffer; } diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 954f58d..d01043c 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -58,13 +58,29 @@ fn init_camera() { let mut allocator = ffi::make_frame_buffer_allocator(&camera.inner); - let buffer_count = ffi::allocate_frame_buffer_stream( - allocator.pin_mut(), - ffi::get_stream_from_configuration(config.pin_mut().at(0)), - ) - .unwrap(); + let stream_config_count = config.pin_mut().size() as u32; + for i in 0..stream_config_count { + let stream_config = config.pin_mut().at(i); + let stream = ffi::get_stream_from_configuration(stream_config); - assert_eq!(buffer_count, 4); + let buffer_count = ffi::allocate_frame_buffer_stream(allocator.pin_mut(), stream).unwrap(); + assert_eq!(buffer_count, 4); + + let _request = camera.inner.pin_mut().create_request(0); + + /* + let buffers = ffi::get_allocator_buffers(&stream); + for i in 0..buffer_count { + let buffer = buffers[i]; + request.pin_mut().add_buffer(&mut stream, &mut buffer); + } + */ + } + + let mut controls = ffi::new_control_list(); + ffi::start_camera(camera.inner.pin_mut(), controls.pin_mut()).unwrap(); + + camera.inner.pin_mut().stop(); camera.inner.pin_mut().release(); } From 174b83b8acf8903f78b986db2d5934c61f5d99aa Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Thu, 16 Jun 2022 16:28:33 -0600 Subject: [PATCH 07/49] Get everything but queue_camera_request working --- build.rs | 4 +- libcamera-bridge/camera.cpp | 19 +++++++- libcamera-bridge/core.hpp | 15 +++++-- libcamera-bridge/frame_buffers.cpp | 19 ++++++-- src/bridge.rs | 32 ++++++++++---- src/bridge/test.rs | 69 +++++++++++++++++++----------- 6 files changed, 112 insertions(+), 46 deletions(-) diff --git a/build.rs b/build.rs index 1663405..431e491 100644 --- a/build.rs +++ b/build.rs @@ -11,8 +11,8 @@ fn main() { .compile("libcamera-bridge"); println!("cargo:rerun-if-changed=src/bridge.rs"); - println!("cargo:rerun-if-changed=libcamera-bridge/core.cpp"); - println!("cargo:rerun-if-changed=libcamera-bridge/core.hpp"); + println!("cargo:rerun-if-changed=libcamera-bridge/*.cpp"); + println!("cargo:rerun-if-changed=libcamera-bridge/*.hpp"); // link libcamera println!("cargo:rustc-link-lib=dylib=camera"); diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index 1b83676..4426f9d 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -2,12 +2,27 @@ #include "libcamera-rs/src/bridge.rs.h" +#include + libcamera::Camera &get_mut_camera(std::shared_ptr &cam) { return *cam.get(); } -void start_camera(libcamera::Camera &cam, libcamera::ControlList &controls) { - cam.start(&controls); +void start_camera_with_controls(libcamera::Camera &cam, + libcamera::ControlList &controls) { + int ret = cam.start(&controls); + + if (ret < 0) { + throw(CameraError)(-ret); + } +} + +void start_camera(libcamera::Camera &cam) { + int ret = cam.start(); + + if (ret < 0) { + throw(CameraError)(-ret); + } } void queue_camera_request(libcamera::Camera &cam, libcamera::Request &req) { diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 8d97e73..28e56e9 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -8,7 +8,6 @@ using CameraConfigurationStatus = libcamera::CameraConfiguration::Status; struct BridgeCamera; -struct BridgeStreamConfiguration; enum class DefaultPixelFormat; @@ -27,7 +26,9 @@ std::unique_ptr make_camera_manager(); libcamera::Camera &get_mut_camera(std::shared_ptr &cam); -void start_camera(libcamera::Camera &cam, libcamera::ControlList &controls); +void start_camera_with_controls(libcamera::Camera &cam, + libcamera::ControlList &controls); +void start_camera(libcamera::Camera &cam); void queue_camera_request(libcamera::Camera &cam, libcamera::Request &req); @@ -53,11 +54,17 @@ std::unique_ptr make_frame_buffer_allocator(const std::shared_ptr &cam); unsigned int -allocate_frame_buffer_stream(libcamera::FrameBufferAllocator &alloc, +allocate_frame_buffer_stream(const libcamera::FrameBufferAllocator &alloc, libcamera::Stream &stream); void add_request_buffer(libcamera::Request &req, libcamera::Stream &stream, - libcamera::FrameBuffer &buffer); + libcamera::FrameBuffer *buffer); + +size_t get_allocator_buffer_count(const libcamera::FrameBufferAllocator &alloc, + libcamera::Stream &stream); +libcamera::FrameBuffer * +get_allocator_buffer(const libcamera::FrameBufferAllocator &alloc, + libcamera::Stream &stream, size_t idx); // Camera Configuration diff --git a/libcamera-bridge/frame_buffers.cpp b/libcamera-bridge/frame_buffers.cpp index f238171..aa30f5b 100644 --- a/libcamera-bridge/frame_buffers.cpp +++ b/libcamera-bridge/frame_buffers.cpp @@ -8,9 +8,9 @@ make_frame_buffer_allocator(const std::shared_ptr &cam) { } unsigned int -allocate_frame_buffer_stream(libcamera::FrameBufferAllocator &alloc, +allocate_frame_buffer_stream(const libcamera::FrameBufferAllocator &alloc, libcamera::Stream &stream) { - int buffers = alloc.allocate(&stream); + int buffers = ((libcamera::FrameBufferAllocator &)alloc).allocate(&stream); if (buffers < 0) { throw(CameraError)(-buffers); @@ -20,6 +20,17 @@ allocate_frame_buffer_stream(libcamera::FrameBufferAllocator &alloc, } void add_request_buffer(libcamera::Request &req, libcamera::Stream &stream, - libcamera::FrameBuffer &buffer) { - req.addBuffer(&stream, &buffer); + libcamera::FrameBuffer *buffer) { + req.addBuffer(&stream, buffer); +} + +size_t get_allocator_buffer_count(const libcamera::FrameBufferAllocator &alloc, + libcamera::Stream &stream) { + return alloc.buffers(&stream).size(); +} + +libcamera::FrameBuffer * +get_allocator_buffer(const libcamera::FrameBufferAllocator &alloc, + libcamera::Stream &stream, size_t idx) { + return alloc.buffers(&stream).at(idx).get(); } diff --git a/src/bridge.rs b/src/bridge.rs index 84a0616..bd358fd 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -10,10 +10,6 @@ pub mod ffi { inner: SharedPtr, } - struct BridgeStreamConfiguration { - inner: SharedPtr, - } - #[namespace = "libcamera"] #[repr(i32)] #[derive(Debug)] @@ -138,12 +134,19 @@ pub mod ffi { pub fn get_mut_camera(cam: &mut SharedPtr) -> Pin<&mut Camera>; pub fn id(self: &Camera) -> &CxxString; + #[must_use] pub fn acquire(self: Pin<&mut Camera>) -> i32; + #[must_use] pub fn release(self: Pin<&mut Camera>) -> i32; + #[must_use] pub fn stop(self: Pin<&mut Camera>) -> i32; // FIXME: Safety - pub fn start_camera(cam: Pin<&mut Camera>, control_list: Pin<&mut ControlList>) -> Result<()>; + pub fn start_camera_with_controls( + cam: Pin<&mut Camera>, + control_list: Pin<&mut ControlList>, + ) -> Result<()>; + pub fn start_camera(cam: Pin<&mut Camera>) -> Result<()>; #[cxx_name = "createRequest"] pub fn create_request(self: Pin<&mut Camera>, cookie: u64) -> UniquePtr; @@ -170,19 +173,32 @@ pub mod ffi { pub fn make_frame_buffer_allocator(cam: &SharedPtr) -> UniquePtr; pub fn allocate_frame_buffer_stream( - alloc: Pin<&mut FrameBufferAllocator>, + alloc: &FrameBufferAllocator, stream: Pin<&mut Stream>, ) -> Result; #[namespace = "libcamera"] type Request; - pub fn add_request_buffer( + /// # Safety + /// TODO + pub unsafe fn add_request_buffer( req: Pin<&mut Request>, stream: Pin<&mut Stream>, - buffer: Pin<&mut FrameBuffer>, + buffer: *mut FrameBuffer, ); + pub fn get_allocator_buffer_count( + alloc: &FrameBufferAllocator, + stream: Pin<&mut Stream>, + ) -> usize; + + pub fn get_allocator_buffer( + alloc: &FrameBufferAllocator, + stream: Pin<&mut Stream>, + idx: usize, + ) -> Result<*mut FrameBuffer>; + #[namespace = "libcamera"] type Stream; diff --git a/src/bridge/test.rs b/src/bridge/test.rs index d01043c..cae84d7 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -26,6 +26,9 @@ fn generate_configuration() { } #[test] +/// Test initializing camera +/// # Safety +/// Always segfaults. fn init_camera() { let mut manager = ffi::make_camera_manager(); manager.pin_mut().start().unwrap(); @@ -33,9 +36,9 @@ fn init_camera() { let mut cameras = manager.pin_mut().cameras(); let camera = &mut cameras[0]; - camera.inner.pin_mut().acquire(); + assert_eq!(camera.inner.pin_mut().acquire(), 0,); - let roles = vec![ffi::StreamRole::StillCapture]; + let roles = vec![ffi::StreamRole::Viewfinder]; let mut config = ffi::generate_camera_configuration(camera.inner.pin_mut(), &roles); ffi::set_stream_pixel_format( @@ -52,35 +55,49 @@ fn init_camera() { ffi::configure_camera(camera.inner.pin_mut(), config.pin_mut()); - ffi::connect_camera_request_completed(camera.inner.pin_mut(), |_req| { - println!("Request Completed"); - }); - - let mut allocator = ffi::make_frame_buffer_allocator(&camera.inner); - + // Allocate and map buffers + let allocator = ffi::make_frame_buffer_allocator(&camera.inner); let stream_config_count = config.pin_mut().size() as u32; - for i in 0..stream_config_count { - let stream_config = config.pin_mut().at(i); - let stream = ffi::get_stream_from_configuration(stream_config); + assert_eq!(stream_config_count, 1); + let buffer_count = ffi::allocate_frame_buffer_stream( + allocator.as_ref().unwrap(), + ffi::get_stream_from_configuration(config.pin_mut().at(0)), + ) + .unwrap(); - let buffer_count = ffi::allocate_frame_buffer_stream(allocator.pin_mut(), stream).unwrap(); + assert_eq!(buffer_count, 4); - assert_eq!(buffer_count, 4); + ffi::start_camera(camera.inner.pin_mut()).unwrap(); - let _request = camera.inner.pin_mut().create_request(0); - - /* - let buffers = ffi::get_allocator_buffers(&stream); - for i in 0..buffer_count { - let buffer = buffers[i]; - request.pin_mut().add_buffer(&mut stream, &mut buffer); - } - */ + let buffer_count = ffi::get_allocator_buffer_count( + &allocator, + ffi::get_stream_from_configuration(config.pin_mut().at(0)), + ); + for buffer_id in 0..buffer_count { + let buffer = ffi::get_allocator_buffer( + &allocator, + ffi::get_stream_from_configuration(config.pin_mut().at(0)), + buffer_id, + ) + .unwrap(); + let mut request = camera.inner.pin_mut().create_request(0); + unsafe { + println!("Buf: {buffer:?}"); + ffi::add_request_buffer( + request.pin_mut(), + ffi::get_stream_from_configuration(config.pin_mut().at(0)), + buffer, + ); + } + // ffi::queue_camera_request(camera.inner.pin_mut(), request.pin_mut()).unwrap(); } - let mut controls = ffi::new_control_list(); - ffi::start_camera(camera.inner.pin_mut(), controls.pin_mut()).unwrap(); + ffi::connect_camera_request_completed(camera.inner.pin_mut(), |_req| { + eprintln!("Request Completed"); + }); + + std::thread::sleep(std::time::Duration::from_millis(10000)); - camera.inner.pin_mut().stop(); - camera.inner.pin_mut().release(); + assert_eq!(camera.inner.pin_mut().stop(), 0); + assert_eq!(camera.inner.pin_mut().release(), 0); } From 96ba58b2d84acd95c2bd9ecd3a78ff02337d5b42 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Tue, 21 Jun 2022 13:20:25 -0600 Subject: [PATCH 08/49] fix: Refactor to fix use-after-free --- build.rs | 9 +- libcamera-bridge/camera.cpp | 107 +++++---- libcamera-bridge/camera_configuration.cpp | 28 +++ libcamera-bridge/camera_manager.cpp | 49 ++-- libcamera-bridge/config.cpp | 29 --- libcamera-bridge/core.cpp | 15 -- libcamera-bridge/core.hpp | 164 +++++++++----- libcamera-bridge/frame_buffer.cpp | 11 + libcamera-bridge/frame_buffer_allocator.cpp | 50 ++++ libcamera-bridge/frame_buffers.cpp | 36 --- libcamera-bridge/request.cpp | 20 ++ libcamera-bridge/stream.cpp | 9 + libcamera-bridge/stream_configuration.cpp | 10 + src/bridge.rs | 238 ++++++++++---------- src/bridge/test.rs | 117 +++------- src/lib.rs | 4 +- 16 files changed, 489 insertions(+), 407 deletions(-) create mode 100644 libcamera-bridge/camera_configuration.cpp delete mode 100644 libcamera-bridge/config.cpp delete mode 100644 libcamera-bridge/core.cpp create mode 100644 libcamera-bridge/frame_buffer.cpp create mode 100644 libcamera-bridge/frame_buffer_allocator.cpp delete mode 100644 libcamera-bridge/frame_buffers.cpp create mode 100644 libcamera-bridge/request.cpp create mode 100644 libcamera-bridge/stream.cpp create mode 100644 libcamera-bridge/stream_configuration.cpp diff --git a/build.rs b/build.rs index 431e491..1744b5e 100644 --- a/build.rs +++ b/build.rs @@ -1,10 +1,13 @@ fn main() { cxx_build::bridge("src/bridge.rs") - .file("libcamera-bridge/core.cpp") .file("libcamera-bridge/camera_manager.cpp") .file("libcamera-bridge/camera.cpp") - .file("libcamera-bridge/frame_buffers.cpp") - .file("libcamera-bridge/config.cpp") + .file("libcamera-bridge/camera_configuration.cpp") + .file("libcamera-bridge/stream_configuration.cpp") + .file("libcamera-bridge/stream.cpp") + .file("libcamera-bridge/frame_buffer_allocator.cpp") + .file("libcamera-bridge/frame_buffer.cpp") + .file("libcamera-bridge/request.cpp") .flag_if_supported("-std=c++17") .include("/usr/local/include/libcamera") .include("libcamera/build/include/libcamera") diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index 4426f9d..6535372 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -2,72 +2,97 @@ #include "libcamera-rs/src/bridge.rs.h" -#include +std::shared_ptr Camera::into_shared() { + VALIDATE_POINTERS() -libcamera::Camera &get_mut_camera(std::shared_ptr &cam) { - return *cam.get(); + return this->inner; } -void start_camera_with_controls(libcamera::Camera &cam, - libcamera::ControlList &controls) { - int ret = cam.start(&controls); +void Camera::acquire() { + VALIDATE_POINTERS() + int ret = this->inner->acquire(); if (ret < 0) { - throw(CameraError)(-ret); + throw(BindErrorCode)(-ret); } } -void start_camera(libcamera::Camera &cam) { - int ret = cam.start(); +void Camera::release() { + VALIDATE_POINTERS() + int ret = this->inner->release(); if (ret < 0) { - throw(CameraError)(-ret); + throw(BindErrorCode)(-ret); } } -void queue_camera_request(libcamera::Camera &cam, libcamera::Request &req) { - cam.queueRequest(&req); -} +BindCameraConfiguration +Camera::generate_configuration(rust::Slice roles) { + VALIDATE_POINTERS() -std::unique_ptr -generate_camera_configuration(libcamera::Camera &cam, - const rust::Vec &roles) { - std::vector cpp_roles; + std::vector roles_vec; + for (libcamera::StreamRole role : roles) { + roles_vec.push_back(role); + } - for (auto role : roles) { - cpp_roles.push_back(role); + std::unique_ptr conf = + this->inner->generateConfiguration(roles_vec); + if (!conf) { + throw(BindErrorCode) ENODEV; } - return cam.generateConfiguration(cpp_roles); + BindCameraConfiguration bind_conf{ + .inner = std::make_unique(std::move(conf)), + }; + return bind_conf; } -void configure_camera(libcamera::Camera &cam, - libcamera::CameraConfiguration &conf) { - int res = cam.configure(&conf); +void Camera::configure(CameraConfiguration &conf) { + VALIDATE_POINTERS() - if (res < 0) { - throw(CameraError)(-res); + int ret = this->inner->configure(conf.into_ptr()); + if (ret < 0) { + throw(BindErrorCode)(-ret); } } -void connect_camera_buffer_completed( - libcamera::Camera &cam, - rust::Fn - callback) { - cam.bufferCompleted.connect(&cam, [&callback](libcamera::Request *request, - libcamera::FrameBuffer *fb) { - callback(*request, *fb); - }); +BindRequest Camera::create_request() { + VALIDATE_POINTERS() + + std::unique_ptr req = this->inner->createRequest(); + if (!req) { + throw(BindErrorCode) ENODEV; + } + + BindRequest bind_req{ + .inner = std::make_unique(std::move(req)), + }; + return bind_req; } -void connect_camera_request_completed( - libcamera::Camera &cam, - rust::Fn callback) { - cam.requestCompleted.connect( - &cam, [&callback](libcamera::Request *request) { callback(*request); }); +void Camera::queue_request(Request &req) { + VALIDATE_POINTERS() + + int ret = this->inner->queueRequest(req.into_ptr()); + if (ret < 0) { + throw(BindErrorCode)(-ret); + } } -void connect_camera_disconnected(libcamera::Camera &cam, - rust::Fn callback) { - cam.disconnected.connect(&cam, [&callback]() { callback(); }); +void Camera::start() { + VALIDATE_POINTERS() + + int ret = this->inner->start(); + if (ret < 0) { + throw(BindErrorCode)(-ret); + } +} + +void Camera::stop() { + VALIDATE_POINTERS() + + int ret = this->inner->stop(); + if (ret < 0) { + throw(BindErrorCode)(-ret); + } } diff --git a/libcamera-bridge/camera_configuration.cpp b/libcamera-bridge/camera_configuration.cpp new file mode 100644 index 0000000..834d04f --- /dev/null +++ b/libcamera-bridge/camera_configuration.cpp @@ -0,0 +1,28 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +libcamera::CameraConfiguration *CameraConfiguration::into_ptr() { + VALIDATE_POINTERS() + + return this->inner.get(); +} + +BindStreamConfiguration CameraConfiguration::at(unsigned int idx) { + VALIDATE_POINTERS() + + libcamera::StreamConfiguration *str = &this->inner->at(idx); + if (!str) { + throw(BindErrorCode) ENODEV; + } + BindStreamConfiguration conf{ + .inner = std::make_unique(str), + }; + return conf; +} + +CameraConfigurationStatus CameraConfiguration::validate() { + VALIDATE_POINTERS() + + return this->inner->validate(); +} diff --git a/libcamera-bridge/camera_manager.cpp b/libcamera-bridge/camera_manager.cpp index 16a0057..f9103e5 100644 --- a/libcamera-bridge/camera_manager.cpp +++ b/libcamera-bridge/camera_manager.cpp @@ -1,32 +1,49 @@ -#include "./core.hpp" +#include "core.hpp" #include "libcamera-rs/src/bridge.rs.h" -#include - -std::unique_ptr make_camera_manager() { - return std::make_unique(); +BindCameraManager make_camera_manager() { + BindCameraManager manager{ + .inner = std::make_unique( + std::make_unique()), + }; + return manager; } void CameraManager::start() { - int res = libcamera::CameraManager::start(); + VALIDATE_POINTERS() - if (res < 0) { - throw(CameraError)(-res); + int ret = this->inner->start(); + if (ret < 0) { + throw(BindErrorCode)(-ret); } } -rust::String CameraManager::version() { - return libcamera::CameraManager::version(); +void CameraManager::stop() { + VALIDATE_POINTERS() + + this->inner->stop(); } -rust::Vec CameraManager::cameras() const { - auto cameras = libcamera::CameraManager::cameras(); - rust::Vec rust_cameras; +rust::Vec CameraManager::get_camera_ids() { + VALIDATE_POINTERS() - for (auto camera : cameras) { - rust_cameras.push_back(BridgeCamera{.inner = camera}); + rust::Vec camera_ids; + for (std::shared_ptr cam : this->inner->cameras()) { + camera_ids.push_back(cam->id()); } + return camera_ids; +} - return rust_cameras; +BindCamera CameraManager::get_camera_by_id(rust::Str id) { + VALIDATE_POINTERS() + + std::shared_ptr cam = this->inner->get(std::string(id)); + if (!cam) { + throw(BindErrorCode) ENODEV; + } + BindCamera bind_cam{ + .inner = std::make_unique(cam), + }; + return bind_cam; } diff --git a/libcamera-bridge/config.cpp b/libcamera-bridge/config.cpp deleted file mode 100644 index baea12e..0000000 --- a/libcamera-bridge/config.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "./core.hpp" - -#include "libcamera-rs/src/bridge.rs.h" - -#include "libcamera/geometry.h" - -void set_stream_pixel_format(libcamera::StreamConfiguration &conf, - const libcamera::PixelFormat &format) { - conf.pixelFormat = format; -} - -void set_stream_size(libcamera::StreamConfiguration &conf, unsigned int width, - unsigned int height) { - conf.size = libcamera::Size(width, height); -} - -void set_stream_buffer_count(libcamera::StreamConfiguration &conf, - unsigned int buffers) { - conf.bufferCount = buffers; -} - -libcamera::Stream & -get_stream_from_configuration(libcamera::StreamConfiguration &conf) { - return *conf.stream(); -} - -std::unique_ptr new_control_list() { - return std::make_unique(); -} diff --git a/libcamera-bridge/core.cpp b/libcamera-bridge/core.cpp deleted file mode 100644 index e76b2e8..0000000 --- a/libcamera-bridge/core.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "./core.hpp" - -#include "libcamera-rs/src/bridge.rs.h" - -#include "libcamera/formats.h" - -const libcamera::PixelFormat & -get_default_pixel_format(DefaultPixelFormat format) { - switch (format) { - case DefaultPixelFormat::Rgb888: - return libcamera::formats::RGB888; - default: - return libcamera::formats::RGB888; - } -} diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 28e56e9..e2b0cb2 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -1,86 +1,136 @@ #pragma once -#include "libcamera/camera.h" -#include "libcamera/camera_manager.h" -#include "libcamera/framebuffer_allocator.h" -#include "libcamera/stream.h" + +#include +#include +#include +#include +#include +#include +#include + #include "rust/cxx.h" +struct BindCameraManager; +struct BindCamera; +struct BindCameraConfiguration; +struct BindStreamConfiguration; +struct BindStream; +struct BindFrameBufferAllocator; +struct BindFrameBuffer; +struct BindRequest; + +struct CameraConfiguration; +struct Request; + using CameraConfigurationStatus = libcamera::CameraConfiguration::Status; -struct BridgeCamera; +// Make sure this->inner is non-null +#define VALIDATE_POINTERS() \ + if (!this->inner) { \ + throw(BindErrorCode) EFAULT; \ + } -enum class DefaultPixelFormat; +BindCameraManager make_camera_manager(); -// Camera Manager +struct CameraManager { +private: + std::unique_ptr inner; -class CameraManager : public libcamera::CameraManager { public: + CameraManager(std::unique_ptr inner_) + : inner{std::move(inner_)} {} + void start(); - rust::String version(); - rust::Vec cameras() const; + void stop(); + rust::Vec get_camera_ids(); + BindCamera get_camera_by_id(rust::Str id); }; -std::unique_ptr make_camera_manager(); +struct Camera { +private: + std::shared_ptr inner; -// Camera +public: + Camera(std::shared_ptr inner_) + : inner{std::move(inner_)} {} + std::shared_ptr into_shared(); + + void acquire(); + void release(); + BindCameraConfiguration + generate_configuration(rust::Slice); + void configure(CameraConfiguration &conf); + BindRequest create_request(); + void queue_request(Request &req); + void start(); + void stop(); +}; -libcamera::Camera &get_mut_camera(std::shared_ptr &cam); +struct CameraConfiguration { +private: + std::unique_ptr inner; -void start_camera_with_controls(libcamera::Camera &cam, - libcamera::ControlList &controls); -void start_camera(libcamera::Camera &cam); +public: + CameraConfiguration(std::unique_ptr inner_) + : inner{std::move(inner_)} {} + libcamera::CameraConfiguration *into_ptr(); -void queue_camera_request(libcamera::Camera &cam, libcamera::Request &req); + BindStreamConfiguration at(unsigned int idx); + CameraConfigurationStatus validate(); +}; -std::unique_ptr -generate_camera_configuration(libcamera::Camera &cam, - const rust::Vec &roles); -void configure_camera(libcamera::Camera &cam, - libcamera::CameraConfiguration &conf); +struct StreamConfiguration { +private: + libcamera::StreamConfiguration *inner; -void connect_camera_buffer_completed( - libcamera::Camera &cam, - rust::Fn - callback); -void connect_camera_request_completed( - libcamera::Camera &cam, - rust::Fn callback); -void connect_camera_disconnected(libcamera::Camera &cam, - rust::Fn callback); +public: + StreamConfiguration(libcamera::StreamConfiguration *inner_) : inner(inner_) {} -// Frame Buffers + BindStream stream(); +}; -std::unique_ptr -make_frame_buffer_allocator(const std::shared_ptr &cam); +struct Stream { +private: + libcamera::Stream *inner; -unsigned int -allocate_frame_buffer_stream(const libcamera::FrameBufferAllocator &alloc, - libcamera::Stream &stream); +public: + Stream(libcamera::Stream *inner_) : inner(inner_) {} + libcamera::Stream *into_ptr(); +}; -void add_request_buffer(libcamera::Request &req, libcamera::Stream &stream, - libcamera::FrameBuffer *buffer); +BindFrameBufferAllocator make_frame_buffer_allocator(Camera &camera); -size_t get_allocator_buffer_count(const libcamera::FrameBufferAllocator &alloc, - libcamera::Stream &stream); -libcamera::FrameBuffer * -get_allocator_buffer(const libcamera::FrameBufferAllocator &alloc, - libcamera::Stream &stream, size_t idx); +struct FrameBufferAllocator { +private: + libcamera::FrameBufferAllocator *inner; + +public: + FrameBufferAllocator(libcamera::FrameBufferAllocator *inner_) + : inner(inner_) {} + ~FrameBufferAllocator(); -// Camera Configuration + void allocate(Stream &stream); + void free(Stream &stream); + rust::Vec buffers(Stream &stream); +}; -void set_stream_pixel_format(libcamera::StreamConfiguration &conf, - const libcamera::PixelFormat &format); -void set_stream_size(libcamera::StreamConfiguration &conf, unsigned int width, - unsigned int height); -void set_stream_buffer_count(libcamera::StreamConfiguration &conf, - unsigned int buffers); +struct FrameBuffer { +private: + libcamera::FrameBuffer *inner; -libcamera::Stream & -get_stream_from_configuration(libcamera::StreamConfiguration &conf); +public: + FrameBuffer(libcamera::FrameBuffer *inner_) : inner(inner_) {} + libcamera::FrameBuffer *into_ptr(); +}; -std::unique_ptr new_control_list(); +struct Request { +private: + std::unique_ptr inner; -// Misc. Types +public: + Request(std::unique_ptr inner_) + : inner{std::move(inner_)} {} + libcamera::Request *into_ptr(); -const libcamera::PixelFormat & -get_default_pixel_format(DefaultPixelFormat format); + void add_buffer(Stream &stream, FrameBuffer &buffer); +}; diff --git a/libcamera-bridge/frame_buffer.cpp b/libcamera-bridge/frame_buffer.cpp new file mode 100644 index 0000000..e3034b9 --- /dev/null +++ b/libcamera-bridge/frame_buffer.cpp @@ -0,0 +1,11 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +#include + +libcamera::FrameBuffer *FrameBuffer::into_ptr() { + VALIDATE_POINTERS() + + return this->inner; +} diff --git a/libcamera-bridge/frame_buffer_allocator.cpp b/libcamera-bridge/frame_buffer_allocator.cpp new file mode 100644 index 0000000..398401c --- /dev/null +++ b/libcamera-bridge/frame_buffer_allocator.cpp @@ -0,0 +1,50 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +BindFrameBufferAllocator make_frame_buffer_allocator(Camera &camera) { + BindFrameBufferAllocator allocator{ + .inner = std::make_unique( + new libcamera::FrameBufferAllocator(camera.into_shared())), + }; + return allocator; +} + +void FrameBufferAllocator::allocate(Stream &stream) { + VALIDATE_POINTERS() + + int ret = this->inner->allocate(stream.into_ptr()); + if (ret < 0) { + throw(BindErrorCode)(-ret); + } +} + +void FrameBufferAllocator::free(Stream &stream) { + VALIDATE_POINTERS() + + int ret = this->inner->free(stream.into_ptr()); + if (ret < 0) { + throw(BindErrorCode)(-ret); + } +} + +rust::Vec FrameBufferAllocator::buffers(Stream &stream) { + VALIDATE_POINTERS() + + rust::Vec vec; + for (const std::unique_ptr &buffer : + this->inner->buffers(stream.into_ptr())) { + BindFrameBuffer bind_buffer{ + .inner = std::make_unique(buffer.get()), + }; + + vec.push_back(std::move(bind_buffer)); + } + return vec; +} + +FrameBufferAllocator::~FrameBufferAllocator() { + if (this->inner) { + delete this->inner; + } +} diff --git a/libcamera-bridge/frame_buffers.cpp b/libcamera-bridge/frame_buffers.cpp deleted file mode 100644 index aa30f5b..0000000 --- a/libcamera-bridge/frame_buffers.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "./core.hpp" - -#include "libcamera-rs/src/bridge.rs.h" - -std::unique_ptr -make_frame_buffer_allocator(const std::shared_ptr &cam) { - return std::make_unique(cam); -} - -unsigned int -allocate_frame_buffer_stream(const libcamera::FrameBufferAllocator &alloc, - libcamera::Stream &stream) { - int buffers = ((libcamera::FrameBufferAllocator &)alloc).allocate(&stream); - - if (buffers < 0) { - throw(CameraError)(-buffers); - } - - return (unsigned int)buffers; -} - -void add_request_buffer(libcamera::Request &req, libcamera::Stream &stream, - libcamera::FrameBuffer *buffer) { - req.addBuffer(&stream, buffer); -} - -size_t get_allocator_buffer_count(const libcamera::FrameBufferAllocator &alloc, - libcamera::Stream &stream) { - return alloc.buffers(&stream).size(); -} - -libcamera::FrameBuffer * -get_allocator_buffer(const libcamera::FrameBufferAllocator &alloc, - libcamera::Stream &stream, size_t idx) { - return alloc.buffers(&stream).at(idx).get(); -} diff --git a/libcamera-bridge/request.cpp b/libcamera-bridge/request.cpp new file mode 100644 index 0000000..093bbbf --- /dev/null +++ b/libcamera-bridge/request.cpp @@ -0,0 +1,20 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +#include + +void Request::add_buffer(Stream &stream, FrameBuffer &buffer) { + VALIDATE_POINTERS() + + int ret = this->inner->addBuffer(stream.into_ptr(), buffer.into_ptr()); + if (ret < 0) { + throw(BindErrorCode)(-ret); + } +} + +libcamera::Request *Request::into_ptr() { + VALIDATE_POINTERS() + + return this->inner.get(); +} diff --git a/libcamera-bridge/stream.cpp b/libcamera-bridge/stream.cpp new file mode 100644 index 0000000..79c1e9d --- /dev/null +++ b/libcamera-bridge/stream.cpp @@ -0,0 +1,9 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +libcamera::Stream *Stream::into_ptr() { + VALIDATE_POINTERS() + + return this->inner; +} diff --git a/libcamera-bridge/stream_configuration.cpp b/libcamera-bridge/stream_configuration.cpp new file mode 100644 index 0000000..95c2fd0 --- /dev/null +++ b/libcamera-bridge/stream_configuration.cpp @@ -0,0 +1,10 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +BindStream StreamConfiguration::stream() { + BindStream stream{ + .inner = std::make_unique(this->inner->stream()), + }; + return stream; +} diff --git a/src/bridge.rs b/src/bridge.rs index bd358fd..fc7aec6 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -1,4 +1,5 @@ -use cxx::SharedPtr; +#![allow(dead_code)] + use std::pin::Pin; #[cfg(test)] @@ -6,10 +7,6 @@ mod test; #[cxx::bridge] pub mod ffi { - struct BridgeCamera { - inner: SharedPtr, - } - #[namespace = "libcamera"] #[repr(i32)] #[derive(Debug)] @@ -40,7 +37,7 @@ pub mod ffi { #[repr(i32)] #[derive(Debug)] - enum CameraError { + enum BindErrorCode { /// Operation not permitted EPerm = 1, /// No such file or directory @@ -56,7 +53,7 @@ pub mod ffi { /// Argument list too long E2Big = 7, /// EXec format error - ENoexec = 8, + ENoExec = 8, /// Bad file number EBadF = 9, /// No child processes @@ -111,149 +108,148 @@ pub mod ffi { ERange = 34, } + struct BindCameraManager { + inner: UniquePtr, + } + struct BindCamera { + inner: UniquePtr, + } + struct BindCameraConfiguration { + inner: UniquePtr, + } + struct BindStreamConfiguration { + inner: UniquePtr, + } + struct BindStream { + inner: UniquePtr, + } + struct BindFrameBufferAllocator { + inner: UniquePtr, + } + struct BindFrameBuffer { + inner: UniquePtr, + } + struct BindRequest { + inner: UniquePtr, + } + unsafe extern "C++" { - include!("libcamera/stream.h"); - include!("libcamera/camera.h"); - include!("libcamera/framebuffer_allocator.h"); include!("libcamera-rs/libcamera-bridge/core.hpp"); - // Camera Manager - type CameraManager; - - pub fn make_camera_manager() -> UniquePtr; - pub fn start(self: Pin<&mut CameraManager>) -> Result<()>; - pub fn stop(self: Pin<&mut CameraManager>); - pub fn version(self: Pin<&mut CameraManager>) -> String; - pub fn cameras(self: &CameraManager) -> Vec; - pub fn get(self: Pin<&mut CameraManager>, id: &CxxString) -> SharedPtr; - - // Camera #[namespace = "libcamera"] - type Camera; - - pub fn get_mut_camera(cam: &mut SharedPtr) -> Pin<&mut Camera>; + type StreamRole; + type CameraConfigurationStatus; - pub fn id(self: &Camera) -> &CxxString; - #[must_use] - pub fn acquire(self: Pin<&mut Camera>) -> i32; - #[must_use] - pub fn release(self: Pin<&mut Camera>) -> i32; - #[must_use] - pub fn stop(self: Pin<&mut Camera>) -> i32; + type CameraManager; + pub fn make_camera_manager() -> BindCameraManager; - // FIXME: Safety - pub fn start_camera_with_controls( - cam: Pin<&mut Camera>, - control_list: Pin<&mut ControlList>, - ) -> Result<()>; - pub fn start_camera(cam: Pin<&mut Camera>) -> Result<()>; + pub unsafe fn start(self: Pin<&mut CameraManager>); + pub unsafe fn stop(self: Pin<&mut CameraManager>); + pub unsafe fn get_camera_ids(self: Pin<&mut CameraManager>) -> Vec; + pub unsafe fn get_camera_by_id(self: Pin<&mut CameraManager>, id: &str) -> BindCamera; - #[cxx_name = "createRequest"] - pub fn create_request(self: Pin<&mut Camera>, cookie: u64) -> UniquePtr; - pub fn queue_camera_request(cam: Pin<&mut Camera>, req: Pin<&mut Request>) -> Result<()>; + type Camera; + pub unsafe fn acquire(self: Pin<&mut Camera>); + pub unsafe fn release(self: Pin<&mut Camera>); + pub unsafe fn generate_configuration( + self: Pin<&mut Camera>, + roles: &[StreamRole], + ) -> BindCameraConfiguration; + pub unsafe fn configure(self: Pin<&mut Camera>, conf: Pin<&mut CameraConfiguration>); + pub unsafe fn create_request(self: Pin<&mut Camera>) -> BindRequest; + pub unsafe fn queue_request(self: Pin<&mut Camera>, req: Pin<&mut Request>); + pub unsafe fn start(self: Pin<&mut Camera>); + pub unsafe fn stop(self: Pin<&mut Camera>); - pub fn generate_camera_configuration( - cam: Pin<&mut Camera>, - roles: &Vec, - ) -> UniquePtr; + type CameraConfiguration; + pub unsafe fn at(self: Pin<&mut CameraConfiguration>, idx: u32) -> BindStreamConfiguration; + pub unsafe fn validate(self: Pin<&mut CameraConfiguration>) -> CameraConfigurationStatus; - pub fn configure_camera(cam: Pin<&mut Camera>, config: Pin<&mut CameraConfiguration>); + type StreamConfiguration; + pub unsafe fn stream(self: Pin<&mut StreamConfiguration>) -> BindStream; - pub fn connect_camera_buffer_completed( - cam: Pin<&mut Camera>, - callback: fn(request: &Request, frame_buffer: &FrameBuffer), - ); - pub fn connect_camera_request_completed(cam: Pin<&mut Camera>, callback: fn(request: &Request)); - pub fn connect_camera_disconnected(cam: Pin<&mut Camera>, callback: fn()); + type Stream; - // Frame Buffers - #[namespace = "libcamera"] type FrameBufferAllocator; + pub fn make_frame_buffer_allocator(camera: Pin<&mut Camera>) -> BindFrameBufferAllocator; - pub fn make_frame_buffer_allocator(cam: &SharedPtr) -> UniquePtr; - - pub fn allocate_frame_buffer_stream( - alloc: &FrameBufferAllocator, + pub unsafe fn allocate(self: Pin<&mut FrameBufferAllocator>, stream: Pin<&mut Stream>); + pub unsafe fn free(self: Pin<&mut FrameBufferAllocator>, stream: Pin<&mut Stream>); + pub unsafe fn buffers( + self: Pin<&mut FrameBufferAllocator>, stream: Pin<&mut Stream>, - ) -> Result; + ) -> Vec; - #[namespace = "libcamera"] - type Request; - - /// # Safety - /// TODO - pub unsafe fn add_request_buffer( - req: Pin<&mut Request>, - stream: Pin<&mut Stream>, - buffer: *mut FrameBuffer, - ); - - pub fn get_allocator_buffer_count( - alloc: &FrameBufferAllocator, - stream: Pin<&mut Stream>, - ) -> usize; + type FrameBuffer; - pub fn get_allocator_buffer( - alloc: &FrameBufferAllocator, + type Request; + pub unsafe fn add_buffer( + self: Pin<&mut Request>, stream: Pin<&mut Stream>, - idx: usize, - ) -> Result<*mut FrameBuffer>; - - #[namespace = "libcamera"] - type Stream; - - // Camera Configuration - #[namespace = "libcamera"] - type CameraConfiguration; - - pub fn at(self: Pin<&mut CameraConfiguration>, index: u32) -> Pin<&mut StreamConfiguration>; - pub fn validate(self: Pin<&mut CameraConfiguration>) -> CameraConfigurationStatus; - pub fn size(self: &CameraConfiguration) -> usize; - - type CameraConfigurationStatus; - - #[namespace = "libcamera"] - type StreamConfiguration; - - pub fn set_stream_pixel_format( - stream: Pin<&mut StreamConfiguration>, - format: Pin<&PixelFormat>, + buffer: Pin<&mut FrameBuffer>, ); - pub fn set_stream_size(stream: Pin<&mut StreamConfiguration>, width: u32, height: u32); - pub fn set_stream_buffer_count(stream: Pin<&mut StreamConfiguration>, buffer_count: u32); - - pub fn get_stream_from_configuration(conf: Pin<&mut StreamConfiguration>) -> Pin<&mut Stream>; - - #[namespace = "libcamera"] - type ControlList; - - pub fn new_control_list() -> UniquePtr; + } +} - // Misc. Types +/// # Safety +/// The inner pointer to the libcamera object must be valid. +unsafe trait PinMut { + type Inner; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner>; +} - #[namespace = "libcamera"] - type StreamRole; +unsafe impl PinMut for ffi::BindCameraManager { + type Inner = ffi::CameraManager; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} - #[namespace = "libcamera"] - type PixelFormat; +unsafe impl PinMut for ffi::BindCamera { + type Inner = ffi::Camera; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} - pub fn get_default_pixel_format(format: DefaultPixelFormat) -> Pin<&'static PixelFormat>; +unsafe impl PinMut for ffi::BindCameraConfiguration { + type Inner = ffi::CameraConfiguration; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} - #[namespace = "libcamera"] - type FrameBuffer; +unsafe impl PinMut for ffi::BindStreamConfiguration { + type Inner = ffi::StreamConfiguration; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() } } -pub trait MutFromSharedPtr { - type Target; +unsafe impl PinMut for ffi::BindStream { + type Inner = ffi::Stream; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} - fn pin_mut(&mut self) -> Pin<&mut Self::Target>; +unsafe impl PinMut for ffi::BindFrameBufferAllocator { + type Inner = ffi::FrameBufferAllocator; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } } -impl MutFromSharedPtr for SharedPtr { - type Target = ffi::Camera; +unsafe impl PinMut for ffi::BindFrameBuffer { + type Inner = ffi::FrameBuffer; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} - fn pin_mut(&mut self) -> Pin<&mut Self::Target> { - ffi::get_mut_camera(self) +unsafe impl PinMut for ffi::BindRequest { + type Inner = ffi::Request; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() } } diff --git a/src/bridge/test.rs b/src/bridge/test.rs index cae84d7..01a2bca 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -1,103 +1,46 @@ -use crate::bridge::MutFromSharedPtr; -use crate::ffi; +use crate::bridge::ffi; +use crate::bridge::PinMut; #[test] fn it_works() { - let mut manager = ffi::make_camera_manager(); - manager.pin_mut().version(); -} - -#[test] -fn generate_configuration() { - let mut manager = ffi::make_camera_manager(); - manager.pin_mut().start().unwrap(); - - let mut cameras = manager.pin_mut().cameras(); - let camera = &mut cameras[0]; + let mut cm = ffi::make_camera_manager(); + unsafe { cm.get().start() }; + let camera_ids = unsafe { cm.get().get_camera_ids() }; + println!("Available Cameras: {camera_ids:?}"); + let mut camera = unsafe { cm.get().get_camera_by_id(&camera_ids[0]) }; - let roles = vec![ffi::StreamRole::StillCapture]; + unsafe { camera.get().acquire() }; - let mut config = ffi::generate_camera_configuration(camera.inner.pin_mut(), &roles); + let mut config = unsafe { + camera + .get() + .generate_configuration(&[ffi::StreamRole::Viewfinder]) + }; - assert_eq!( - config.pin_mut().validate(), - ffi::CameraConfigurationStatus::Valid - ); -} - -#[test] -/// Test initializing camera -/// # Safety -/// Always segfaults. -fn init_camera() { - let mut manager = ffi::make_camera_manager(); - manager.pin_mut().start().unwrap(); + unsafe { camera.get().configure(config.get()) }; - let mut cameras = manager.pin_mut().cameras(); - let camera = &mut cameras[0]; + let mut stream_config = unsafe { config.get().at(0) }; - assert_eq!(camera.inner.pin_mut().acquire(), 0,); + let mut allocator = unsafe { ffi::make_frame_buffer_allocator(camera.get()) }; - let roles = vec![ffi::StreamRole::Viewfinder]; - let mut config = ffi::generate_camera_configuration(camera.inner.pin_mut(), &roles); + let mut stream = unsafe { stream_config.get().stream() }; - ffi::set_stream_pixel_format( - config.pin_mut().at(0), - ffi::get_default_pixel_format(ffi::DefaultPixelFormat::Rgb888), - ); - ffi::set_stream_size(config.pin_mut().at(0), 640, 480); - ffi::set_stream_buffer_count(config.pin_mut().at(0), 4); - - assert_ne!( - config.pin_mut().validate(), - ffi::CameraConfigurationStatus::Invalid - ); - - ffi::configure_camera(camera.inner.pin_mut(), config.pin_mut()); - - // Allocate and map buffers - let allocator = ffi::make_frame_buffer_allocator(&camera.inner); - let stream_config_count = config.pin_mut().size() as u32; - assert_eq!(stream_config_count, 1); - let buffer_count = ffi::allocate_frame_buffer_stream( - allocator.as_ref().unwrap(), - ffi::get_stream_from_configuration(config.pin_mut().at(0)), - ) - .unwrap(); - - assert_eq!(buffer_count, 4); + unsafe { allocator.get().allocate(stream.get()) }; + let mut requests = Vec::new(); + for mut buffer in unsafe { allocator.get().buffers(stream.get()) } { + let mut request = unsafe { camera.get().create_request() }; + unsafe { request.get().add_buffer(stream.get(), buffer.get()) }; + requests.push(request); + } - ffi::start_camera(camera.inner.pin_mut()).unwrap(); + unsafe { camera.get().start() }; - let buffer_count = ffi::get_allocator_buffer_count( - &allocator, - ffi::get_stream_from_configuration(config.pin_mut().at(0)), - ); - for buffer_id in 0..buffer_count { - let buffer = ffi::get_allocator_buffer( - &allocator, - ffi::get_stream_from_configuration(config.pin_mut().at(0)), - buffer_id, - ) - .unwrap(); - let mut request = camera.inner.pin_mut().create_request(0); - unsafe { - println!("Buf: {buffer:?}"); - ffi::add_request_buffer( - request.pin_mut(), - ffi::get_stream_from_configuration(config.pin_mut().at(0)), - buffer, - ); - } - // ffi::queue_camera_request(camera.inner.pin_mut(), request.pin_mut()).unwrap(); + for request in &mut requests { + unsafe { camera.get().queue_request(request.get()) }; } - ffi::connect_camera_request_completed(camera.inner.pin_mut(), |_req| { - eprintln!("Request Completed"); - }); - - std::thread::sleep(std::time::Duration::from_millis(10000)); + std::thread::sleep(std::time::Duration::from_millis(1000)); - assert_eq!(camera.inner.pin_mut().stop(), 0); - assert_eq!(camera.inner.pin_mut().release(), 0); + unsafe { camera.get().stop() }; + unsafe { camera.get().release() }; } diff --git a/src/lib.rs b/src/lib.rs index 3ce1880..d974ceb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,3 @@ -mod bridge; +#![deny(clippy::all)] -pub use bridge::ffi; +mod bridge; From 097148aeddcc086d1907c90cb297db550060678a Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Tue, 21 Jun 2022 16:35:03 -0600 Subject: [PATCH 09/49] feat: Capture images --- build.rs | 3 ++ libcamera-bridge/core.hpp | 37 ++++++++++++++++++++ libcamera-bridge/fd.cpp | 18 ++++++++++ libcamera-bridge/frame_buffer.cpp | 14 ++++++++ libcamera-bridge/frame_buffer_plane.cpp | 21 +++++++++++ libcamera-bridge/memory_buffer.cpp | 20 +++++++++++ src/bridge.rs | 38 ++++++++++++++++++++ src/bridge/test.rs | 46 +++++++++++++++++++++++++ 8 files changed, 197 insertions(+) create mode 100644 libcamera-bridge/fd.cpp create mode 100644 libcamera-bridge/frame_buffer_plane.cpp create mode 100644 libcamera-bridge/memory_buffer.cpp diff --git a/build.rs b/build.rs index 1744b5e..084c68a 100644 --- a/build.rs +++ b/build.rs @@ -7,6 +7,9 @@ fn main() { .file("libcamera-bridge/stream.cpp") .file("libcamera-bridge/frame_buffer_allocator.cpp") .file("libcamera-bridge/frame_buffer.cpp") + .file("libcamera-bridge/frame_buffer_plane.cpp") + .file("libcamera-bridge/fd.cpp") + .file("libcamera-bridge/memory_buffer.cpp") .file("libcamera-bridge/request.cpp") .flag_if_supported("-std=c++17") .include("/usr/local/include/libcamera") diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index e2b0cb2..636f56f 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -17,6 +17,9 @@ struct BindStreamConfiguration; struct BindStream; struct BindFrameBufferAllocator; struct BindFrameBuffer; +struct BindFrameBufferPlane; +struct BindFd; +struct BindMemoryBuffer; struct BindRequest; struct CameraConfiguration; @@ -121,6 +124,40 @@ struct FrameBuffer { public: FrameBuffer(libcamera::FrameBuffer *inner_) : inner(inner_) {} libcamera::FrameBuffer *into_ptr(); + rust::Vec planes(); +}; + +size_t fd_len(int fd); + +struct FrameBufferPlane { +private: + const libcamera::FrameBuffer::Plane *inner; + +public: + FrameBufferPlane(const libcamera::FrameBuffer::Plane *inner_) + : inner(inner_) {} + + int get_fd(); + size_t get_offset(); + size_t get_length(); +}; + +// File descriptor functions + +size_t fd_len(int fd); +BindMemoryBuffer mmap_plane(int fd, size_t len); + +struct MemoryBuffer { +private: + const unsigned char *pointer; + size_t length; + +public: + MemoryBuffer(const unsigned char *pointer_, size_t length_) + : pointer(pointer_), length(length_) {} + + BindMemoryBuffer sub_buffer(size_t offset, size_t length); + rust::Vec read_to_vec(); }; struct Request { diff --git a/libcamera-bridge/fd.cpp b/libcamera-bridge/fd.cpp new file mode 100644 index 0000000..7242f20 --- /dev/null +++ b/libcamera-bridge/fd.cpp @@ -0,0 +1,18 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +#include +#include + +size_t fd_len(int fd) { return lseek(fd, 0, SEEK_END); } + +BindMemoryBuffer mmap_plane(int fd, size_t len) { + void *address = mmap(nullptr, len, PROT_READ, MAP_SHARED, fd, 0); + if (!address || address == MAP_FAILED) { + throw(BindErrorCode) errno; + } + BindMemoryBuffer buffer{.inner = std::make_unique( + static_cast(address), len)}; + return buffer; +} diff --git a/libcamera-bridge/frame_buffer.cpp b/libcamera-bridge/frame_buffer.cpp index e3034b9..aaf5ac6 100644 --- a/libcamera-bridge/frame_buffer.cpp +++ b/libcamera-bridge/frame_buffer.cpp @@ -9,3 +9,17 @@ libcamera::FrameBuffer *FrameBuffer::into_ptr() { return this->inner; } + +rust::Vec FrameBuffer::planes() { + VALIDATE_POINTERS() + + rust::Vec vec; + for (const libcamera::FrameBuffer::Plane &plane : this->inner->planes()) { + BindFrameBufferPlane bind_plane{ + .inner = std::make_unique(&plane), + }; + + vec.push_back(std::move(bind_plane)); + } + return vec; +} diff --git a/libcamera-bridge/frame_buffer_plane.cpp b/libcamera-bridge/frame_buffer_plane.cpp new file mode 100644 index 0000000..ab4e460 --- /dev/null +++ b/libcamera-bridge/frame_buffer_plane.cpp @@ -0,0 +1,21 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +int FrameBufferPlane::get_fd() { + VALIDATE_POINTERS() + + return this->inner->fd.get(); +} + +size_t FrameBufferPlane::get_offset() { + VALIDATE_POINTERS() + + return (size_t)this->inner->offset; +} + +size_t FrameBufferPlane::get_length() { + VALIDATE_POINTERS() + + return (size_t)this->inner->length; +} diff --git a/libcamera-bridge/memory_buffer.cpp b/libcamera-bridge/memory_buffer.cpp new file mode 100644 index 0000000..4a334df --- /dev/null +++ b/libcamera-bridge/memory_buffer.cpp @@ -0,0 +1,20 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +BindMemoryBuffer MemoryBuffer::sub_buffer(size_t offset, size_t length) { + if (offset > this->length || offset + length > this->length) { + throw BindErrorCode::EFault; + } + BindMemoryBuffer buffer{ + .inner = std::make_unique(this->pointer + offset, length)}; + return buffer; +} + +rust::Vec MemoryBuffer::read_to_vec() { + rust::Vec buf; + for (size_t i = 0; i < this->length; i++) { + buf.push_back(this->pointer[i]); + } + return buf; +} diff --git a/src/bridge.rs b/src/bridge.rs index fc7aec6..9f60b32 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -129,6 +129,12 @@ pub mod ffi { struct BindFrameBuffer { inner: UniquePtr, } + struct BindFrameBufferPlane { + inner: UniquePtr, + } + struct BindMemoryBuffer { + inner: UniquePtr, + } struct BindRequest { inner: UniquePtr, } @@ -181,6 +187,24 @@ pub mod ffi { ) -> Vec; type FrameBuffer; + pub unsafe fn planes(self: Pin<&mut FrameBuffer>) -> Vec; + + type FrameBufferPlane; + pub unsafe fn get_fd(self: Pin<&mut FrameBufferPlane>) -> i32; + pub unsafe fn get_offset(self: Pin<&mut FrameBufferPlane>) -> usize; + pub unsafe fn get_length(self: Pin<&mut FrameBufferPlane>) -> usize; + + /// File descriptor functions + pub unsafe fn fd_len(fd: i32) -> usize; + pub unsafe fn mmap_plane(fd: i32, length: usize) -> BindMemoryBuffer; + + type MemoryBuffer; + pub unsafe fn sub_buffer( + self: Pin<&mut MemoryBuffer>, + offset: usize, + length: usize, + ) -> BindMemoryBuffer; + pub unsafe fn read_to_vec(self: Pin<&mut MemoryBuffer>) -> Vec; type Request; pub unsafe fn add_buffer( @@ -247,6 +271,20 @@ unsafe impl PinMut for ffi::BindFrameBuffer { } } +unsafe impl PinMut for ffi::BindFrameBufferPlane { + type Inner = ffi::FrameBufferPlane; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} + +unsafe impl PinMut for ffi::BindMemoryBuffer { + type Inner = ffi::MemoryBuffer; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} + unsafe impl PinMut for ffi::BindRequest { type Inner = ffi::Request; unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 01a2bca..f84b30d 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -1,6 +1,8 @@ use crate::bridge::ffi; use crate::bridge::PinMut; +use std::collections::HashMap; + #[test] fn it_works() { let mut cm = ffi::make_camera_manager(); @@ -27,8 +29,45 @@ fn it_works() { unsafe { allocator.get().allocate(stream.get()) }; let mut requests = Vec::new(); + let mut planes = Vec::new(); for mut buffer in unsafe { allocator.get().buffers(stream.get()) } { let mut request = unsafe { camera.get().create_request() }; + + let mut mapped_buffers: HashMap, usize, usize)> = + HashMap::new(); + for mut plane in unsafe { buffer.get().planes() } { + let fd = unsafe { plane.get().get_fd() }; + let length = mapped_buffers + .entry(fd) + .or_insert((None, 0, unsafe { ffi::fd_len(fd) })) + .2; + if unsafe { plane.get().get_offset() } + unsafe { plane.get().get_length() } > length { + panic!( + "Plane is out of buffer: buffer length = {length}, plane offset = {}, plane length = {}", + unsafe { plane.get().get_offset() }, + unsafe { plane.get().get_length() } + ); + } + let map_len = mapped_buffers[&fd].1; + mapped_buffers.get_mut(&fd).unwrap().1 = + map_len.max(unsafe { plane.get().get_offset() } + unsafe { plane.get().get_length() }); + } + for mut plane in unsafe { buffer.get().planes() } { + let fd = unsafe { plane.get().get_fd() }; + let mapped_buffer = mapped_buffers.get_mut(&fd).unwrap(); + if mapped_buffer.0.is_none() { + mapped_buffer.0 = Some(unsafe { ffi::mmap_plane(fd, mapped_buffer.1) }); + } + planes.push(unsafe { + mapped_buffer + .0 + .as_mut() + .unwrap() + .get() + .sub_buffer(plane.get().get_offset(), plane.get().get_length()) + }); + } + unsafe { request.get().add_buffer(stream.get(), buffer.get()) }; requests.push(request); } @@ -41,6 +80,13 @@ fn it_works() { std::thread::sleep(std::time::Duration::from_millis(1000)); + for (i, plane) in planes.iter_mut().enumerate() { + std::fs::write(&format!("plane_{i}.jpeg"), unsafe { + plane.get().read_to_vec() + }) + .unwrap(); + } + unsafe { camera.get().stop() }; unsafe { camera.get().release() }; } From 6ea5b097f40f0a8cabbeaca8eb29173767ee622e Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 22 Jun 2022 11:13:26 -0600 Subject: [PATCH 10/49] feat: Camera Configuration --- build.rs | 2 + libcamera-bridge/core.hpp | 42 +++++++++++++++++ libcamera-bridge/frame_buffer.cpp | 2 - libcamera-bridge/pixel_format.cpp | 55 +++++++++++++++++++++++ libcamera-bridge/request.cpp | 4 +- libcamera-bridge/size.cpp | 22 +++++++++ libcamera-bridge/stream_configuration.cpp | 33 ++++++++++++++ src/bridge.rs | 41 +++++++++++++++++ src/bridge/test.rs | 20 +++++++++ 9 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 libcamera-bridge/pixel_format.cpp create mode 100644 libcamera-bridge/size.cpp diff --git a/build.rs b/build.rs index 084c68a..90715a7 100644 --- a/build.rs +++ b/build.rs @@ -4,6 +4,8 @@ fn main() { .file("libcamera-bridge/camera.cpp") .file("libcamera-bridge/camera_configuration.cpp") .file("libcamera-bridge/stream_configuration.cpp") + .file("libcamera-bridge/pixel_format.cpp") + .file("libcamera-bridge/size.cpp") .file("libcamera-bridge/stream.cpp") .file("libcamera-bridge/frame_buffer_allocator.cpp") .file("libcamera-bridge/frame_buffer.cpp") diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 636f56f..8bfe245 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -14,6 +14,8 @@ struct BindCameraManager; struct BindCamera; struct BindCameraConfiguration; struct BindStreamConfiguration; +struct BindPixelFormat; +struct BindSize; struct BindStream; struct BindFrameBufferAllocator; struct BindFrameBuffer; @@ -23,7 +25,10 @@ struct BindMemoryBuffer; struct BindRequest; struct CameraConfiguration; +struct PixelFormat; +struct Size; struct Request; +enum class DefaultPixelFormat; using CameraConfigurationStatus = libcamera::CameraConfiguration::Status; @@ -90,6 +95,42 @@ struct StreamConfiguration { StreamConfiguration(libcamera::StreamConfiguration *inner_) : inner(inner_) {} BindStream stream(); + void set_pixel_format(BindPixelFormat pixel_format); + BindPixelFormat get_pixel_format(); + void set_size(BindSize size); + BindSize get_size(); + rust::String to_string(); +}; + +BindPixelFormat get_default_pixel_format(DefaultPixelFormat default_format); + +struct PixelFormat { +private: + libcamera::PixelFormat inner; + +public: + PixelFormat(libcamera::PixelFormat inner_) : inner(inner_) {} + libcamera::PixelFormat into_inner(); + + rust::String to_string(); +}; + +BindSize new_size(unsigned int width, unsigned int height); + +struct Size { +private: + libcamera::Size inner; + +public: + Size(libcamera::Size inner_) : inner(inner_) {} + libcamera::Size into_inner(); + + unsigned int get_width(); + unsigned int get_height(); + void set_width(unsigned int width); + void set_height(unsigned int height); + + rust::String to_string(); }; struct Stream { @@ -170,4 +211,5 @@ struct Request { libcamera::Request *into_ptr(); void add_buffer(Stream &stream, FrameBuffer &buffer); + rust::String to_string(); }; diff --git a/libcamera-bridge/frame_buffer.cpp b/libcamera-bridge/frame_buffer.cpp index aaf5ac6..3de76dc 100644 --- a/libcamera-bridge/frame_buffer.cpp +++ b/libcamera-bridge/frame_buffer.cpp @@ -2,8 +2,6 @@ #include "libcamera-rs/src/bridge.rs.h" -#include - libcamera::FrameBuffer *FrameBuffer::into_ptr() { VALIDATE_POINTERS() diff --git a/libcamera-bridge/pixel_format.cpp b/libcamera-bridge/pixel_format.cpp new file mode 100644 index 0000000..2369856 --- /dev/null +++ b/libcamera-bridge/pixel_format.cpp @@ -0,0 +1,55 @@ +#include "core.hpp" + +#include + +#include "libcamera-rs/src/bridge.rs.h" + +BindPixelFormat get_default_pixel_format(DefaultPixelFormat default_format) { + const libcamera::PixelFormat *fmt; + switch (default_format) { + case DefaultPixelFormat::R8: + fmt = &libcamera::formats::R8; + break; + case DefaultPixelFormat::Rgb888: + fmt = &libcamera::formats::RGB888; + break; + case DefaultPixelFormat::Rgb565: + fmt = &libcamera::formats::RGB565; + break; + case DefaultPixelFormat::Bgr888: + fmt = &libcamera::formats::BGR888; + break; + case DefaultPixelFormat::Yuyv: + fmt = &libcamera::formats::YUYV; + break; + case DefaultPixelFormat::Yvyu: + fmt = &libcamera::formats::YVYU; + break; + case DefaultPixelFormat::Yuv420: + fmt = &libcamera::formats::YUV420; + break; + case DefaultPixelFormat::Yuv422: + fmt = &libcamera::formats::YUV422; + break; + case DefaultPixelFormat::Yvu422: + fmt = &libcamera::formats::YVU422; + break; + case DefaultPixelFormat::Yuv444: + fmt = &libcamera::formats::YUV444; + break; + case DefaultPixelFormat::Mjpeg: + fmt = &libcamera::formats::MJPEG; + break; + } + if (!fmt) { + throw BindErrorCode::EFault; + } + BindPixelFormat pixel_format{ + .inner = std::make_unique(*fmt), + }; + return pixel_format; +} + +libcamera::PixelFormat PixelFormat::into_inner() { return this->inner; } + +rust::String PixelFormat::to_string() { return this->inner.toString(); } diff --git a/libcamera-bridge/request.cpp b/libcamera-bridge/request.cpp index 093bbbf..7ae9abe 100644 --- a/libcamera-bridge/request.cpp +++ b/libcamera-bridge/request.cpp @@ -2,8 +2,6 @@ #include "libcamera-rs/src/bridge.rs.h" -#include - void Request::add_buffer(Stream &stream, FrameBuffer &buffer) { VALIDATE_POINTERS() @@ -18,3 +16,5 @@ libcamera::Request *Request::into_ptr() { return this->inner.get(); } + +rust::String Request::to_string() { return this->inner->toString(); } diff --git a/libcamera-bridge/size.cpp b/libcamera-bridge/size.cpp new file mode 100644 index 0000000..6c3a4b1 --- /dev/null +++ b/libcamera-bridge/size.cpp @@ -0,0 +1,22 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +BindSize new_size(unsigned int width, unsigned int height) { + BindSize size{ + .inner = std::make_unique(libcamera::Size(width, height)), + }; + return size; +} + +libcamera::Size Size::into_inner() { return this->inner; } + +unsigned int Size::get_width() { return this->inner.width; } + +unsigned int Size::get_height() { return this->inner.height; } + +void Size::set_width(unsigned int width) { this->inner.width = width; } + +void Size::set_height(unsigned int height) { this->inner.height = height; } + +rust::String Size::to_string() { return this->inner.toString(); } diff --git a/libcamera-bridge/stream_configuration.cpp b/libcamera-bridge/stream_configuration.cpp index 95c2fd0..3d0ccd5 100644 --- a/libcamera-bridge/stream_configuration.cpp +++ b/libcamera-bridge/stream_configuration.cpp @@ -8,3 +8,36 @@ BindStream StreamConfiguration::stream() { }; return stream; } + +void StreamConfiguration::set_pixel_format(BindPixelFormat pixel_format) { + VALIDATE_POINTERS() + + this->inner->pixelFormat = pixel_format.inner->into_inner(); +} + +BindPixelFormat StreamConfiguration::get_pixel_format() { + VALIDATE_POINTERS() + + BindPixelFormat pixel_format{ + .inner = std::make_unique(this->inner->pixelFormat), + }; + return pixel_format; +} + +void StreamConfiguration::set_size(BindSize size) { + + this->inner->size = size.inner->into_inner(); +} + +BindSize StreamConfiguration::get_size() { + VALIDATE_POINTERS() + + BindSize size{ + .inner = std::make_unique(this->inner->size), + }; + return size; +} + +rust::String StreamConfiguration::to_string() { + return this->inner->toString(); +} diff --git a/src/bridge.rs b/src/bridge.rs index 9f60b32..cebf490 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -29,9 +29,16 @@ pub mod ffi { #[repr(i32)] #[derive(Debug)] enum DefaultPixelFormat { + R8, Rgb888, + Rgb565, Bgr888, + Yuyv, + Yvyu, Yuv420, + Yuv422, + Yvu422, + Yuv444, Mjpeg, } @@ -117,6 +124,12 @@ pub mod ffi { struct BindCameraConfiguration { inner: UniquePtr, } + struct BindPixelFormat { + inner: UniquePtr, + } + struct BindSize { + inner: UniquePtr, + } struct BindStreamConfiguration { inner: UniquePtr, } @@ -173,6 +186,20 @@ pub mod ffi { type StreamConfiguration; pub unsafe fn stream(self: Pin<&mut StreamConfiguration>) -> BindStream; + pub unsafe fn set_pixel_format( + self: Pin<&mut StreamConfiguration>, + pixel_format: BindPixelFormat, + ); + pub unsafe fn get_pixel_format(self: Pin<&mut StreamConfiguration>) -> BindPixelFormat; + pub unsafe fn set_size(self: Pin<&mut StreamConfiguration>, size: BindSize); + pub unsafe fn get_size(self: Pin<&mut StreamConfiguration>) -> BindSize; + pub unsafe fn to_string(self: Pin<&mut StreamConfiguration>) -> String; + + type PixelFormat; + pub fn get_default_pixel_format(default_format: DefaultPixelFormat) -> BindPixelFormat; + + type Size; + pub fn new_size(width: u32, height: u32) -> BindSize; type Stream; @@ -250,6 +277,20 @@ unsafe impl PinMut for ffi::BindStreamConfiguration { } } +unsafe impl PinMut for ffi::BindPixelFormat { + type Inner = ffi::PixelFormat; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} + +unsafe impl PinMut for ffi::BindSize { + type Inner = ffi::Size; + unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} + unsafe impl PinMut for ffi::BindStream { type Inner = ffi::Stream; unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { diff --git a/src/bridge/test.rs b/src/bridge/test.rs index f84b30d..509dd6b 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -19,6 +19,26 @@ fn it_works() { .generate_configuration(&[ffi::StreamRole::Viewfinder]) }; + let mut stream_config = unsafe { config.get().at(0) }; + + unsafe { + stream_config + .get() + .set_pixel_format(ffi::get_default_pixel_format( + ffi::DefaultPixelFormat::Yuv422, + )) + }; + + unsafe { stream_config.get().set_size(ffi::new_size(640, 480)) }; + + let status = unsafe { config.get().validate() }; + if status == ffi::CameraConfigurationStatus::Invalid { + panic!("Invalid Camera Configuration!"); + } + if status == ffi::CameraConfigurationStatus::Adjusted { + println!("Camera Configuration Adjusted."); + } + unsafe { camera.get().configure(config.get()) }; let mut stream_config = unsafe { config.get().at(0) }; From d0411c2c607010eeeb1fa0040b355306dfbd3d48 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 29 Jun 2022 14:52:46 -0600 Subject: [PATCH 11/49] chore: setup clang-tidy --- .clang-tidy | 13 +++++ .gitignore | 2 + libcamera-bridge/camera_configuration.cpp | 2 +- libcamera-bridge/core.hpp | 8 +-- libcamera-bridge/fd.cpp | 12 ++++- libcamera-bridge/frame_buffer_allocator.cpp | 10 ++-- libcamera-bridge/memory_buffer.cpp | 6 +++ libcamera-bridge/pixel_format.cpp | 6 ++- libcamera-bridge/size.cpp | 4 +- libcamera-bridge/stream_configuration.cpp | 12 +++++ makefile | 3 ++ src/bridge.rs | 48 +++++++++++------ src/bridge/test.rs | 58 ++++++++++++--------- 13 files changed, 125 insertions(+), 59 deletions(-) create mode 100644 .clang-tidy diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..69699a5 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,13 @@ +Checks: " + -checks=clang-diagnostic-*, + clang-analyzer-*, + modernize-*, + cppcoreguidelines-*, + readability-*, + portability-*, + bugprone-*, + llvm-*, + cert-*, + hicpp-*, + -hicpp-exception-baseclass, + -modernize-use-trailing-return-type" diff --git a/.gitignore b/.gitignore index 0115f2b..c7c6b0e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ /target Cargo.lock + +plane_*.jpeg diff --git a/libcamera-bridge/camera_configuration.cpp b/libcamera-bridge/camera_configuration.cpp index 834d04f..0c3577e 100644 --- a/libcamera-bridge/camera_configuration.cpp +++ b/libcamera-bridge/camera_configuration.cpp @@ -12,7 +12,7 @@ BindStreamConfiguration CameraConfiguration::at(unsigned int idx) { VALIDATE_POINTERS() libcamera::StreamConfiguration *str = &this->inner->at(idx); - if (!str) { + if (str == nullptr) { throw(BindErrorCode) ENODEV; } BindStreamConfiguration conf{ diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 8bfe245..57e4a48 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -99,6 +99,8 @@ struct StreamConfiguration { BindPixelFormat get_pixel_format(); void set_size(BindSize size); BindSize get_size(); + void set_buffer_count(size_t buffer_count); + size_t get_buffer_count(); rust::String to_string(); }; @@ -125,8 +127,8 @@ struct Size { Size(libcamera::Size inner_) : inner(inner_) {} libcamera::Size into_inner(); - unsigned int get_width(); - unsigned int get_height(); + unsigned int get_width() const; + unsigned int get_height() const; void set_width(unsigned int width); void set_height(unsigned int height); @@ -153,7 +155,7 @@ struct FrameBufferAllocator { : inner(inner_) {} ~FrameBufferAllocator(); - void allocate(Stream &stream); + size_t allocate(Stream &stream); void free(Stream &stream); rust::Vec buffers(Stream &stream); }; diff --git a/libcamera-bridge/fd.cpp b/libcamera-bridge/fd.cpp index 7242f20..0e44400 100644 --- a/libcamera-bridge/fd.cpp +++ b/libcamera-bridge/fd.cpp @@ -5,11 +5,19 @@ #include #include -size_t fd_len(int fd) { return lseek(fd, 0, SEEK_END); } +size_t fd_len(int fd) { + long ret = lseek(fd, 0, SEEK_END); + if (ret < 0) { + throw(BindErrorCode) errno; + } + return ret; +} BindMemoryBuffer mmap_plane(int fd, size_t len) { void *address = mmap(nullptr, len, PROT_READ, MAP_SHARED, fd, 0); - if (!address || address == MAP_FAILED) { + // MAP_FAILED expands to a silly C-style cast. + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + if ((address == nullptr) || address == MAP_FAILED) { throw(BindErrorCode) errno; } BindMemoryBuffer buffer{.inner = std::make_unique( diff --git a/libcamera-bridge/frame_buffer_allocator.cpp b/libcamera-bridge/frame_buffer_allocator.cpp index 398401c..c366368 100644 --- a/libcamera-bridge/frame_buffer_allocator.cpp +++ b/libcamera-bridge/frame_buffer_allocator.cpp @@ -10,13 +10,15 @@ BindFrameBufferAllocator make_frame_buffer_allocator(Camera &camera) { return allocator; } -void FrameBufferAllocator::allocate(Stream &stream) { +size_t FrameBufferAllocator::allocate(Stream &stream) { VALIDATE_POINTERS() int ret = this->inner->allocate(stream.into_ptr()); if (ret < 0) { throw(BindErrorCode)(-ret); } + + return ret; } void FrameBufferAllocator::free(Stream &stream) { @@ -43,8 +45,4 @@ rust::Vec FrameBufferAllocator::buffers(Stream &stream) { return vec; } -FrameBufferAllocator::~FrameBufferAllocator() { - if (this->inner) { - delete this->inner; - } -} +FrameBufferAllocator::~FrameBufferAllocator() { delete this->inner; } diff --git a/libcamera-bridge/memory_buffer.cpp b/libcamera-bridge/memory_buffer.cpp index 4a334df..4310cb8 100644 --- a/libcamera-bridge/memory_buffer.cpp +++ b/libcamera-bridge/memory_buffer.cpp @@ -4,9 +4,13 @@ BindMemoryBuffer MemoryBuffer::sub_buffer(size_t offset, size_t length) { if (offset > this->length || offset + length > this->length) { + // The thrown error is an int and so is copyable. + // NOLINTNEXTLINE(cert-err09-cpp,cert-err61-cpp) throw BindErrorCode::EFault; } BindMemoryBuffer buffer{ + // The point of this class is to safely wrap raw memory pointers. + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) .inner = std::make_unique(this->pointer + offset, length)}; return buffer; } @@ -14,6 +18,8 @@ BindMemoryBuffer MemoryBuffer::sub_buffer(size_t offset, size_t length) { rust::Vec MemoryBuffer::read_to_vec() { rust::Vec buf; for (size_t i = 0; i < this->length; i++) { + // The point of this class is to safely wrap raw memory pointers. + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) buf.push_back(this->pointer[i]); } return buf; diff --git a/libcamera-bridge/pixel_format.cpp b/libcamera-bridge/pixel_format.cpp index 2369856..54c50a7 100644 --- a/libcamera-bridge/pixel_format.cpp +++ b/libcamera-bridge/pixel_format.cpp @@ -5,7 +5,7 @@ #include "libcamera-rs/src/bridge.rs.h" BindPixelFormat get_default_pixel_format(DefaultPixelFormat default_format) { - const libcamera::PixelFormat *fmt; + const libcamera::PixelFormat *fmt = nullptr; switch (default_format) { case DefaultPixelFormat::R8: fmt = &libcamera::formats::R8; @@ -41,7 +41,9 @@ BindPixelFormat get_default_pixel_format(DefaultPixelFormat default_format) { fmt = &libcamera::formats::MJPEG; break; } - if (!fmt) { + if (fmt == nullptr) { + // The thrown error is an int and so is copyable. + // NOLINTNEXTLINE(cert-err09-cpp,cert-err61-cpp) throw BindErrorCode::EFault; } BindPixelFormat pixel_format{ diff --git a/libcamera-bridge/size.cpp b/libcamera-bridge/size.cpp index 6c3a4b1..6f4ebe4 100644 --- a/libcamera-bridge/size.cpp +++ b/libcamera-bridge/size.cpp @@ -11,9 +11,9 @@ BindSize new_size(unsigned int width, unsigned int height) { libcamera::Size Size::into_inner() { return this->inner; } -unsigned int Size::get_width() { return this->inner.width; } +unsigned int Size::get_width() const { return this->inner.width; } -unsigned int Size::get_height() { return this->inner.height; } +unsigned int Size::get_height() const { return this->inner.height; } void Size::set_width(unsigned int width) { this->inner.width = width; } diff --git a/libcamera-bridge/stream_configuration.cpp b/libcamera-bridge/stream_configuration.cpp index 3d0ccd5..a05f8f7 100644 --- a/libcamera-bridge/stream_configuration.cpp +++ b/libcamera-bridge/stream_configuration.cpp @@ -38,6 +38,18 @@ BindSize StreamConfiguration::get_size() { return size; } +void StreamConfiguration::set_buffer_count(size_t buffer_count) { + VALIDATE_POINTERS() + + this->inner->bufferCount = buffer_count; +} + +size_t StreamConfiguration::get_buffer_count() { + VALIDATE_POINTERS() + + return this->inner->bufferCount; +} + rust::String StreamConfiguration::to_string() { return this->inner->toString(); } diff --git a/makefile b/makefile index 31c40d8..00b00da 100644 --- a/makefile +++ b/makefile @@ -3,10 +3,13 @@ chk: check lint: check check: cargo clippy + cppcheck -q libcamera-bridge/*.hpp libcamera-bridge/*.cpp + clang-tidy --format-style=file libcamera-bridge/*.cpp -- -I/usr/local/include/libcamera -I./target/cxxbridge -I.. --std=c++17 cargo test -- --test-threads=1 f: format fmt: format format: + clang-tidy --format-style=file --fix --fix-errors --fix-notes libcamera-bridge/*.cpp -- -I/usr/local/include/libcamera -I./target/cxxbridge -I.. --std=c++17 clang-format -style=file -i libcamera-bridge/* cargo fmt diff --git a/src/bridge.rs b/src/bridge.rs index cebf490..c39061c 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -162,26 +162,32 @@ pub mod ffi { type CameraManager; pub fn make_camera_manager() -> BindCameraManager; - pub unsafe fn start(self: Pin<&mut CameraManager>); + pub unsafe fn start(self: Pin<&mut CameraManager>) -> Result<()>; pub unsafe fn stop(self: Pin<&mut CameraManager>); pub unsafe fn get_camera_ids(self: Pin<&mut CameraManager>) -> Vec; - pub unsafe fn get_camera_by_id(self: Pin<&mut CameraManager>, id: &str) -> BindCamera; + pub unsafe fn get_camera_by_id(self: Pin<&mut CameraManager>, id: &str) -> Result; type Camera; - pub unsafe fn acquire(self: Pin<&mut Camera>); - pub unsafe fn release(self: Pin<&mut Camera>); + pub unsafe fn acquire(self: Pin<&mut Camera>) -> Result<()>; + pub unsafe fn release(self: Pin<&mut Camera>) -> Result<()>; pub unsafe fn generate_configuration( self: Pin<&mut Camera>, roles: &[StreamRole], - ) -> BindCameraConfiguration; - pub unsafe fn configure(self: Pin<&mut Camera>, conf: Pin<&mut CameraConfiguration>); - pub unsafe fn create_request(self: Pin<&mut Camera>) -> BindRequest; - pub unsafe fn queue_request(self: Pin<&mut Camera>, req: Pin<&mut Request>); - pub unsafe fn start(self: Pin<&mut Camera>); - pub unsafe fn stop(self: Pin<&mut Camera>); + ) -> Result; + pub unsafe fn configure( + self: Pin<&mut Camera>, + conf: Pin<&mut CameraConfiguration>, + ) -> Result<()>; + pub unsafe fn create_request(self: Pin<&mut Camera>) -> Result; + pub unsafe fn queue_request(self: Pin<&mut Camera>, req: Pin<&mut Request>) -> Result<()>; + pub unsafe fn start(self: Pin<&mut Camera>) -> Result<()>; + pub unsafe fn stop(self: Pin<&mut Camera>) -> Result<()>; type CameraConfiguration; - pub unsafe fn at(self: Pin<&mut CameraConfiguration>, idx: u32) -> BindStreamConfiguration; + pub unsafe fn at( + self: Pin<&mut CameraConfiguration>, + idx: u32, + ) -> Result; pub unsafe fn validate(self: Pin<&mut CameraConfiguration>) -> CameraConfigurationStatus; type StreamConfiguration; @@ -193,6 +199,8 @@ pub mod ffi { pub unsafe fn get_pixel_format(self: Pin<&mut StreamConfiguration>) -> BindPixelFormat; pub unsafe fn set_size(self: Pin<&mut StreamConfiguration>, size: BindSize); pub unsafe fn get_size(self: Pin<&mut StreamConfiguration>) -> BindSize; + pub unsafe fn set_buffer_count(self: Pin<&mut StreamConfiguration>, buffer_count: usize); + pub unsafe fn get_buffer_count(self: Pin<&mut StreamConfiguration>) -> usize; pub unsafe fn to_string(self: Pin<&mut StreamConfiguration>) -> String; type PixelFormat; @@ -206,8 +214,14 @@ pub mod ffi { type FrameBufferAllocator; pub fn make_frame_buffer_allocator(camera: Pin<&mut Camera>) -> BindFrameBufferAllocator; - pub unsafe fn allocate(self: Pin<&mut FrameBufferAllocator>, stream: Pin<&mut Stream>); - pub unsafe fn free(self: Pin<&mut FrameBufferAllocator>, stream: Pin<&mut Stream>); + pub unsafe fn allocate( + self: Pin<&mut FrameBufferAllocator>, + stream: Pin<&mut Stream>, + ) -> Result; + pub unsafe fn free( + self: Pin<&mut FrameBufferAllocator>, + stream: Pin<&mut Stream>, + ) -> Result<()>; pub unsafe fn buffers( self: Pin<&mut FrameBufferAllocator>, stream: Pin<&mut Stream>, @@ -222,15 +236,15 @@ pub mod ffi { pub unsafe fn get_length(self: Pin<&mut FrameBufferPlane>) -> usize; /// File descriptor functions - pub unsafe fn fd_len(fd: i32) -> usize; - pub unsafe fn mmap_plane(fd: i32, length: usize) -> BindMemoryBuffer; + pub unsafe fn fd_len(fd: i32) -> Result; + pub unsafe fn mmap_plane(fd: i32, length: usize) -> Result; type MemoryBuffer; pub unsafe fn sub_buffer( self: Pin<&mut MemoryBuffer>, offset: usize, length: usize, - ) -> BindMemoryBuffer; + ) -> Result; pub unsafe fn read_to_vec(self: Pin<&mut MemoryBuffer>) -> Vec; type Request; @@ -238,7 +252,7 @@ pub mod ffi { self: Pin<&mut Request>, stream: Pin<&mut Stream>, buffer: Pin<&mut FrameBuffer>, - ); + ) -> Result<()>; } } diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 509dd6b..e5f0055 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -6,20 +6,21 @@ use std::collections::HashMap; #[test] fn it_works() { let mut cm = ffi::make_camera_manager(); - unsafe { cm.get().start() }; + unsafe { cm.get().start() }.unwrap(); let camera_ids = unsafe { cm.get().get_camera_ids() }; println!("Available Cameras: {camera_ids:?}"); - let mut camera = unsafe { cm.get().get_camera_by_id(&camera_ids[0]) }; + let mut camera = unsafe { cm.get().get_camera_by_id(&camera_ids[0]) }.unwrap(); - unsafe { camera.get().acquire() }; + unsafe { camera.get().acquire() }.unwrap(); let mut config = unsafe { camera .get() - .generate_configuration(&[ffi::StreamRole::Viewfinder]) - }; + .generate_configuration(&[ffi::StreamRole::StillCapture]) + } + .unwrap(); - let mut stream_config = unsafe { config.get().at(0) }; + let mut stream_config = unsafe { config.get().at(0) }.unwrap(); unsafe { stream_config @@ -29,7 +30,7 @@ fn it_works() { )) }; - unsafe { stream_config.get().set_size(ffi::new_size(640, 480)) }; + unsafe { stream_config.get().set_size(ffi::new_size(1280, 720)) }; let status = unsafe { config.get().validate() }; if status == ffi::CameraConfigurationStatus::Invalid { @@ -39,19 +40,21 @@ fn it_works() { println!("Camera Configuration Adjusted."); } - unsafe { camera.get().configure(config.get()) }; + unsafe { camera.get().configure(config.get()) }.unwrap(); - let mut stream_config = unsafe { config.get().at(0) }; + unsafe { stream_config.get().set_buffer_count(1) }; let mut allocator = unsafe { ffi::make_frame_buffer_allocator(camera.get()) }; let mut stream = unsafe { stream_config.get().stream() }; - unsafe { allocator.get().allocate(stream.get()) }; + let buffer_count = unsafe { allocator.get().allocate(stream.get()) }.unwrap(); + println!("Buffers: {buffer_count}"); + let mut requests = Vec::new(); let mut planes = Vec::new(); for mut buffer in unsafe { allocator.get().buffers(stream.get()) } { - let mut request = unsafe { camera.get().create_request() }; + let mut request = unsafe { camera.get().create_request() }.unwrap(); let mut mapped_buffers: HashMap, usize, usize)> = HashMap::new(); @@ -59,7 +62,7 @@ fn it_works() { let fd = unsafe { plane.get().get_fd() }; let length = mapped_buffers .entry(fd) - .or_insert((None, 0, unsafe { ffi::fd_len(fd) })) + .or_insert((None, 0, unsafe { ffi::fd_len(fd) }.unwrap())) .2; if unsafe { plane.get().get_offset() } + unsafe { plane.get().get_length() } > length { panic!( @@ -76,26 +79,29 @@ fn it_works() { let fd = unsafe { plane.get().get_fd() }; let mapped_buffer = mapped_buffers.get_mut(&fd).unwrap(); if mapped_buffer.0.is_none() { - mapped_buffer.0 = Some(unsafe { ffi::mmap_plane(fd, mapped_buffer.1) }); + mapped_buffer.0 = Some(unsafe { ffi::mmap_plane(fd, mapped_buffer.1) }.unwrap()); } - planes.push(unsafe { - mapped_buffer - .0 - .as_mut() - .unwrap() - .get() - .sub_buffer(plane.get().get_offset(), plane.get().get_length()) - }); + planes.push( + unsafe { + mapped_buffer + .0 + .as_mut() + .unwrap() + .get() + .sub_buffer(plane.get().get_offset(), plane.get().get_length()) + } + .unwrap(), + ); } - unsafe { request.get().add_buffer(stream.get(), buffer.get()) }; + unsafe { request.get().add_buffer(stream.get(), buffer.get()) }.unwrap(); requests.push(request); } - unsafe { camera.get().start() }; + unsafe { camera.get().start() }.unwrap(); for request in &mut requests { - unsafe { camera.get().queue_request(request.get()) }; + unsafe { camera.get().queue_request(request.get()) }.unwrap(); } std::thread::sleep(std::time::Duration::from_millis(1000)); @@ -107,6 +113,6 @@ fn it_works() { .unwrap(); } - unsafe { camera.get().stop() }; - unsafe { camera.get().release() }; + unsafe { camera.get().stop() }.unwrap(); + unsafe { camera.get().release() }.unwrap(); } From 9f27054e7130887607fc10dfc3a9bd8be97d487c Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 29 Jun 2022 15:46:55 -0600 Subject: [PATCH 12/49] chore: make functions that can be const const --- libcamera-bridge/camera_manager.cpp | 2 +- libcamera-bridge/core.hpp | 77 ++++++------ libcamera-bridge/frame_buffer.cpp | 2 +- libcamera-bridge/frame_buffer_allocator.cpp | 7 +- libcamera-bridge/frame_buffer_plane.cpp | 6 +- libcamera-bridge/memory_buffer.cpp | 2 +- libcamera-bridge/pixel_format.cpp | 4 +- libcamera-bridge/request.cpp | 4 +- libcamera-bridge/size.cpp | 10 +- libcamera-bridge/stream.cpp | 6 + libcamera-bridge/stream_configuration.cpp | 10 +- src/bridge.rs | 123 +++++++++++++------- src/bridge/test.rs | 46 ++++---- 13 files changed, 179 insertions(+), 120 deletions(-) diff --git a/libcamera-bridge/camera_manager.cpp b/libcamera-bridge/camera_manager.cpp index f9103e5..dbffeb9 100644 --- a/libcamera-bridge/camera_manager.cpp +++ b/libcamera-bridge/camera_manager.cpp @@ -25,7 +25,7 @@ void CameraManager::stop() { this->inner->stop(); } -rust::Vec CameraManager::get_camera_ids() { +rust::Vec CameraManager::get_camera_ids() const { VALIDATE_POINTERS() rust::Vec camera_ids; diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 57e4a48..4754262 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -1,3 +1,6 @@ +#ifndef _HOME_BEN1JEN_GITLAB_LIBCAMERA_RS_LIBCAMERA_BRIDGE_CORE_HPP +#define _HOME_BEN1JEN_GITLAB_LIBCAMERA_RS_LIBCAMERA_BRIDGE_CORE_HPP + #pragma once #include @@ -45,12 +48,12 @@ struct CameraManager { std::unique_ptr inner; public: - CameraManager(std::unique_ptr inner_) + explicit CameraManager(std::unique_ptr inner_) : inner{std::move(inner_)} {} void start(); void stop(); - rust::Vec get_camera_ids(); + [[nodiscard]] rust::Vec get_camera_ids() const; BindCamera get_camera_by_id(rust::Str id); }; @@ -59,14 +62,14 @@ struct Camera { std::shared_ptr inner; public: - Camera(std::shared_ptr inner_) + explicit Camera(std::shared_ptr inner_) : inner{std::move(inner_)} {} std::shared_ptr into_shared(); void acquire(); void release(); - BindCameraConfiguration - generate_configuration(rust::Slice); + BindCameraConfiguration generate_configuration( + rust::Slice /*roles*/); void configure(CameraConfiguration &conf); BindRequest create_request(); void queue_request(Request &req); @@ -79,7 +82,8 @@ struct CameraConfiguration { std::unique_ptr inner; public: - CameraConfiguration(std::unique_ptr inner_) + explicit CameraConfiguration( + std::unique_ptr inner_) : inner{std::move(inner_)} {} libcamera::CameraConfiguration *into_ptr(); @@ -92,16 +96,17 @@ struct StreamConfiguration { libcamera::StreamConfiguration *inner; public: - StreamConfiguration(libcamera::StreamConfiguration *inner_) : inner(inner_) {} + explicit StreamConfiguration(libcamera::StreamConfiguration *inner_) + : inner(inner_) {} - BindStream stream(); + [[nodiscard]] BindStream stream() const; void set_pixel_format(BindPixelFormat pixel_format); - BindPixelFormat get_pixel_format(); + [[nodiscard]] BindPixelFormat get_pixel_format() const; void set_size(BindSize size); - BindSize get_size(); + [[nodiscard]] BindSize get_size() const; void set_buffer_count(size_t buffer_count); - size_t get_buffer_count(); - rust::String to_string(); + [[nodiscard]] size_t get_buffer_count() const; + [[nodiscard]] rust::String raw_to_string() const; }; BindPixelFormat get_default_pixel_format(DefaultPixelFormat default_format); @@ -111,10 +116,10 @@ struct PixelFormat { libcamera::PixelFormat inner; public: - PixelFormat(libcamera::PixelFormat inner_) : inner(inner_) {} + explicit PixelFormat(libcamera::PixelFormat inner_) : inner(inner_) {} libcamera::PixelFormat into_inner(); - rust::String to_string(); + [[nodiscard]] rust::String raw_to_string() const; }; BindSize new_size(unsigned int width, unsigned int height); @@ -124,15 +129,15 @@ struct Size { libcamera::Size inner; public: - Size(libcamera::Size inner_) : inner(inner_) {} + explicit Size(libcamera::Size inner_) : inner(inner_) {} libcamera::Size into_inner(); - unsigned int get_width() const; - unsigned int get_height() const; void set_width(unsigned int width); + [[nodiscard]] unsigned int get_width() const; void set_height(unsigned int height); + [[nodiscard]] unsigned int get_height() const; - rust::String to_string(); + [[nodiscard]] rust::String raw_to_string() const; }; struct Stream { @@ -140,24 +145,25 @@ struct Stream { libcamera::Stream *inner; public: - Stream(libcamera::Stream *inner_) : inner(inner_) {} + explicit Stream(libcamera::Stream *inner_) : inner(inner_) {} libcamera::Stream *into_ptr(); + const libcamera::Stream *into_ptr() const; }; BindFrameBufferAllocator make_frame_buffer_allocator(Camera &camera); struct FrameBufferAllocator { private: - libcamera::FrameBufferAllocator *inner; + std::unique_ptr inner; public: - FrameBufferAllocator(libcamera::FrameBufferAllocator *inner_) - : inner(inner_) {} - ~FrameBufferAllocator(); + explicit FrameBufferAllocator( + std::unique_ptr inner_) + : inner{std::move(inner_)} {} size_t allocate(Stream &stream); void free(Stream &stream); - rust::Vec buffers(Stream &stream); + rust::Vec buffers(Stream &stream) const; }; struct FrameBuffer { @@ -165,9 +171,9 @@ struct FrameBuffer { libcamera::FrameBuffer *inner; public: - FrameBuffer(libcamera::FrameBuffer *inner_) : inner(inner_) {} + explicit FrameBuffer(libcamera::FrameBuffer *inner_) : inner(inner_) {} libcamera::FrameBuffer *into_ptr(); - rust::Vec planes(); + [[nodiscard]] rust::Vec planes() const; }; size_t fd_len(int fd); @@ -177,17 +183,16 @@ struct FrameBufferPlane { const libcamera::FrameBuffer::Plane *inner; public: - FrameBufferPlane(const libcamera::FrameBuffer::Plane *inner_) + explicit FrameBufferPlane(const libcamera::FrameBuffer::Plane *inner_) : inner(inner_) {} - int get_fd(); - size_t get_offset(); - size_t get_length(); + [[nodiscard]] int get_fd() const; + [[nodiscard]] size_t get_offset() const; + [[nodiscard]] size_t get_length() const; }; // File descriptor functions -size_t fd_len(int fd); BindMemoryBuffer mmap_plane(int fd, size_t len); struct MemoryBuffer { @@ -200,7 +205,7 @@ struct MemoryBuffer { : pointer(pointer_), length(length_) {} BindMemoryBuffer sub_buffer(size_t offset, size_t length); - rust::Vec read_to_vec(); + [[nodiscard]] rust::Vec read_to_vec() const; }; struct Request { @@ -208,10 +213,12 @@ struct Request { std::unique_ptr inner; public: - Request(std::unique_ptr inner_) + explicit Request(std::unique_ptr inner_) : inner{std::move(inner_)} {} libcamera::Request *into_ptr(); - void add_buffer(Stream &stream, FrameBuffer &buffer); - rust::String to_string(); + void add_buffer(const Stream &stream, FrameBuffer &buffer); + [[nodiscard]] rust::String raw_to_string() const; }; + +#endif diff --git a/libcamera-bridge/frame_buffer.cpp b/libcamera-bridge/frame_buffer.cpp index 3de76dc..9218f4e 100644 --- a/libcamera-bridge/frame_buffer.cpp +++ b/libcamera-bridge/frame_buffer.cpp @@ -8,7 +8,7 @@ libcamera::FrameBuffer *FrameBuffer::into_ptr() { return this->inner; } -rust::Vec FrameBuffer::planes() { +rust::Vec FrameBuffer::planes() const { VALIDATE_POINTERS() rust::Vec vec; diff --git a/libcamera-bridge/frame_buffer_allocator.cpp b/libcamera-bridge/frame_buffer_allocator.cpp index c366368..9bec5f6 100644 --- a/libcamera-bridge/frame_buffer_allocator.cpp +++ b/libcamera-bridge/frame_buffer_allocator.cpp @@ -5,7 +5,8 @@ BindFrameBufferAllocator make_frame_buffer_allocator(Camera &camera) { BindFrameBufferAllocator allocator{ .inner = std::make_unique( - new libcamera::FrameBufferAllocator(camera.into_shared())), + std::make_unique( + camera.into_shared())), }; return allocator; } @@ -30,7 +31,7 @@ void FrameBufferAllocator::free(Stream &stream) { } } -rust::Vec FrameBufferAllocator::buffers(Stream &stream) { +rust::Vec FrameBufferAllocator::buffers(Stream &stream) const { VALIDATE_POINTERS() rust::Vec vec; @@ -44,5 +45,3 @@ rust::Vec FrameBufferAllocator::buffers(Stream &stream) { } return vec; } - -FrameBufferAllocator::~FrameBufferAllocator() { delete this->inner; } diff --git a/libcamera-bridge/frame_buffer_plane.cpp b/libcamera-bridge/frame_buffer_plane.cpp index ab4e460..6886f3f 100644 --- a/libcamera-bridge/frame_buffer_plane.cpp +++ b/libcamera-bridge/frame_buffer_plane.cpp @@ -2,19 +2,19 @@ #include "libcamera-rs/src/bridge.rs.h" -int FrameBufferPlane::get_fd() { +int FrameBufferPlane::get_fd() const { VALIDATE_POINTERS() return this->inner->fd.get(); } -size_t FrameBufferPlane::get_offset() { +size_t FrameBufferPlane::get_offset() const { VALIDATE_POINTERS() return (size_t)this->inner->offset; } -size_t FrameBufferPlane::get_length() { +size_t FrameBufferPlane::get_length() const { VALIDATE_POINTERS() return (size_t)this->inner->length; diff --git a/libcamera-bridge/memory_buffer.cpp b/libcamera-bridge/memory_buffer.cpp index 4310cb8..a89d445 100644 --- a/libcamera-bridge/memory_buffer.cpp +++ b/libcamera-bridge/memory_buffer.cpp @@ -15,7 +15,7 @@ BindMemoryBuffer MemoryBuffer::sub_buffer(size_t offset, size_t length) { return buffer; } -rust::Vec MemoryBuffer::read_to_vec() { +rust::Vec MemoryBuffer::read_to_vec() const { rust::Vec buf; for (size_t i = 0; i < this->length; i++) { // The point of this class is to safely wrap raw memory pointers. diff --git a/libcamera-bridge/pixel_format.cpp b/libcamera-bridge/pixel_format.cpp index 54c50a7..3f09c8e 100644 --- a/libcamera-bridge/pixel_format.cpp +++ b/libcamera-bridge/pixel_format.cpp @@ -54,4 +54,6 @@ BindPixelFormat get_default_pixel_format(DefaultPixelFormat default_format) { libcamera::PixelFormat PixelFormat::into_inner() { return this->inner; } -rust::String PixelFormat::to_string() { return this->inner.toString(); } +rust::String PixelFormat::raw_to_string() const { + return this->inner.toString(); +} diff --git a/libcamera-bridge/request.cpp b/libcamera-bridge/request.cpp index 7ae9abe..e36fdd2 100644 --- a/libcamera-bridge/request.cpp +++ b/libcamera-bridge/request.cpp @@ -2,7 +2,7 @@ #include "libcamera-rs/src/bridge.rs.h" -void Request::add_buffer(Stream &stream, FrameBuffer &buffer) { +void Request::add_buffer(const Stream &stream, FrameBuffer &buffer) { VALIDATE_POINTERS() int ret = this->inner->addBuffer(stream.into_ptr(), buffer.into_ptr()); @@ -17,4 +17,4 @@ libcamera::Request *Request::into_ptr() { return this->inner.get(); } -rust::String Request::to_string() { return this->inner->toString(); } +rust::String Request::raw_to_string() const { return this->inner->toString(); } diff --git a/libcamera-bridge/size.cpp b/libcamera-bridge/size.cpp index 6f4ebe4..903ff0a 100644 --- a/libcamera-bridge/size.cpp +++ b/libcamera-bridge/size.cpp @@ -11,12 +11,12 @@ BindSize new_size(unsigned int width, unsigned int height) { libcamera::Size Size::into_inner() { return this->inner; } -unsigned int Size::get_width() const { return this->inner.width; } - -unsigned int Size::get_height() const { return this->inner.height; } - void Size::set_width(unsigned int width) { this->inner.width = width; } +unsigned int Size::get_width() const { return this->inner.width; } + void Size::set_height(unsigned int height) { this->inner.height = height; } -rust::String Size::to_string() { return this->inner.toString(); } +unsigned int Size::get_height() const { return this->inner.height; } + +rust::String Size::raw_to_string() const { return this->inner.toString(); } diff --git a/libcamera-bridge/stream.cpp b/libcamera-bridge/stream.cpp index 79c1e9d..7dc3873 100644 --- a/libcamera-bridge/stream.cpp +++ b/libcamera-bridge/stream.cpp @@ -7,3 +7,9 @@ libcamera::Stream *Stream::into_ptr() { return this->inner; } + +const libcamera::Stream *Stream::into_ptr() const { + VALIDATE_POINTERS() + + return this->inner; +} diff --git a/libcamera-bridge/stream_configuration.cpp b/libcamera-bridge/stream_configuration.cpp index a05f8f7..4d85c9d 100644 --- a/libcamera-bridge/stream_configuration.cpp +++ b/libcamera-bridge/stream_configuration.cpp @@ -2,7 +2,7 @@ #include "libcamera-rs/src/bridge.rs.h" -BindStream StreamConfiguration::stream() { +BindStream StreamConfiguration::stream() const { BindStream stream{ .inner = std::make_unique(this->inner->stream()), }; @@ -15,7 +15,7 @@ void StreamConfiguration::set_pixel_format(BindPixelFormat pixel_format) { this->inner->pixelFormat = pixel_format.inner->into_inner(); } -BindPixelFormat StreamConfiguration::get_pixel_format() { +BindPixelFormat StreamConfiguration::get_pixel_format() const { VALIDATE_POINTERS() BindPixelFormat pixel_format{ @@ -29,7 +29,7 @@ void StreamConfiguration::set_size(BindSize size) { this->inner->size = size.inner->into_inner(); } -BindSize StreamConfiguration::get_size() { +BindSize StreamConfiguration::get_size() const { VALIDATE_POINTERS() BindSize size{ @@ -44,12 +44,12 @@ void StreamConfiguration::set_buffer_count(size_t buffer_count) { this->inner->bufferCount = buffer_count; } -size_t StreamConfiguration::get_buffer_count() { +size_t StreamConfiguration::get_buffer_count() const { VALIDATE_POINTERS() return this->inner->bufferCount; } -rust::String StreamConfiguration::to_string() { +rust::String StreamConfiguration::raw_to_string() const { return this->inner->toString(); } diff --git a/src/bridge.rs b/src/bridge.rs index c39061c..ec24354 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -164,7 +164,7 @@ pub mod ffi { pub unsafe fn start(self: Pin<&mut CameraManager>) -> Result<()>; pub unsafe fn stop(self: Pin<&mut CameraManager>); - pub unsafe fn get_camera_ids(self: Pin<&mut CameraManager>) -> Vec; + pub unsafe fn get_camera_ids(self: &CameraManager) -> Vec; pub unsafe fn get_camera_by_id(self: Pin<&mut CameraManager>, id: &str) -> Result; type Camera; @@ -191,23 +191,29 @@ pub mod ffi { pub unsafe fn validate(self: Pin<&mut CameraConfiguration>) -> CameraConfigurationStatus; type StreamConfiguration; - pub unsafe fn stream(self: Pin<&mut StreamConfiguration>) -> BindStream; + pub unsafe fn stream(self: &StreamConfiguration) -> BindStream; pub unsafe fn set_pixel_format( self: Pin<&mut StreamConfiguration>, pixel_format: BindPixelFormat, ); - pub unsafe fn get_pixel_format(self: Pin<&mut StreamConfiguration>) -> BindPixelFormat; + pub unsafe fn get_pixel_format(self: &StreamConfiguration) -> BindPixelFormat; pub unsafe fn set_size(self: Pin<&mut StreamConfiguration>, size: BindSize); - pub unsafe fn get_size(self: Pin<&mut StreamConfiguration>) -> BindSize; + pub unsafe fn get_size(self: &StreamConfiguration) -> BindSize; pub unsafe fn set_buffer_count(self: Pin<&mut StreamConfiguration>, buffer_count: usize); - pub unsafe fn get_buffer_count(self: Pin<&mut StreamConfiguration>) -> usize; - pub unsafe fn to_string(self: Pin<&mut StreamConfiguration>) -> String; + pub unsafe fn get_buffer_count(self: &StreamConfiguration) -> usize; + pub unsafe fn raw_to_string(self: &StreamConfiguration) -> String; type PixelFormat; pub fn get_default_pixel_format(default_format: DefaultPixelFormat) -> BindPixelFormat; + pub fn raw_to_string(self: &PixelFormat) -> String; type Size; pub fn new_size(width: u32, height: u32) -> BindSize; + pub fn set_width(self: Pin<&mut Size>, width: u32); + pub fn get_width(self: &Size) -> u32; + pub fn set_height(self: Pin<&mut Size>, height: u32); + pub fn get_height(self: &Size) -> u32; + pub fn raw_to_string(self: &Size) -> String; type Stream; @@ -223,17 +229,17 @@ pub mod ffi { stream: Pin<&mut Stream>, ) -> Result<()>; pub unsafe fn buffers( - self: Pin<&mut FrameBufferAllocator>, + self: &FrameBufferAllocator, stream: Pin<&mut Stream>, ) -> Vec; type FrameBuffer; - pub unsafe fn planes(self: Pin<&mut FrameBuffer>) -> Vec; + pub unsafe fn planes(self: &FrameBuffer) -> Vec; type FrameBufferPlane; - pub unsafe fn get_fd(self: Pin<&mut FrameBufferPlane>) -> i32; - pub unsafe fn get_offset(self: Pin<&mut FrameBufferPlane>) -> usize; - pub unsafe fn get_length(self: Pin<&mut FrameBufferPlane>) -> usize; + pub unsafe fn get_fd(self: &FrameBufferPlane) -> i32; + pub unsafe fn get_offset(self: &FrameBufferPlane) -> usize; + pub unsafe fn get_length(self: &FrameBufferPlane) -> usize; /// File descriptor functions pub unsafe fn fd_len(fd: i32) -> Result; @@ -245,104 +251,143 @@ pub mod ffi { offset: usize, length: usize, ) -> Result; - pub unsafe fn read_to_vec(self: Pin<&mut MemoryBuffer>) -> Vec; + pub unsafe fn read_to_vec(self: &MemoryBuffer) -> Vec; type Request; pub unsafe fn add_buffer( self: Pin<&mut Request>, - stream: Pin<&mut Stream>, + stream: &Stream, buffer: Pin<&mut FrameBuffer>, ) -> Result<()>; + + pub fn raw_to_string(self: &Request) -> String; } } /// # Safety /// The inner pointer to the libcamera object must be valid. -unsafe trait PinMut { +unsafe trait GetInner { type Inner; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner>; + unsafe fn get(&self) -> &Self::Inner; + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner>; } -unsafe impl PinMut for ffi::BindCameraManager { +unsafe impl GetInner for ffi::BindCameraManager { type Inner = ffi::CameraManager; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindCamera { +unsafe impl GetInner for ffi::BindCamera { type Inner = ffi::Camera; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindCameraConfiguration { +unsafe impl GetInner for ffi::BindCameraConfiguration { type Inner = ffi::CameraConfiguration; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindStreamConfiguration { +unsafe impl GetInner for ffi::BindStreamConfiguration { type Inner = ffi::StreamConfiguration; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindPixelFormat { +unsafe impl GetInner for ffi::BindPixelFormat { type Inner = ffi::PixelFormat; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindSize { +unsafe impl GetInner for ffi::BindSize { type Inner = ffi::Size; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindStream { +unsafe impl GetInner for ffi::BindStream { type Inner = ffi::Stream; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindFrameBufferAllocator { +unsafe impl GetInner for ffi::BindFrameBufferAllocator { type Inner = ffi::FrameBufferAllocator; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindFrameBuffer { +unsafe impl GetInner for ffi::BindFrameBuffer { type Inner = ffi::FrameBuffer; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindFrameBufferPlane { +unsafe impl GetInner for ffi::BindFrameBufferPlane { type Inner = ffi::FrameBufferPlane; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindMemoryBuffer { +unsafe impl GetInner for ffi::BindMemoryBuffer { type Inner = ffi::MemoryBuffer; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } -unsafe impl PinMut for ffi::BindRequest { +unsafe impl GetInner for ffi::BindRequest { type Inner = ffi::Request; - unsafe fn get(&mut self) -> Pin<&mut Self::Inner> { + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { self.inner.pin_mut() } } diff --git a/src/bridge/test.rs b/src/bridge/test.rs index e5f0055..8923637 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -1,38 +1,38 @@ use crate::bridge::ffi; -use crate::bridge::PinMut; +use crate::bridge::GetInner; use std::collections::HashMap; #[test] fn it_works() { let mut cm = ffi::make_camera_manager(); - unsafe { cm.get().start() }.unwrap(); + unsafe { cm.get_mut().start() }.unwrap(); let camera_ids = unsafe { cm.get().get_camera_ids() }; println!("Available Cameras: {camera_ids:?}"); - let mut camera = unsafe { cm.get().get_camera_by_id(&camera_ids[0]) }.unwrap(); + let mut camera = unsafe { cm.get_mut().get_camera_by_id(&camera_ids[0]) }.unwrap(); - unsafe { camera.get().acquire() }.unwrap(); + unsafe { camera.get_mut().acquire() }.unwrap(); let mut config = unsafe { camera - .get() + .get_mut() .generate_configuration(&[ffi::StreamRole::StillCapture]) } .unwrap(); - let mut stream_config = unsafe { config.get().at(0) }.unwrap(); + let mut stream_config = unsafe { config.get_mut().at(0) }.unwrap(); unsafe { stream_config - .get() + .get_mut() .set_pixel_format(ffi::get_default_pixel_format( ffi::DefaultPixelFormat::Yuv422, )) }; - unsafe { stream_config.get().set_size(ffi::new_size(1280, 720)) }; + unsafe { stream_config.get_mut().set_size(ffi::new_size(1280, 720)) }; - let status = unsafe { config.get().validate() }; + let status = unsafe { config.get_mut().validate() }; if status == ffi::CameraConfigurationStatus::Invalid { panic!("Invalid Camera Configuration!"); } @@ -40,25 +40,25 @@ fn it_works() { println!("Camera Configuration Adjusted."); } - unsafe { camera.get().configure(config.get()) }.unwrap(); + unsafe { camera.get_mut().configure(config.get_mut()) }.unwrap(); - unsafe { stream_config.get().set_buffer_count(1) }; + unsafe { stream_config.get_mut().set_buffer_count(1) }; - let mut allocator = unsafe { ffi::make_frame_buffer_allocator(camera.get()) }; + let mut allocator = unsafe { ffi::make_frame_buffer_allocator(camera.get_mut()) }; let mut stream = unsafe { stream_config.get().stream() }; - let buffer_count = unsafe { allocator.get().allocate(stream.get()) }.unwrap(); + let buffer_count = unsafe { allocator.get_mut().allocate(stream.get_mut()) }.unwrap(); println!("Buffers: {buffer_count}"); let mut requests = Vec::new(); let mut planes = Vec::new(); - for mut buffer in unsafe { allocator.get().buffers(stream.get()) } { - let mut request = unsafe { camera.get().create_request() }.unwrap(); + for mut buffer in unsafe { allocator.get().buffers(stream.get_mut()) } { + let mut request = unsafe { camera.get_mut().create_request() }.unwrap(); let mut mapped_buffers: HashMap, usize, usize)> = HashMap::new(); - for mut plane in unsafe { buffer.get().planes() } { + for plane in unsafe { buffer.get().planes() } { let fd = unsafe { plane.get().get_fd() }; let length = mapped_buffers .entry(fd) @@ -75,7 +75,7 @@ fn it_works() { mapped_buffers.get_mut(&fd).unwrap().1 = map_len.max(unsafe { plane.get().get_offset() } + unsafe { plane.get().get_length() }); } - for mut plane in unsafe { buffer.get().planes() } { + for plane in unsafe { buffer.get().planes() } { let fd = unsafe { plane.get().get_fd() }; let mapped_buffer = mapped_buffers.get_mut(&fd).unwrap(); if mapped_buffer.0.is_none() { @@ -87,21 +87,21 @@ fn it_works() { .0 .as_mut() .unwrap() - .get() + .get_mut() .sub_buffer(plane.get().get_offset(), plane.get().get_length()) } .unwrap(), ); } - unsafe { request.get().add_buffer(stream.get(), buffer.get()) }.unwrap(); + unsafe { request.get_mut().add_buffer(stream.get(), buffer.get_mut()) }.unwrap(); requests.push(request); } - unsafe { camera.get().start() }.unwrap(); + unsafe { camera.get_mut().start() }.unwrap(); for request in &mut requests { - unsafe { camera.get().queue_request(request.get()) }.unwrap(); + unsafe { camera.get_mut().queue_request(request.get_mut()) }.unwrap(); } std::thread::sleep(std::time::Duration::from_millis(1000)); @@ -113,6 +113,6 @@ fn it_works() { .unwrap(); } - unsafe { camera.get().stop() }.unwrap(); - unsafe { camera.get().release() }.unwrap(); + unsafe { camera.get_mut().stop() }.unwrap(); + unsafe { camera.get_mut().release() }.unwrap(); } From 3ef26cad62be1735c61fddf9873868e54bc54ab8 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Thu, 30 Jun 2022 11:58:33 -0600 Subject: [PATCH 13/49] feat: more work on the safe API --- Cargo.toml | 1 + libcamera-bridge/camera_configuration.cpp | 6 ++ libcamera-bridge/core.hpp | 1 + src/bridge.rs | 19 +++- src/bridge/test.rs | 10 +- src/camera.rs | 115 ++++++++++++++++++++++ src/config.rs | 74 ++++++++++++++ src/lib.rs | 19 ++++ src/prelude.rs | 2 + src/test.rs | 15 +++ 10 files changed, 256 insertions(+), 6 deletions(-) create mode 100644 src/camera.rs create mode 100644 src/config.rs create mode 100644 src/prelude.rs create mode 100644 src/test.rs diff --git a/Cargo.toml b/Cargo.toml index c3d202d..da65f74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] cxx = "1.0" +thiserror = "1" [build-dependencies] cxx-build = "1.0" diff --git a/libcamera-bridge/camera_configuration.cpp b/libcamera-bridge/camera_configuration.cpp index 0c3577e..64312f2 100644 --- a/libcamera-bridge/camera_configuration.cpp +++ b/libcamera-bridge/camera_configuration.cpp @@ -8,6 +8,12 @@ libcamera::CameraConfiguration *CameraConfiguration::into_ptr() { return this->inner.get(); } +size_t CameraConfiguration::size() const { + VALIDATE_POINTERS() + + return this->inner->size(); +} + BindStreamConfiguration CameraConfiguration::at(unsigned int idx) { VALIDATE_POINTERS() diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 4754262..7015bd2 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -87,6 +87,7 @@ struct CameraConfiguration { : inner{std::move(inner_)} {} libcamera::CameraConfiguration *into_ptr(); + size_t size() const; BindStreamConfiguration at(unsigned int idx); CameraConfigurationStatus validate(); }; diff --git a/src/bridge.rs b/src/bridge.rs index ec24354..940ea98 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -7,13 +7,17 @@ mod test; #[cxx::bridge] pub mod ffi { + /// Represents a "role" that a camera stream can be optimized for. #[namespace = "libcamera"] #[repr(i32)] #[derive(Debug)] enum StreamRole { Raw, + /// Capturing still images StillCapture, + /// Recording a video VideoRecording, + /// Displaying a viewfinder Viewfinder, } @@ -26,19 +30,31 @@ pub mod ffi { Invalid, } + /// Represents common pixel formats. #[repr(i32)] #[derive(Debug)] enum DefaultPixelFormat { + /// 8bpp, single channel image R8, + /// 24bpp, three channel image, order red, green, blue Rgb888, + /// 16bpp, three channel image, order red, green, blue Rgb565, + /// 24bpp, three channel image, order blue, green, red Bgr888, + /// 16bpp, three channel image, YUV (4:2:2) encoding, order Y' U Y' V Yuyv, + /// 16bpp, three channel image, YUV (4:2:2) encoding, order Y' V Y' U Yvyu, + /// 16bpp*, chroma subsampling (U, V half width + height), three channel image, YUV (4:2:0) encoing, order Y', U, V Yuv420, + /// 16bpp*, chroma subsampling (U, V half width), three channel image, YUV (4:2:2) encoing, order Y', U, V Yuv422, + /// 16bpp*, chroma subsampling (U, V half width), three channel image, YUV (4:2:2) encoing, order Y', V, U Yvu422, + /// 24bpp, three channel image, YUV (4:4:4) encoing, order Y', U, V Yuv444, + /// MJPEG (motion JPEG) encoding, effectively one JPEG image per frame Mjpeg, } @@ -184,6 +200,7 @@ pub mod ffi { pub unsafe fn stop(self: Pin<&mut Camera>) -> Result<()>; type CameraConfiguration; + pub unsafe fn size(self: &CameraConfiguration) -> usize; pub unsafe fn at( self: Pin<&mut CameraConfiguration>, idx: u32, @@ -266,7 +283,7 @@ pub mod ffi { /// # Safety /// The inner pointer to the libcamera object must be valid. -unsafe trait GetInner { +pub unsafe trait GetInner { type Inner; unsafe fn get(&self) -> &Self::Inner; unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner>; diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 8923637..6d50950 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -4,7 +4,7 @@ use crate::bridge::GetInner; use std::collections::HashMap; #[test] -fn it_works() { +fn test_unsafe_camera() { let mut cm = ffi::make_camera_manager(); unsafe { cm.get_mut().start() }.unwrap(); let camera_ids = unsafe { cm.get().get_camera_ids() }; @@ -13,6 +13,8 @@ fn it_works() { unsafe { camera.get_mut().acquire() }.unwrap(); + let mut allocator = unsafe { ffi::make_frame_buffer_allocator(camera.get_mut()) }; + let mut config = unsafe { camera .get_mut() @@ -30,7 +32,7 @@ fn it_works() { )) }; - unsafe { stream_config.get_mut().set_size(ffi::new_size(1280, 720)) }; + unsafe { stream_config.get_mut().set_size(ffi::new_size(640, 480)) }; let status = unsafe { config.get_mut().validate() }; if status == ffi::CameraConfigurationStatus::Invalid { @@ -40,11 +42,9 @@ fn it_works() { println!("Camera Configuration Adjusted."); } - unsafe { camera.get_mut().configure(config.get_mut()) }.unwrap(); - unsafe { stream_config.get_mut().set_buffer_count(1) }; - let mut allocator = unsafe { ffi::make_frame_buffer_allocator(camera.get_mut()) }; + unsafe { camera.get_mut().configure(config.get_mut()) }.unwrap(); let mut stream = unsafe { stream_config.get().stream() }; diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..07d0ca3 --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,115 @@ +use std::fmt; +use std::marker::PhantomData; + +use crate::bridge::{ffi, GetInner}; +use crate::config::CameraConfig; + +use crate::{LibcameraError, Result}; + +pub use ffi::StreamRole; + +/// Manages cameras +pub struct CameraManager { + inner: ffi::BindCameraManager, +} + +impl fmt::Debug for CameraManager { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CameraManager").finish_non_exhaustive() + } +} + +impl CameraManager { + /// Constructs a new camera manager + pub fn new() -> Result { + let mut cm = ffi::make_camera_manager(); + // The primary safety concern for the CM is that it must be started once before calling all functions. + unsafe { cm.get_mut().start() }?; + Ok(CameraManager { inner: cm }) + } + /// Get a list of all attached cameras + pub fn get_camera_names(&self) -> Vec { + unsafe { self.inner.get().get_camera_ids() } + } + /// Get a camera with a given name + pub fn get_camera_by_name(&mut self, name: &str) -> Result> { + let mut cam = unsafe { self.inner.get_mut().get_camera_by_id(name) }?; + unsafe { cam.get_mut().acquire() }?; + let allocator = unsafe { ffi::make_frame_buffer_allocator(cam.get_mut()) }; + Ok(Camera { + _camera_manager: PhantomData, + name: name.to_string(), + config: None, + inner: cam, + allocator, + }) + } +} + +impl Drop for CameraManager { + fn drop(&mut self) { + unsafe { self.inner.get_mut().stop() }; + } +} + +/// Represents a camera +pub struct Camera<'a> { + _camera_manager: PhantomData<&'a CameraManager>, + name: String, + config: Option, + inner: ffi::BindCamera, + allocator: ffi::BindFrameBufferAllocator, +} + +impl fmt::Debug for Camera<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Camera") + .field("name", &self.name) + .finish_non_exhaustive() + } +} + +impl Camera<'_> { + /// Generate a configuration for this camera using the given set of stream roles to generate an corresponding set of streams. + pub fn generate_config(&mut self, caps: &[StreamRole]) -> Result<&mut CameraConfig> { + let config = unsafe { self.inner.get_mut().generate_configuration(caps) }?; + self.config = Some(CameraConfig::wrap_inner(config)?); + self.config.as_mut().ok_or(LibcameraError::InvalidConfig) + } + /// Validate and apply the configuration previously generated by this camera. + pub fn apply_config(&mut self) -> Result { + if let Some(config) = &mut self.config { + let config_status = unsafe { config.get_inner().get_mut().validate() }; + let (set, result) = match config_status { + ffi::CameraConfigurationStatus::Valid => (true, Ok(ConfigStatus::Unchanged)), + ffi::CameraConfigurationStatus::Adjusted => (true, Ok(ConfigStatus::Changed)), + _ => (false, Err(LibcameraError::InvalidConfig)), + }; + if set { + unsafe { self.inner.get_mut().configure(config.get_inner().get_mut()) }?; + } + result + } else { + Err(LibcameraError::InvalidConfig) + } + } + /// Borrow this camera's config. + pub fn get_config(&self) -> Option<&CameraConfig> { + self.config.as_ref() + } +} + +impl Drop for Camera<'_> { + fn drop(&mut self) { + unsafe { self.inner.get_mut().release() }.unwrap(); + } +} + +/// Represents the result of applying a configuration to a camera. +#[derive(Debug)] +pub enum ConfigStatus { + /// The configuration was applied to the camera unchanged + Unchanged, + /// The configuration was applied to the camera, but some values have been adjusted by the driver to a supported configuration for this camera + Changed, +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..63484fd --- /dev/null +++ b/src/config.rs @@ -0,0 +1,74 @@ +use std::fmt; + +use crate::bridge::{ffi, GetInner}; + +use crate::Result; + +pub use ffi::DefaultPixelFormat; + +/// Represents the configuration for a camera. +pub struct CameraConfig { + inner: ffi::BindCameraConfiguration, + streams: Vec, +} + +impl CameraConfig { + pub(crate) fn wrap_inner(mut inner: ffi::BindCameraConfiguration) -> Result { + let streams = (0..unsafe { inner.get().size() }) + .map(|n| { + Ok(StreamConfig::wrap_inner(unsafe { + inner.get_mut().at(n.try_into()?) + }?)) + }) + .collect::>>()?; + Ok(CameraConfig { inner, streams }) + } + pub(crate) fn get_inner(&mut self) -> &mut ffi::BindCameraConfiguration { + &mut self.inner + } + pub fn streams(&self) -> &Vec { + &self.streams + } + pub fn streams_mut(&mut self) -> &mut Vec { + &mut self.streams + } +} + +impl fmt::Debug for CameraConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CameraConfig") + .field("streams", &self.streams) + .finish_non_exhaustive() + } +} + +/// Represents the configuration for a stream in a camera. +pub struct StreamConfig { + inner: ffi::BindStreamConfiguration, +} + +impl StreamConfig { + pub(crate) fn wrap_inner(inner: ffi::BindStreamConfiguration) -> Self { + StreamConfig { inner } + } + /// Set the pixel format for this stream to a [DefaultPixelFormat] + pub fn set_default_pixel_format(&mut self, fmt: DefaultPixelFormat) { + unsafe { + self + .inner + .get_mut() + .set_pixel_format(ffi::get_default_pixel_format(fmt)) + }; + } + pub fn set_size(&mut self, width: u32, height: u32) { + unsafe { self.inner.get_mut().set_size(ffi::new_size(width, height)) }; + } +} + +impl fmt::Debug for StreamConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamConfig") + .field("description", &unsafe { self.inner.get().raw_to_string() }) + .finish_non_exhaustive() + } +} diff --git a/src/lib.rs b/src/lib.rs index d974ceb..a2b313f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,22 @@ #![deny(clippy::all)] +use thiserror::Error; + mod bridge; +pub mod camera; +pub mod config; +pub mod prelude; +#[cfg(test)] +mod test; + +#[derive(Debug, Error)] +pub enum LibcameraError { + #[error("Inner C++ error: {0}")] + InnerError(#[from] cxx::Exception), + #[error("Int conversion error: {0}")] + IntConversion(#[from] std::num::TryFromIntError), + #[error("Configuration Invalid or Missing")] + InvalidConfig, +} + +type Result = std::result::Result; diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..82d6433 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,2 @@ +pub use crate::camera::{CameraManager, StreamRole}; +pub use crate::config::DefaultPixelFormat; diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 0000000..85f3d34 --- /dev/null +++ b/src/test.rs @@ -0,0 +1,15 @@ +use crate::prelude::{CameraManager, DefaultPixelFormat, StreamRole}; + +#[test] +fn test_camera() { + let mut cm = CameraManager::new().unwrap(); + println!("cm: {cm:?}"); + let mut cam = cm.get_camera_by_name(&cm.get_camera_names()[0]).unwrap(); + println!("cam: {cam:?}"); + let conf = cam.generate_config(&[StreamRole::StillCapture]).unwrap(); + println!("conf: {conf:?}"); + let stream = &mut conf.streams_mut()[0]; + stream.set_default_pixel_format(DefaultPixelFormat::Yuyv); + stream.set_size(640, 480); + println!("Configuration applied: {:?}", cam.apply_config().unwrap()); +} From 0a76b6a45dcc2655b72ac10720c284eeb96611d7 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Thu, 30 Jun 2022 15:03:07 -0600 Subject: [PATCH 14/49] feat: start capture function on camera --- makefile | 2 +- src/bridge/test.rs | 4 +- src/camera.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++- src/config.rs | 3 ++ src/test.rs | 6 +++ 5 files changed, 114 insertions(+), 4 deletions(-) diff --git a/makefile b/makefile index 00b00da..bad67cb 100644 --- a/makefile +++ b/makefile @@ -3,9 +3,9 @@ chk: check lint: check check: cargo clippy + cargo test -- --test-threads=1 cppcheck -q libcamera-bridge/*.hpp libcamera-bridge/*.cpp clang-tidy --format-style=file libcamera-bridge/*.cpp -- -I/usr/local/include/libcamera -I./target/cxxbridge -I.. --std=c++17 - cargo test -- --test-threads=1 f: format fmt: format diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 6d50950..9b8353f 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -28,11 +28,11 @@ fn test_unsafe_camera() { stream_config .get_mut() .set_pixel_format(ffi::get_default_pixel_format( - ffi::DefaultPixelFormat::Yuv422, + ffi::DefaultPixelFormat::Mjpeg, )) }; - unsafe { stream_config.get_mut().set_size(ffi::new_size(640, 480)) }; + unsafe { stream_config.get_mut().set_size(ffi::new_size(1280, 720)) }; let status = unsafe { config.get_mut().validate() }; if status == ffi::CameraConfigurationStatus::Invalid { diff --git a/src/camera.rs b/src/camera.rs index 07d0ca3..254a36c 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,9 +1,9 @@ +use std::collections::HashMap; use std::fmt; use std::marker::PhantomData; use crate::bridge::{ffi, GetInner}; use crate::config::CameraConfig; - use crate::{LibcameraError, Result}; pub use ffi::StreamRole; @@ -42,6 +42,8 @@ impl CameraManager { config: None, inner: cam, allocator, + streams: Vec::new(), + started: false, }) } } @@ -52,6 +54,11 @@ impl Drop for CameraManager { } } +struct CameraStream { + next_buffer: usize, + buffers: Vec<(ffi::BindRequest, Vec)>, +} + /// Represents a camera pub struct Camera<'a> { _camera_manager: PhantomData<&'a CameraManager>, @@ -59,6 +66,8 @@ pub struct Camera<'a> { config: Option, inner: ffi::BindCamera, allocator: ffi::BindFrameBufferAllocator, + streams: Vec, + started: bool, } impl fmt::Debug for Camera<'_> { @@ -97,10 +106,102 @@ impl Camera<'_> { pub fn get_config(&self) -> Option<&CameraConfig> { self.config.as_ref() } + /// Start the camera so that it's ready to capture images. + /// + /// This should only be called once, future calls will do nothing and the camera's streams cannot be configured while it is started. + /// # Returns + /// On success returns whether the stream was newly started (i.e. false means the stream was already running). + /// This will fail if the camera has not been properly configured, or if libcamera decides to not work. + /// # Panics + /// This will panic if the buffer sizes produced by libcamera extend past the end of the actual camera memory buffer. + pub fn start_stream(&mut self) -> Result { + if self.started { + // Do nothing if the camera was already started. + return Ok(false); + } + self.started = true; + // Ok so: + // The camera contains streams, each stream has multiple buffers and each buffer has multiple planes. + // Each request to the camera operates on one buffer on one stream and fills the buffer with data from that stream. + // To start the camera we must allocate the buffers for all the streams and save them somewhere for future reading. + // We also must create a request for each buffer that we can re-use later every time we need an image. + // Technically requests can have multiple buffers, but I don't think know why this would be the case and I don't think it's necessary. + + // For each stream... + for stream_config in self + .config + .as_ref() + .ok_or(LibcameraError::InvalidConfig)? + .streams() + { + let mut stream = unsafe { stream_config.get_inner().get().stream() }; + // Allocate buffers + let _buffer_count = unsafe { self.allocator.get_mut().allocate(stream.get_mut()) }; + let mut camera_stream = CameraStream { + next_buffer: 0, + buffers: Vec::new(), + }; + // Create requests and map memory + for mut buffer in unsafe { self.allocator.get().buffers(stream.get_mut()) } { + let mut request = unsafe { self.inner.get_mut().create_request() }?; + let mut planes = Vec::new(); + let mut mapped_buffers: HashMap, usize, usize)> = + HashMap::new(); + for plane in unsafe { buffer.get().planes() } { + let fd = unsafe { plane.get().get_fd() }; + let mapped_buffer = mapped_buffers + .entry(fd) + .or_insert((None, 0, unsafe { ffi::fd_len(fd) }?)); + let length = mapped_buffer.2; + let plane_offset = unsafe { plane.get().get_offset() }; + let plane_length = unsafe { plane.get().get_length() }; + if plane_offset + plane_length > length { + panic!( + "Plane is out of buffer: buffer length = {length}, plane offset = {}, plane length = {}", + unsafe { plane.get().get_offset() }, + unsafe { plane.get().get_length() }, + ); + } + mapped_buffer.1 = mapped_buffer.1.max(plane_offset + plane_length); + } + for plane in unsafe { buffer.get().planes() } { + let fd = unsafe { plane.get().get_fd() }; + let mapped_buffer = mapped_buffers.get_mut(&fd).unwrap(); + if mapped_buffer.0.is_none() { + mapped_buffer.0 = Some(unsafe { ffi::mmap_plane(fd, mapped_buffer.1) }?); + } + planes.push(unsafe { + mapped_buffer + .0 + .as_mut() + .unwrap() + .get_mut() + .sub_buffer(plane.get().get_offset(), plane.get().get_length()) + }?); + } + + unsafe { request.get_mut().add_buffer(stream.get(), buffer.get_mut()) }?; + camera_stream.buffers.push((request, planes)); + } + self.streams.push(camera_stream); + } + unsafe { self.inner.get_mut().start() }?; + Ok(true) + } + /// Start the process to capture an image from the camera. + pub fn capture_next_picture(&mut self, stream: usize) -> Result<()> { + let mut stream = &mut self.streams[stream]; + let mut buffer = &mut stream.buffers[stream.next_buffer]; + unsafe { self.inner.get_mut().queue_request(buffer.0.get_mut()) }?; + stream.next_buffer += 1; + Ok(()) + } } impl Drop for Camera<'_> { fn drop(&mut self) { + self.streams = Vec::new(); + unsafe { self.inner.get_mut().stop() }.unwrap(); unsafe { self.inner.get_mut().release() }.unwrap(); } } diff --git a/src/config.rs b/src/config.rs index 63484fd..2c14808 100644 --- a/src/config.rs +++ b/src/config.rs @@ -51,6 +51,9 @@ impl StreamConfig { pub(crate) fn wrap_inner(inner: ffi::BindStreamConfiguration) -> Self { StreamConfig { inner } } + pub(crate) fn get_inner(&self) -> &ffi::BindStreamConfiguration { + &self.inner + } /// Set the pixel format for this stream to a [DefaultPixelFormat] pub fn set_default_pixel_format(&mut self, fmt: DefaultPixelFormat) { unsafe { diff --git a/src/test.rs b/src/test.rs index 85f3d34..d8f5cc9 100644 --- a/src/test.rs +++ b/src/test.rs @@ -12,4 +12,10 @@ fn test_camera() { stream.set_default_pixel_format(DefaultPixelFormat::Yuyv); stream.set_size(640, 480); println!("Configuration applied: {:?}", cam.apply_config().unwrap()); + cam.start_stream().unwrap(); + println!("Started stream."); + cam.capture_next_picture(0).unwrap(); + println!("Capturing single frame..."); + std::thread::sleep(std::time::Duration::from_millis(100)); + //println!("Saving planes..."); } From 6f81cf334064562f5d6d56c1d24dde8be592c674 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Thu, 30 Jun 2022 17:02:20 -0600 Subject: [PATCH 15/49] feat: polling events from the camera --- libcamera-bridge/camera.cpp | 27 +++++++++++++++++++++++++-- libcamera-bridge/core.hpp | 16 +++++++++++++--- makefile | 2 +- src/bridge.rs | 18 +++++++++++++++++- src/bridge/test.rs | 4 +++- src/camera.rs | 2 +- 6 files changed, 60 insertions(+), 9 deletions(-) diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index 6535372..0cbbc56 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -56,10 +56,10 @@ void Camera::configure(CameraConfiguration &conf) { } } -BindRequest Camera::create_request() { +BindRequest Camera::create_request(unsigned long cookie) { VALIDATE_POINTERS() - std::unique_ptr req = this->inner->createRequest(); + std::unique_ptr req = this->inner->createRequest(cookie); if (!req) { throw(BindErrorCode) ENODEV; } @@ -96,3 +96,26 @@ void Camera::stop() { throw(BindErrorCode)(-ret); } } + +Camera::Camera(std::shared_ptr inner_) + : inner{std::move(inner_)} { + this->inner->bufferCompleted.connect( + this, [&](libcamera::Request *req, libcamera::FrameBuffer *fb) { + this->message_mutex.lock(); + this->message_queue.push(CameraMessage{ + .message_type = CameraMessageType::BufferComplete, + .request_cookie = req->cookie(), + .buffer_cookie = fb->cookie(), + }); + this->message_mutex.unlock(); + }); +} + +rust::Vec Camera::poll_events() { + rust::Vec messages; + while (!this->message_queue.empty()) { + messages.push_back(this->message_queue.front()); + message_queue.pop(); + } + return messages; +} diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 7015bd2..bc540fa 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -3,6 +3,9 @@ #pragma once +#include +#include + #include #include #include @@ -33,6 +36,9 @@ struct Size; struct Request; enum class DefaultPixelFormat; +enum class CameraMessageType; +struct CameraMessage; + using CameraConfigurationStatus = libcamera::CameraConfiguration::Status; // Make sure this->inner is non-null @@ -61,9 +67,11 @@ struct Camera { private: std::shared_ptr inner; + std::mutex message_mutex; + std::queue message_queue; + public: - explicit Camera(std::shared_ptr inner_) - : inner{std::move(inner_)} {} + explicit Camera(std::shared_ptr inner_); std::shared_ptr into_shared(); void acquire(); @@ -71,10 +79,12 @@ struct Camera { BindCameraConfiguration generate_configuration( rust::Slice /*roles*/); void configure(CameraConfiguration &conf); - BindRequest create_request(); + BindRequest create_request(unsigned long cookie); void queue_request(Request &req); void start(); void stop(); + + rust::Vec poll_events(); }; struct CameraConfiguration { diff --git a/makefile b/makefile index bad67cb..94486f1 100644 --- a/makefile +++ b/makefile @@ -10,6 +10,6 @@ check: f: format fmt: format format: - clang-tidy --format-style=file --fix --fix-errors --fix-notes libcamera-bridge/*.cpp -- -I/usr/local/include/libcamera -I./target/cxxbridge -I.. --std=c++17 clang-format -style=file -i libcamera-bridge/* cargo fmt + clang-tidy --format-style=file --fix --fix-errors --fix-notes libcamera-bridge/*.cpp -- -I/usr/local/include/libcamera -I./target/cxxbridge -I.. --std=c++17 diff --git a/src/bridge.rs b/src/bridge.rs index 940ea98..378896a 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -168,6 +168,20 @@ pub mod ffi { inner: UniquePtr, } + #[repr(i32)] + #[derive(Debug)] + enum CameraMessageType { + RequestComplete, + BufferComplete, + } + + #[derive(Debug)] + struct CameraMessage { + message_type: CameraMessageType, + request_cookie: u64, + buffer_cookie: u32, + } + unsafe extern "C++" { include!("libcamera-rs/libcamera-bridge/core.hpp"); @@ -194,11 +208,13 @@ pub mod ffi { self: Pin<&mut Camera>, conf: Pin<&mut CameraConfiguration>, ) -> Result<()>; - pub unsafe fn create_request(self: Pin<&mut Camera>) -> Result; + pub unsafe fn create_request(self: Pin<&mut Camera>, cookie: u64) -> Result; pub unsafe fn queue_request(self: Pin<&mut Camera>, req: Pin<&mut Request>) -> Result<()>; pub unsafe fn start(self: Pin<&mut Camera>) -> Result<()>; pub unsafe fn stop(self: Pin<&mut Camera>) -> Result<()>; + pub unsafe fn poll_events(self: Pin<&mut Camera>) -> Vec; + type CameraConfiguration; pub unsafe fn size(self: &CameraConfiguration) -> usize; pub unsafe fn at( diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 9b8353f..3e7b8a9 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -54,7 +54,7 @@ fn test_unsafe_camera() { let mut requests = Vec::new(); let mut planes = Vec::new(); for mut buffer in unsafe { allocator.get().buffers(stream.get_mut()) } { - let mut request = unsafe { camera.get_mut().create_request() }.unwrap(); + let mut request = unsafe { camera.get_mut().create_request(69) }.unwrap(); let mut mapped_buffers: HashMap, usize, usize)> = HashMap::new(); @@ -106,6 +106,8 @@ fn test_unsafe_camera() { std::thread::sleep(std::time::Duration::from_millis(1000)); + println!("Events: {:?}", unsafe { camera.get_mut().poll_events() }); + for (i, plane) in planes.iter_mut().enumerate() { std::fs::write(&format!("plane_{i}.jpeg"), unsafe { plane.get().read_to_vec() diff --git a/src/camera.rs b/src/camera.rs index 254a36c..a36a853 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -143,7 +143,7 @@ impl Camera<'_> { }; // Create requests and map memory for mut buffer in unsafe { self.allocator.get().buffers(stream.get_mut()) } { - let mut request = unsafe { self.inner.get_mut().create_request() }?; + let mut request = unsafe { self.inner.get_mut().create_request(420) }?; let mut planes = Vec::new(); let mut mapped_buffers: HashMap, usize, usize)> = HashMap::new(); From acc56e921efc096a94c8ff400ac627d3590efb5d Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Mon, 4 Jul 2022 11:21:04 -0600 Subject: [PATCH 16/49] feat: camera events --- libcamera-bridge/camera.cpp | 42 ++++++++++++++++++++----------- libcamera-bridge/core.hpp | 4 +++ libcamera-bridge/frame_buffer.cpp | 12 +++++++++ src/bridge.rs | 4 ++- src/bridge/test.rs | 2 ++ src/camera.rs | 2 +- src/test.rs | 2 +- 7 files changed, 51 insertions(+), 17 deletions(-) diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index 0cbbc56..f493f06 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -2,6 +2,34 @@ #include "libcamera-rs/src/bridge.rs.h" +Camera::Camera(std::shared_ptr inner_) + : inner{std::move(inner_)} { + this->inner->bufferCompleted.connect( + this, [&](libcamera::Request *req, libcamera::FrameBuffer *fb) { + this->message_mutex.lock(); + this->message_queue.push(CameraMessage{ + .message_type = CameraMessageType::BufferComplete, + .request_cookie = req->cookie(), + .buffer_cookie = fb->cookie(), + }); + this->message_mutex.unlock(); + }); + this->inner->requestCompleted.connect(this, [&](libcamera::Request *req) { + this->message_mutex.lock(); + this->message_queue.push(CameraMessage{ + .message_type = CameraMessageType::RequestComplete, + .request_cookie = req->cookie(), + .buffer_cookie = 0, + }); + this->message_mutex.unlock(); + }); +} + +Camera::~Camera() { + this->inner->bufferCompleted.disconnect(); + this->inner->requestCompleted.disconnect(); +} + std::shared_ptr Camera::into_shared() { VALIDATE_POINTERS() @@ -97,20 +125,6 @@ void Camera::stop() { } } -Camera::Camera(std::shared_ptr inner_) - : inner{std::move(inner_)} { - this->inner->bufferCompleted.connect( - this, [&](libcamera::Request *req, libcamera::FrameBuffer *fb) { - this->message_mutex.lock(); - this->message_queue.push(CameraMessage{ - .message_type = CameraMessageType::BufferComplete, - .request_cookie = req->cookie(), - .buffer_cookie = fb->cookie(), - }); - this->message_mutex.unlock(); - }); -} - rust::Vec Camera::poll_events() { rust::Vec messages; while (!this->message_queue.empty()) { diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index bc540fa..fe121fe 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -72,6 +72,7 @@ struct Camera { public: explicit Camera(std::shared_ptr inner_); + ~Camera(); std::shared_ptr into_shared(); void acquire(); @@ -184,7 +185,10 @@ struct FrameBuffer { public: explicit FrameBuffer(libcamera::FrameBuffer *inner_) : inner(inner_) {} libcamera::FrameBuffer *into_ptr(); + [[nodiscard]] rust::Vec planes() const; + void set_cookie(unsigned int cookie); + unsigned int get_cookie() const; }; size_t fd_len(int fd); diff --git a/libcamera-bridge/frame_buffer.cpp b/libcamera-bridge/frame_buffer.cpp index 9218f4e..07172e0 100644 --- a/libcamera-bridge/frame_buffer.cpp +++ b/libcamera-bridge/frame_buffer.cpp @@ -21,3 +21,15 @@ rust::Vec FrameBuffer::planes() const { } return vec; } + +void FrameBuffer::set_cookie(unsigned int cookie) { + VALIDATE_POINTERS() + + this->inner->setCookie(cookie); +} + +unsigned int FrameBuffer::get_cookie() const { + VALIDATE_POINTERS() + + return this->inner->cookie(); +} diff --git a/src/bridge.rs b/src/bridge.rs index 378896a..b84d439 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -234,7 +234,7 @@ pub mod ffi { pub unsafe fn get_size(self: &StreamConfiguration) -> BindSize; pub unsafe fn set_buffer_count(self: Pin<&mut StreamConfiguration>, buffer_count: usize); pub unsafe fn get_buffer_count(self: &StreamConfiguration) -> usize; - pub unsafe fn raw_to_string(self: &StreamConfiguration) -> String; + pub fn raw_to_string(self: &StreamConfiguration) -> String; type PixelFormat; pub fn get_default_pixel_format(default_format: DefaultPixelFormat) -> BindPixelFormat; @@ -268,6 +268,8 @@ pub mod ffi { type FrameBuffer; pub unsafe fn planes(self: &FrameBuffer) -> Vec; + pub unsafe fn set_cookie(self: Pin<&mut FrameBuffer>, cookie: u32); + pub unsafe fn get_cookie(self: &FrameBuffer) -> u32; type FrameBufferPlane; pub unsafe fn get_fd(self: &FrameBufferPlane) -> i32; diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 3e7b8a9..d8f71cb 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -56,6 +56,8 @@ fn test_unsafe_camera() { for mut buffer in unsafe { allocator.get().buffers(stream.get_mut()) } { let mut request = unsafe { camera.get_mut().create_request(69) }.unwrap(); + unsafe { buffer.get_mut().set_cookie(420) }; + let mut mapped_buffers: HashMap, usize, usize)> = HashMap::new(); for plane in unsafe { buffer.get().planes() } { diff --git a/src/camera.rs b/src/camera.rs index a36a853..fb01a7a 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -191,7 +191,7 @@ impl Camera<'_> { /// Start the process to capture an image from the camera. pub fn capture_next_picture(&mut self, stream: usize) -> Result<()> { let mut stream = &mut self.streams[stream]; - let mut buffer = &mut stream.buffers[stream.next_buffer]; + let buffer = &mut stream.buffers[stream.next_buffer]; unsafe { self.inner.get_mut().queue_request(buffer.0.get_mut()) }?; stream.next_buffer += 1; Ok(()) diff --git a/src/test.rs b/src/test.rs index d8f5cc9..6bf2737 100644 --- a/src/test.rs +++ b/src/test.rs @@ -17,5 +17,5 @@ fn test_camera() { cam.capture_next_picture(0).unwrap(); println!("Capturing single frame..."); std::thread::sleep(std::time::Duration::from_millis(100)); - //println!("Saving planes..."); + println!("Saving planes..."); } From 912dac0e35f0c5ad44b4878ca15cbf5dc85c9802 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Mon, 4 Jul 2022 14:10:18 -0600 Subject: [PATCH 17/49] feat: capture images via safe api --- .gitignore | 2 +- src/camera.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++------ src/lib.rs | 2 ++ src/prelude.rs | 2 +- src/test.rs | 28 ++++++++++++---- 5 files changed, 103 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index c7c6b0e..dfff5ae 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ /target Cargo.lock -plane_*.jpeg +plane_* diff --git a/src/camera.rs b/src/camera.rs index fb01a7a..22fcb63 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -54,9 +54,18 @@ impl Drop for CameraManager { } } +struct CameraBuffer { + buffer_id: u32, + buffer: ffi::BindFrameBuffer, + request: Option, + planes: Vec, +} + struct CameraStream { + stream_id: u32, + stream: ffi::BindStream, next_buffer: usize, - buffers: Vec<(ffi::BindRequest, Vec)>, + buffers: Vec, } /// Represents a camera @@ -137,13 +146,17 @@ impl Camera<'_> { let mut stream = unsafe { stream_config.get_inner().get().stream() }; // Allocate buffers let _buffer_count = unsafe { self.allocator.get_mut().allocate(stream.get_mut()) }; + let stream_id = self.streams.len() as u32; let mut camera_stream = CameraStream { + stream_id, + stream, next_buffer: 0, buffers: Vec::new(), }; - // Create requests and map memory - for mut buffer in unsafe { self.allocator.get().buffers(stream.get_mut()) } { - let mut request = unsafe { self.inner.get_mut().create_request(420) }?; + // Map memory for buffers + for mut buffer in unsafe { self.allocator.get().buffers(camera_stream.stream.get_mut()) } { + let buffer_id = camera_stream.buffers.len() as u32; + unsafe { buffer.get_mut().set_cookie(buffer_id) }; let mut planes = Vec::new(); let mut mapped_buffers: HashMap, usize, usize)> = HashMap::new(); @@ -180,8 +193,12 @@ impl Camera<'_> { }?); } - unsafe { request.get_mut().add_buffer(stream.get(), buffer.get_mut()) }?; - camera_stream.buffers.push((request, planes)); + camera_stream.buffers.push(CameraBuffer { + buffer_id, + request: None, + buffer, + planes, + }); } self.streams.push(camera_stream); } @@ -192,9 +209,52 @@ impl Camera<'_> { pub fn capture_next_picture(&mut self, stream: usize) -> Result<()> { let mut stream = &mut self.streams[stream]; let buffer = &mut stream.buffers[stream.next_buffer]; - unsafe { self.inner.get_mut().queue_request(buffer.0.get_mut()) }?; - stream.next_buffer += 1; - Ok(()) + if buffer.request.is_none() { + let request_id = buffer.buffer_id as u64 | ((stream.stream_id as u64) << 32); + println!("Queuing request with id {}", request_id); + let mut req = unsafe { self.inner.get_mut().create_request(request_id) }?; + unsafe { + req + .get_mut() + .add_buffer(stream.stream.get(), buffer.buffer.get_mut()) + }?; + unsafe { self.inner.get_mut().queue_request(req.get_mut()) }?; + buffer.request = Some(req); + stream.next_buffer += 1; + stream.next_buffer %= stream.buffers.len(); + Ok(()) + } else { + Err(LibcameraError::NoBufferReady) + } + } + pub fn poll_events(&mut self) -> Result> { + let events = unsafe { self.inner.get_mut().poll_events() }; + Ok( + events + .into_iter() + .flat_map(|event| match event.message_type { + ffi::CameraMessageType::RequestComplete => { + println!("Ev: {event:?}"); + let stream_id = ((event.request_cookie >> 32) & 0xFFFFFFFF) as usize; + let buffer_id = (event.request_cookie & 0xFFFFFFFF) as usize; + println!( + "Request completed on stream {}, buffer {}.", + stream_id, buffer_id + ); + let buffer = &mut self.streams[stream_id].buffers[buffer_id]; + buffer.request = None; + Some(CameraEvent::RequestComplete( + buffer + .planes + .iter() + .map(|plane| unsafe { plane.get().read_to_vec() }) + .collect(), + )) + } + _ => None, + }) + .collect(), + ) } } @@ -206,8 +266,15 @@ impl Drop for Camera<'_> { } } +/// Represents an event from the camera +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CameraEvent { + /// Triggered when a capture request has completed, containing a vec of the resulting image planes. + RequestComplete(Vec>), +} + /// Represents the result of applying a configuration to a camera. -#[derive(Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ConfigStatus { /// The configuration was applied to the camera unchanged Unchanged, diff --git a/src/lib.rs b/src/lib.rs index a2b313f..ee29d59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,8 @@ pub enum LibcameraError { IntConversion(#[from] std::num::TryFromIntError), #[error("Configuration Invalid or Missing")] InvalidConfig, + #[error("No buffer ready for capture (all buffers in use, capture pictures slower!)")] + NoBufferReady, } type Result = std::result::Result; diff --git a/src/prelude.rs b/src/prelude.rs index 82d6433..0c749ab 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,2 +1,2 @@ -pub use crate::camera::{CameraManager, StreamRole}; +pub use crate::camera::{CameraEvent, CameraManager, StreamRole}; pub use crate::config::DefaultPixelFormat; diff --git a/src/test.rs b/src/test.rs index 6bf2737..a924c25 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,4 +1,4 @@ -use crate::prelude::{CameraManager, DefaultPixelFormat, StreamRole}; +use crate::prelude::{CameraEvent, CameraManager, DefaultPixelFormat, StreamRole}; #[test] fn test_camera() { @@ -9,13 +9,29 @@ fn test_camera() { let conf = cam.generate_config(&[StreamRole::StillCapture]).unwrap(); println!("conf: {conf:?}"); let stream = &mut conf.streams_mut()[0]; - stream.set_default_pixel_format(DefaultPixelFormat::Yuyv); + stream.set_default_pixel_format(DefaultPixelFormat::Mjpeg); stream.set_size(640, 480); println!("Configuration applied: {:?}", cam.apply_config().unwrap()); cam.start_stream().unwrap(); println!("Started stream."); - cam.capture_next_picture(0).unwrap(); - println!("Capturing single frame..."); - std::thread::sleep(std::time::Duration::from_millis(100)); - println!("Saving planes..."); + println!("Capturing frames..."); + for i in 0..10 { + cam.capture_next_picture(0).unwrap(); + println!("Capturing image #{}", i); + std::thread::sleep(std::time::Duration::from_millis(100)); + let events = cam.poll_events().unwrap(); + for event in events { + match event { + CameraEvent::RequestComplete(planes) => { + println!("Got request back with {} image planes", planes.len()); + for (j, plane) in planes.iter().enumerate() { + let filename = format!("plane_{}_{}.jpeg", i, j); + std::fs::write(&filename, plane).unwrap(); + println!("Saved plane to '{filename}'."); + } + } + } + } + } + println!("Done!"); } From 02d7e1df509740bb944b8f5a46a5bfbcdfab6f83 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Tue, 5 Jul 2022 10:36:27 -0600 Subject: [PATCH 18/49] feat: camera control values --- .cargo/config | 2 + .gitmodules | 3 + build.rs | 4 +- gnutls | 1 + libcamera-bridge/camera.cpp | 29 +++++-- libcamera-bridge/camera_configuration.cpp | 2 +- libcamera-bridge/camera_manager.cpp | 4 +- libcamera-bridge/control_id.cpp | 21 +++++ libcamera-bridge/control_value.cpp | 54 +++++++++++++ libcamera-bridge/core.hpp | 54 +++++++++++-- libcamera-bridge/fd.cpp | 4 +- libcamera-bridge/frame_buffer_allocator.cpp | 4 +- libcamera-bridge/memory_buffer.cpp | 4 +- libcamera-bridge/pixel_format.cpp | 4 +- libcamera-bridge/request.cpp | 32 +++++++- src/bridge.rs | 87 +++++++++++++++++---- src/bridge/test.rs | 26 +++++- src/camera.rs | 2 +- 18 files changed, 292 insertions(+), 45 deletions(-) create mode 100644 .cargo/config create mode 160000 gnutls create mode 100644 libcamera-bridge/control_id.cpp create mode 100644 libcamera-bridge/control_value.cpp diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..3c32d25 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" diff --git a/.gitmodules b/.gitmodules index 74c1c37..b3388b8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "libcamera"] path = libcamera url = https://git.libcamera.org/libcamera/libcamera.git +[submodule "gnutls"] + path = gnutls + url = https://gitlab.com/gnutls/gnutls.git diff --git a/build.rs b/build.rs index 90715a7..87c94c7 100644 --- a/build.rs +++ b/build.rs @@ -13,6 +13,8 @@ fn main() { .file("libcamera-bridge/fd.cpp") .file("libcamera-bridge/memory_buffer.cpp") .file("libcamera-bridge/request.cpp") + .file("libcamera-bridge/control_id.cpp") + .file("libcamera-bridge/control_value.cpp") .flag_if_supported("-std=c++17") .include("/usr/local/include/libcamera") .include("libcamera/build/include/libcamera") @@ -20,7 +22,7 @@ fn main() { println!("cargo:rerun-if-changed=src/bridge.rs"); println!("cargo:rerun-if-changed=libcamera-bridge/*.cpp"); - println!("cargo:rerun-if-changed=libcamera-bridge/*.hpp"); + println!("cargo:rerun-if-changed=libcamera-bridge/core.hpp"); // link libcamera println!("cargo:rustc-link-lib=dylib=camera"); diff --git a/gnutls b/gnutls new file mode 160000 index 0000000..41c6b26 --- /dev/null +++ b/gnutls @@ -0,0 +1 @@ +Subproject commit 41c6b26cf099803fc2515ca800e0439f152d5a0f diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index f493f06..dc09bef 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -41,7 +41,7 @@ void Camera::acquire() { int ret = this->inner->acquire(); if (ret < 0) { - throw(BindErrorCode)(-ret); + throw error_from_code(-ret); } } @@ -50,7 +50,7 @@ void Camera::release() { int ret = this->inner->release(); if (ret < 0) { - throw(BindErrorCode)(-ret); + throw error_from_code(-ret); } } @@ -66,7 +66,7 @@ Camera::generate_configuration(rust::Slice roles) { std::unique_ptr conf = this->inner->generateConfiguration(roles_vec); if (!conf) { - throw(BindErrorCode) ENODEV; + throw error_from_code(ENODEV); } BindCameraConfiguration bind_conf{ @@ -80,7 +80,7 @@ void Camera::configure(CameraConfiguration &conf) { int ret = this->inner->configure(conf.into_ptr()); if (ret < 0) { - throw(BindErrorCode)(-ret); + throw error_from_code(-ret); } } @@ -89,7 +89,7 @@ BindRequest Camera::create_request(unsigned long cookie) { std::unique_ptr req = this->inner->createRequest(cookie); if (!req) { - throw(BindErrorCode) ENODEV; + throw error_from_code(ENODEV); } BindRequest bind_req{ @@ -103,7 +103,7 @@ void Camera::queue_request(Request &req) { int ret = this->inner->queueRequest(req.into_ptr()); if (ret < 0) { - throw(BindErrorCode)(-ret); + throw error_from_code(-ret); } } @@ -112,7 +112,7 @@ void Camera::start() { int ret = this->inner->start(); if (ret < 0) { - throw(BindErrorCode)(-ret); + throw error_from_code(-ret); } } @@ -121,10 +121,23 @@ void Camera::stop() { int ret = this->inner->stop(); if (ret < 0) { - throw(BindErrorCode)(-ret); + throw error_from_code(-ret); } } +rust::Vec Camera::get_controls() const { + VALIDATE_POINTERS() + + rust::Vec controls; + for (const auto &[control, _value] : this->inner->controls()) { + BindControlId control_id{ + .inner = std::make_unique(control), + }; + controls.push_back(std::move(control_id)); + } + return controls; +} + rust::Vec Camera::poll_events() { rust::Vec messages; while (!this->message_queue.empty()) { diff --git a/libcamera-bridge/camera_configuration.cpp b/libcamera-bridge/camera_configuration.cpp index 64312f2..8fe5711 100644 --- a/libcamera-bridge/camera_configuration.cpp +++ b/libcamera-bridge/camera_configuration.cpp @@ -19,7 +19,7 @@ BindStreamConfiguration CameraConfiguration::at(unsigned int idx) { libcamera::StreamConfiguration *str = &this->inner->at(idx); if (str == nullptr) { - throw(BindErrorCode) ENODEV; + throw std::runtime_error("No stream configuration with specified id."); } BindStreamConfiguration conf{ .inner = std::make_unique(str), diff --git a/libcamera-bridge/camera_manager.cpp b/libcamera-bridge/camera_manager.cpp index dbffeb9..993dba8 100644 --- a/libcamera-bridge/camera_manager.cpp +++ b/libcamera-bridge/camera_manager.cpp @@ -15,7 +15,7 @@ void CameraManager::start() { int ret = this->inner->start(); if (ret < 0) { - throw(BindErrorCode)(-ret); + throw error_from_code(-ret); } } @@ -40,7 +40,7 @@ BindCamera CameraManager::get_camera_by_id(rust::Str id) { std::shared_ptr cam = this->inner->get(std::string(id)); if (!cam) { - throw(BindErrorCode) ENODEV; + throw error_from_code(ENODEV); } BindCamera bind_cam{ .inner = std::make_unique(cam), diff --git a/libcamera-bridge/control_id.cpp b/libcamera-bridge/control_id.cpp new file mode 100644 index 0000000..095c47a --- /dev/null +++ b/libcamera-bridge/control_id.cpp @@ -0,0 +1,21 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +rust::String ControlId::get_name() const { + VALIDATE_POINTERS() + + return this->inner->name(); +} + +unsigned int ControlId::get_id() const { + VALIDATE_POINTERS() + + return this->inner->id(); +} + +CameraControlType ControlId::get_type() const { + VALIDATE_POINTERS() + + return static_cast(this->inner->type()); +} diff --git a/libcamera-bridge/control_value.cpp b/libcamera-bridge/control_value.cpp new file mode 100644 index 0000000..bc3ea26 --- /dev/null +++ b/libcamera-bridge/control_value.cpp @@ -0,0 +1,54 @@ +#include "core.hpp" + +#include "libcamera-rs/src/bridge.rs.h" + +BindControlValue new_control_value_bool(bool value) { + BindControlValue control_value{ + .inner = std::make_unique(libcamera::ControlValue(value)), + }; + return control_value; +} + +BindControlValue new_control_value_byte(unsigned char value) { + BindControlValue control_value{ + .inner = std::make_unique(libcamera::ControlValue(value)), + }; + return control_value; +} + +BindControlValue new_control_value_i32(int value) { + BindControlValue control_value{ + .inner = std::make_unique(libcamera::ControlValue(value)), + }; + return control_value; +} + +BindControlValue new_control_value_i64(long int value) { + BindControlValue control_value{ + .inner = std::make_unique(libcamera::ControlValue(value)), + }; + return control_value; +} + +BindControlValue new_control_value_f32(float value) { + BindControlValue control_value{ + .inner = std::make_unique(libcamera::ControlValue(value)), + }; + return control_value; +} + +BindControlValue new_control_value_string(rust::String value) { + BindControlValue control_value{ + .inner = std::make_unique( + libcamera::ControlValue(static_cast(value))), + }; + return control_value; +} + +const libcamera::ControlValue &ControlValue::get_inner() const { + return this->inner; +} + +rust::String ControlValue::raw_to_string() const { + return this->inner.toString(); +} diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index fe121fe..f723c23 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -1,8 +1,9 @@ -#ifndef _HOME_BEN1JEN_GITLAB_LIBCAMERA_RS_LIBCAMERA_BRIDGE_CORE_HPP -#define _HOME_BEN1JEN_GITLAB_LIBCAMERA_RS_LIBCAMERA_BRIDGE_CORE_HPP +#ifndef _LIBCAMERA_BRIDGE_CORE_HPP +#define _LIBCAMERA_BRIDGE_CORE_HPP #pragma once +#include #include #include @@ -29,22 +30,30 @@ struct BindFrameBufferPlane; struct BindFd; struct BindMemoryBuffer; struct BindRequest; +struct BindControlId; +struct BindControlValue; struct CameraConfiguration; struct PixelFormat; struct Size; struct Request; -enum class DefaultPixelFormat; +struct ControlValue; +enum class DefaultPixelFormat; +enum class CameraControlType; enum class CameraMessageType; struct CameraMessage; using CameraConfigurationStatus = libcamera::CameraConfiguration::Status; +static inline std::runtime_error error_from_code(int code) { + return std::runtime_error(strerror(code)); +} + // Make sure this->inner is non-null #define VALIDATE_POINTERS() \ if (!this->inner) { \ - throw(BindErrorCode) EFAULT; \ + throw std::runtime_error("Inner pointer invalid."); \ } BindCameraManager make_camera_manager(); @@ -70,10 +79,13 @@ struct Camera { std::mutex message_mutex; std::queue message_queue; + std::unordered_map controls_by_id; + public: explicit Camera(std::shared_ptr inner_); ~Camera(); std::shared_ptr into_shared(); + const libcamera::ControlId *get_control_by_id(unsigned int id) const; void acquire(); void release(); @@ -84,7 +96,7 @@ struct Camera { void queue_request(Request &req); void start(); void stop(); - + rust::Vec get_controls() const; rust::Vec poll_events(); }; @@ -233,6 +245,38 @@ struct Request { libcamera::Request *into_ptr(); void add_buffer(const Stream &stream, FrameBuffer &buffer); + BindControlValue get_control(unsigned int id) const; + void set_control(unsigned int id, const ControlValue &value); + [[nodiscard]] rust::String raw_to_string() const; +}; + +struct ControlId { +private: + const libcamera::ControlId *inner; + +public: + explicit ControlId(const libcamera::ControlId *inner_) : inner{inner_} {} + + rust::String get_name() const; + unsigned int get_id() const; + CameraControlType get_type() const; +}; + +BindControlValue new_control_value_bool(bool value); +BindControlValue new_control_value_byte(unsigned char value); +BindControlValue new_control_value_i32(int value); +BindControlValue new_control_value_i64(long int value); +BindControlValue new_control_value_f32(float value); +BindControlValue new_control_value_string(rust::String value); + +struct ControlValue { +private: + const libcamera::ControlValue inner; + +public: + explicit ControlValue(libcamera::ControlValue inner_) : inner{inner_} {} + const libcamera::ControlValue &get_inner() const; + [[nodiscard]] rust::String raw_to_string() const; }; diff --git a/libcamera-bridge/fd.cpp b/libcamera-bridge/fd.cpp index 0e44400..421f9b4 100644 --- a/libcamera-bridge/fd.cpp +++ b/libcamera-bridge/fd.cpp @@ -8,7 +8,7 @@ size_t fd_len(int fd) { long ret = lseek(fd, 0, SEEK_END); if (ret < 0) { - throw(BindErrorCode) errno; + throw error_from_code(errno); } return ret; } @@ -18,7 +18,7 @@ BindMemoryBuffer mmap_plane(int fd, size_t len) { // MAP_FAILED expands to a silly C-style cast. // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) if ((address == nullptr) || address == MAP_FAILED) { - throw(BindErrorCode) errno; + throw error_from_code(errno); } BindMemoryBuffer buffer{.inner = std::make_unique( static_cast(address), len)}; diff --git a/libcamera-bridge/frame_buffer_allocator.cpp b/libcamera-bridge/frame_buffer_allocator.cpp index 9bec5f6..ae421cf 100644 --- a/libcamera-bridge/frame_buffer_allocator.cpp +++ b/libcamera-bridge/frame_buffer_allocator.cpp @@ -16,7 +16,7 @@ size_t FrameBufferAllocator::allocate(Stream &stream) { int ret = this->inner->allocate(stream.into_ptr()); if (ret < 0) { - throw(BindErrorCode)(-ret); + throw error_from_code(-ret); } return ret; @@ -27,7 +27,7 @@ void FrameBufferAllocator::free(Stream &stream) { int ret = this->inner->free(stream.into_ptr()); if (ret < 0) { - throw(BindErrorCode)(-ret); + throw error_from_code(-ret); } } diff --git a/libcamera-bridge/memory_buffer.cpp b/libcamera-bridge/memory_buffer.cpp index a89d445..823008c 100644 --- a/libcamera-bridge/memory_buffer.cpp +++ b/libcamera-bridge/memory_buffer.cpp @@ -4,9 +4,7 @@ BindMemoryBuffer MemoryBuffer::sub_buffer(size_t offset, size_t length) { if (offset > this->length || offset + length > this->length) { - // The thrown error is an int and so is copyable. - // NOLINTNEXTLINE(cert-err09-cpp,cert-err61-cpp) - throw BindErrorCode::EFault; + throw std::runtime_error("Sub buffer out of range of outer buffer."); } BindMemoryBuffer buffer{ // The point of this class is to safely wrap raw memory pointers. diff --git a/libcamera-bridge/pixel_format.cpp b/libcamera-bridge/pixel_format.cpp index 3f09c8e..e174dff 100644 --- a/libcamera-bridge/pixel_format.cpp +++ b/libcamera-bridge/pixel_format.cpp @@ -42,9 +42,7 @@ BindPixelFormat get_default_pixel_format(DefaultPixelFormat default_format) { break; } if (fmt == nullptr) { - // The thrown error is an int and so is copyable. - // NOLINTNEXTLINE(cert-err09-cpp,cert-err61-cpp) - throw BindErrorCode::EFault; + throw std::runtime_error("Unknown default pixel format."); } BindPixelFormat pixel_format{ .inner = std::make_unique(*fmt), diff --git a/libcamera-bridge/request.cpp b/libcamera-bridge/request.cpp index e36fdd2..978f811 100644 --- a/libcamera-bridge/request.cpp +++ b/libcamera-bridge/request.cpp @@ -7,7 +7,7 @@ void Request::add_buffer(const Stream &stream, FrameBuffer &buffer) { int ret = this->inner->addBuffer(stream.into_ptr(), buffer.into_ptr()); if (ret < 0) { - throw(BindErrorCode)(-ret); + throw error_from_code(-ret); } } @@ -17,4 +17,32 @@ libcamera::Request *Request::into_ptr() { return this->inner.get(); } -rust::String Request::raw_to_string() const { return this->inner->toString(); } +#include + +BindControlValue Request::get_control(unsigned int id) const { + VALIDATE_POINTERS() + + libcamera::ControlList &controls = this->inner->controls(); + + if (!controls.contains(id)) { + throw std::runtime_error("No control with specified id."); + } + BindControlValue control_value{ + .inner = std::make_unique(controls.get(id)), + }; + return control_value; +} + +void Request::set_control(unsigned int id, const ControlValue &value) { + VALIDATE_POINTERS() + + libcamera::ControlList &controls = this->inner->controls(); + + controls.set(id, value.get_inner()); +} + +rust::String Request::raw_to_string() const { + VALIDATE_POINTERS() + + return this->inner->toString(); +} diff --git a/src/bridge.rs b/src/bridge.rs index b84d439..db97946 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -167,6 +167,12 @@ pub mod ffi { struct BindRequest { inner: UniquePtr, } + struct BindControlId { + inner: UniquePtr, + } + struct BindControlValue { + inner: UniquePtr, + } #[repr(i32)] #[derive(Debug)] @@ -182,7 +188,21 @@ pub mod ffi { buffer_cookie: u32, } - unsafe extern "C++" { + #[repr(i32)] + #[derive(Debug)] + enum CameraControlType { + None = 0, + Bool = 1, + Byte = 2, + Integer32 = 3, + Integer64 = 4, + Float = 5, + String = 6, + Rectangle = 7, + Size = 8, + } + + extern "C++" { include!("libcamera-rs/libcamera-bridge/core.hpp"); #[namespace = "libcamera"] @@ -190,7 +210,7 @@ pub mod ffi { type CameraConfigurationStatus; type CameraManager; - pub fn make_camera_manager() -> BindCameraManager; + pub unsafe fn make_camera_manager() -> BindCameraManager; pub unsafe fn start(self: Pin<&mut CameraManager>) -> Result<()>; pub unsafe fn stop(self: Pin<&mut CameraManager>); @@ -212,7 +232,7 @@ pub mod ffi { pub unsafe fn queue_request(self: Pin<&mut Camera>, req: Pin<&mut Request>) -> Result<()>; pub unsafe fn start(self: Pin<&mut Camera>) -> Result<()>; pub unsafe fn stop(self: Pin<&mut Camera>) -> Result<()>; - + pub unsafe fn get_controls(self: &Camera) -> Vec; pub unsafe fn poll_events(self: Pin<&mut Camera>) -> Vec; type CameraConfiguration; @@ -234,24 +254,25 @@ pub mod ffi { pub unsafe fn get_size(self: &StreamConfiguration) -> BindSize; pub unsafe fn set_buffer_count(self: Pin<&mut StreamConfiguration>, buffer_count: usize); pub unsafe fn get_buffer_count(self: &StreamConfiguration) -> usize; - pub fn raw_to_string(self: &StreamConfiguration) -> String; + pub unsafe fn raw_to_string(self: &StreamConfiguration) -> String; type PixelFormat; - pub fn get_default_pixel_format(default_format: DefaultPixelFormat) -> BindPixelFormat; - pub fn raw_to_string(self: &PixelFormat) -> String; + pub unsafe fn get_default_pixel_format(default_format: DefaultPixelFormat) -> BindPixelFormat; + pub unsafe fn raw_to_string(self: &PixelFormat) -> String; type Size; - pub fn new_size(width: u32, height: u32) -> BindSize; - pub fn set_width(self: Pin<&mut Size>, width: u32); - pub fn get_width(self: &Size) -> u32; - pub fn set_height(self: Pin<&mut Size>, height: u32); - pub fn get_height(self: &Size) -> u32; - pub fn raw_to_string(self: &Size) -> String; + pub unsafe fn new_size(width: u32, height: u32) -> BindSize; + pub unsafe fn set_width(self: Pin<&mut Size>, width: u32); + pub unsafe fn get_width(self: &Size) -> u32; + pub unsafe fn set_height(self: Pin<&mut Size>, height: u32); + pub unsafe fn get_height(self: &Size) -> u32; + pub unsafe fn raw_to_string(self: &Size) -> String; type Stream; type FrameBufferAllocator; - pub fn make_frame_buffer_allocator(camera: Pin<&mut Camera>) -> BindFrameBufferAllocator; + pub unsafe fn make_frame_buffer_allocator(camera: Pin<&mut Camera>) + -> BindFrameBufferAllocator; pub unsafe fn allocate( self: Pin<&mut FrameBufferAllocator>, @@ -294,8 +315,26 @@ pub mod ffi { stream: &Stream, buffer: Pin<&mut FrameBuffer>, ) -> Result<()>; + pub unsafe fn get_control(self: &Request, id: u32) -> Result; + pub unsafe fn set_control(self: Pin<&mut Request>, id: u32, value: &ControlValue); + pub unsafe fn raw_to_string(self: &Request) -> String; + + type ControlId; + + pub unsafe fn get_name(self: &ControlId) -> String; + pub unsafe fn get_id(self: &ControlId) -> u32; + pub unsafe fn get_type(self: &ControlId) -> CameraControlType; - pub fn raw_to_string(self: &Request) -> String; + type ControlValue; + + pub unsafe fn new_control_value_bool(value: bool) -> BindControlValue; + pub unsafe fn new_control_value_byte(value: u8) -> BindControlValue; + pub unsafe fn new_control_value_i32(value: i32) -> BindControlValue; + pub unsafe fn new_control_value_i64(value: i64) -> BindControlValue; + pub unsafe fn new_control_value_f32(value: f32) -> BindControlValue; + pub unsafe fn new_control_value_string(value: String) -> BindControlValue; + + pub unsafe fn raw_to_string(self: &ControlValue) -> String; } } @@ -426,3 +465,23 @@ unsafe impl GetInner for ffi::BindRequest { self.inner.pin_mut() } } + +unsafe impl GetInner for ffi::BindControlId { + type Inner = ffi::ControlId; + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} + +unsafe impl GetInner for ffi::BindControlValue { + type Inner = ffi::ControlValue; + unsafe fn get(&self) -> &Self::Inner { + &self.inner + } + unsafe fn get_mut(&mut self) -> Pin<&mut Self::Inner> { + self.inner.pin_mut() + } +} diff --git a/src/bridge/test.rs b/src/bridge/test.rs index d8f71cb..8905b45 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; #[test] fn test_unsafe_camera() { - let mut cm = ffi::make_camera_manager(); + let mut cm = unsafe { ffi::make_camera_manager() }; unsafe { cm.get_mut().start() }.unwrap(); let camera_ids = unsafe { cm.get().get_camera_ids() }; println!("Available Cameras: {camera_ids:?}"); @@ -46,6 +46,16 @@ fn test_unsafe_camera() { unsafe { camera.get_mut().configure(config.get_mut()) }.unwrap(); + let controls = unsafe { camera.get().get_controls() }; + for control in &controls { + println!( + "Camera control '{}': ID={}, type={:?}", + unsafe { control.get().get_name() }, + unsafe { control.get().get_id() }, + unsafe { control.get().get_type() } + ); + } + let mut stream = unsafe { stream_config.get().stream() }; let buffer_count = unsafe { allocator.get_mut().allocate(stream.get_mut()) }.unwrap(); @@ -56,6 +66,20 @@ fn test_unsafe_camera() { for mut buffer in unsafe { allocator.get().buffers(stream.get_mut()) } { let mut request = unsafe { camera.get_mut().create_request(69) }.unwrap(); + unsafe { + request + .get_mut() + .set_control(9, ffi::new_control_value_f32(0.5).get()) + }; + for control in &controls { + println!( + "Control '{}' value in current request: {:?}", + unsafe { control.get().get_id() }, + unsafe { request.get().get_control(control.get().get_id()) } + .map(|c| unsafe { c.get().raw_to_string() }) + ); + } + unsafe { buffer.get_mut().set_cookie(420) }; let mut mapped_buffers: HashMap, usize, usize)> = diff --git a/src/camera.rs b/src/camera.rs index 22fcb63..266024b 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -22,7 +22,7 @@ impl fmt::Debug for CameraManager { impl CameraManager { /// Constructs a new camera manager pub fn new() -> Result { - let mut cm = ffi::make_camera_manager(); + let mut cm = unsafe { ffi::make_camera_manager() }; // The primary safety concern for the CM is that it must be started once before calling all functions. unsafe { cm.get_mut().start() }?; Ok(CameraManager { inner: cm }) From c0d5cf17ee001f8580b0ce242896d79f315175d4 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 6 Jul 2022 12:02:17 -0600 Subject: [PATCH 19/49] Add camera controls --- libcamera-bridge/camera.cpp | 27 +- libcamera-bridge/control_value.cpp | 37 ++- libcamera-bridge/core.hpp | 11 +- src/bridge.rs | 17 +- src/bridge/test.rs | 15 +- src/camera.rs | 422 ++++++++++++++++++++++++++++- src/lib.rs | 2 + 7 files changed, 512 insertions(+), 19 deletions(-) diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index dc09bef..4cde8b2 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -125,15 +125,30 @@ void Camera::stop() { } } -rust::Vec Camera::get_controls() const { +rust::Vec Camera::get_controls() const { VALIDATE_POINTERS() - rust::Vec controls; - for (const auto &[control, _value] : this->inner->controls()) { - BindControlId control_id{ - .inner = std::make_unique(control), + rust::Vec controls; + for (const auto &[control, value] : this->inner->controls()) { + ControlPair control_pair{ + .id = + { + .inner = std::make_unique(control), + }, + .min = + { + .inner = std::make_unique(value.min()), + }, + .max = + { + .inner = std::make_unique(value.max()), + }, + .value = + { + .inner = std::make_unique(value.def()), + }, }; - controls.push_back(std::move(control_id)); + controls.push_back(std::move(control_pair)); } return controls; } diff --git a/libcamera-bridge/control_value.cpp b/libcamera-bridge/control_value.cpp index bc3ea26..792220f 100644 --- a/libcamera-bridge/control_value.cpp +++ b/libcamera-bridge/control_value.cpp @@ -9,7 +9,7 @@ BindControlValue new_control_value_bool(bool value) { return control_value; } -BindControlValue new_control_value_byte(unsigned char value) { +BindControlValue new_control_value_u8(unsigned char value) { BindControlValue control_value{ .inner = std::make_unique(libcamera::ControlValue(value)), }; @@ -49,6 +49,41 @@ const libcamera::ControlValue &ControlValue::get_inner() const { return this->inner; } +bool ControlValue::get_bool() const { + if (this->inner.type() != libcamera::ControlType::ControlTypeBool) { + throw std::runtime_error("Bad type! Expected Bool."); + } + return this->inner.get(); +} + +unsigned char ControlValue::get_u8() const { + if (this->inner.type() != libcamera::ControlType::ControlTypeByte) { + throw std::runtime_error("Bad type! Expected Byte."); + } + return this->inner.get(); +} + +int ControlValue::get_i32() const { + if (this->inner.type() != libcamera::ControlType::ControlTypeInteger32) { + throw std::runtime_error("Bad type! Expected I32."); + } + return this->inner.get(); +} + +long ControlValue::get_i64() const { + if (this->inner.type() != libcamera::ControlType::ControlTypeInteger64) { + throw std::runtime_error("Bad type! Expected I64."); + } + return this->inner.get(); +} + +float ControlValue::get_f32() const { + if (this->inner.type() != libcamera::ControlType::ControlTypeFloat) { + throw std::runtime_error("Bad type! Expected Float."); + } + return this->inner.get(); +} + rust::String ControlValue::raw_to_string() const { return this->inner.toString(); } diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index f723c23..3dfaa36 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -38,6 +38,7 @@ struct PixelFormat; struct Size; struct Request; struct ControlValue; +struct ControlPair; enum class DefaultPixelFormat; enum class CameraControlType; @@ -96,7 +97,7 @@ struct Camera { void queue_request(Request &req); void start(); void stop(); - rust::Vec get_controls() const; + rust::Vec get_controls() const; rust::Vec poll_events(); }; @@ -263,7 +264,7 @@ struct ControlId { }; BindControlValue new_control_value_bool(bool value); -BindControlValue new_control_value_byte(unsigned char value); +BindControlValue new_control_value_u8(unsigned char value); BindControlValue new_control_value_i32(int value); BindControlValue new_control_value_i64(long int value); BindControlValue new_control_value_f32(float value); @@ -277,6 +278,12 @@ struct ControlValue { explicit ControlValue(libcamera::ControlValue inner_) : inner{inner_} {} const libcamera::ControlValue &get_inner() const; + bool get_bool() const; + unsigned char get_u8() const; + int get_i32() const; + long get_i64() const; + float get_f32() const; + [[nodiscard]] rust::String raw_to_string() const; }; diff --git a/src/bridge.rs b/src/bridge.rs index db97946..4dab4a0 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -202,6 +202,13 @@ pub mod ffi { Size = 8, } + struct ControlPair { + id: BindControlId, + min: BindControlValue, + max: BindControlValue, + value: BindControlValue, + } + extern "C++" { include!("libcamera-rs/libcamera-bridge/core.hpp"); @@ -232,7 +239,7 @@ pub mod ffi { pub unsafe fn queue_request(self: Pin<&mut Camera>, req: Pin<&mut Request>) -> Result<()>; pub unsafe fn start(self: Pin<&mut Camera>) -> Result<()>; pub unsafe fn stop(self: Pin<&mut Camera>) -> Result<()>; - pub unsafe fn get_controls(self: &Camera) -> Vec; + pub unsafe fn get_controls(self: &Camera) -> Vec; pub unsafe fn poll_events(self: Pin<&mut Camera>) -> Vec; type CameraConfiguration; @@ -328,12 +335,18 @@ pub mod ffi { type ControlValue; pub unsafe fn new_control_value_bool(value: bool) -> BindControlValue; - pub unsafe fn new_control_value_byte(value: u8) -> BindControlValue; + pub unsafe fn new_control_value_u8(value: u8) -> BindControlValue; pub unsafe fn new_control_value_i32(value: i32) -> BindControlValue; pub unsafe fn new_control_value_i64(value: i64) -> BindControlValue; pub unsafe fn new_control_value_f32(value: f32) -> BindControlValue; pub unsafe fn new_control_value_string(value: String) -> BindControlValue; + pub unsafe fn get_bool(self: &ControlValue) -> Result; + pub unsafe fn get_u8(self: &ControlValue) -> Result; + pub unsafe fn get_i32(self: &ControlValue) -> Result; + pub unsafe fn get_i64(self: &ControlValue) -> Result; + pub unsafe fn get_f32(self: &ControlValue) -> Result; + pub unsafe fn raw_to_string(self: &ControlValue) -> String; } } diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 8905b45..a70b78a 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -49,10 +49,11 @@ fn test_unsafe_camera() { let controls = unsafe { camera.get().get_controls() }; for control in &controls { println!( - "Camera control '{}': ID={}, type={:?}", - unsafe { control.get().get_name() }, - unsafe { control.get().get_id() }, - unsafe { control.get().get_type() } + "Camera control '{}': ID={}, type={:?}, value={}", + unsafe { control.id.get().get_name() }, + unsafe { control.id.get().get_id() }, + unsafe { control.id.get().get_type() }, + unsafe { control.value.get().raw_to_string() }, ); } @@ -69,13 +70,13 @@ fn test_unsafe_camera() { unsafe { request .get_mut() - .set_control(9, ffi::new_control_value_f32(0.5).get()) + .set_control(9, ffi::new_control_value_f32(-0.5).get()) }; for control in &controls { println!( "Control '{}' value in current request: {:?}", - unsafe { control.get().get_id() }, - unsafe { request.get().get_control(control.get().get_id()) } + unsafe { control.id.get().get_id() }, + unsafe { request.get().get_control(control.id.get().get_id()) } .map(|c| unsafe { c.get().raw_to_string() }) ); } diff --git a/src/camera.rs b/src/camera.rs index 266024b..90a6314 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; -use std::fmt; +use std::fmt::{self, Debug}; use std::marker::PhantomData; +use std::ops::RangeInclusive; use crate::bridge::{ffi, GetInner}; use crate::config::CameraConfig; @@ -36,6 +37,7 @@ impl CameraManager { let mut cam = unsafe { self.inner.get_mut().get_camera_by_id(name) }?; unsafe { cam.get_mut().acquire() }?; let allocator = unsafe { ffi::make_frame_buffer_allocator(cam.get_mut()) }; + let controls = CameraControls::from_libcamera(unsafe { cam.get().get_controls() }); Ok(Camera { _camera_manager: PhantomData, name: name.to_string(), @@ -44,6 +46,7 @@ impl CameraManager { allocator, streams: Vec::new(), started: false, + controls, }) } } @@ -61,6 +64,15 @@ struct CameraBuffer { planes: Vec, } +impl fmt::Debug for CameraBuffer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CameraBuffer") + .field("buffer_id", &self.buffer_id) + .field("plane_count", &self.planes.len()) + .finish_non_exhaustive() + } +} + struct CameraStream { stream_id: u32, stream: ffi::BindStream, @@ -68,6 +80,398 @@ struct CameraStream { buffers: Vec, } +impl fmt::Debug for CameraStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CameraStream") + .field("stream_id", &self.stream_id) + .field("next_buffer", &self.next_buffer) + .field("buffers", &self.buffers) + .finish_non_exhaustive() + } +} + +/// Contains a value with an acceptable minimum and maximum and a default. +#[derive(Debug)] +pub struct MinMaxValue { + range: RangeInclusive, + default: T, + value: T, +} + +impl MinMaxValue { + /// Creates a new MinMaxValue out of a given min, max, and default + /// + /// # Returns + /// Returns None if default is not within min and max. + pub fn new(min: T, max: T, default: T) -> Result> { + let range = min..=max; + if range.contains(&default) { + Ok(MinMaxValue { + range: min..=max, + default, + value: default, + }) + } else { + Err(LibcameraError::InvalidControlValue) + } + } + /// Retrieve the default value + pub fn get_default(&self) -> T { + self.default + } + /// Retrieve the minimum value + pub fn min(&self) -> T { + *self.range.start() + } + /// Retrieve the maximum value + pub fn max(&self) -> T { + *self.range.end() + } + /// Gets the stored value + /// It is gurenteed to lie within MinMaxValue::min() and MinMaxValue::max(). + pub fn get_value(&self) -> T { + self.value + } + /// Verifies that value lies within the acceptable range for this value + /// + /// # Returns + /// `true` if the value lies within the acceptable range for this value and was stored, `false` otherwise. + pub fn set_value(&mut self, value: T) -> bool { + if self.range.contains(&value) { + self.value = value; + true + } else { + false + } + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_bool() }?, + unsafe { pair.max.get().get_bool() }?, + unsafe { pair.value.get().get_bool() }?, + ) + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_u8() }?, + unsafe { pair.max.get().get_u8() }?, + unsafe { pair.value.get().get_u8() }?, + ) + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_i32() }?, + unsafe { pair.max.get().get_i32() }?, + unsafe { pair.value.get().get_i32() }?, + ) + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_i64() }?, + unsafe { pair.max.get().get_i64() }?, + unsafe { pair.value.get().get_i64() }?, + ) + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_f32() }?, + unsafe { pair.max.get().get_f32() }?, + unsafe { pair.value.get().get_f32() }?, + ) + } +} + +/// Represents a camera control value with an unknown type +/// +/// Most of the time you probably want to use `CameraControls` instead. +#[non_exhaustive] +#[derive(Debug)] +pub enum CameraControlValue { + None, + Bool(MinMaxValue), + Byte(MinMaxValue), + Integer32(MinMaxValue), + Integer64(MinMaxValue), + Float(MinMaxValue), + // String(MinMaxValue), + // Rectangle(MinMaxValue), + // Size(MinMaxValue), +} + +/// Stores camera controls. +/// +/// Common controls are fields on this struct +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct CameraControls { + pub ae_enable: Option>, + pub ae_metering_mode: Option>, + pub ae_constraint_mode: Option>, + pub ae_exposure_mode: Option>, + pub exposure_value: Option>, + pub exposure_time: Option>, + pub analogue_gain: Option>, + pub brightness: Option>, + pub contract: Option>, + pub awb_enable: Option>, + pub awb_mode: Option>, + pub colour_gains: Option>, + pub saturation: Option>, + pub sharpness: Option>, + pub colour_correction_matrix: Option>, + // pub scaler_crop: Option>, // Rectangle TODO + pub frame_duration_limits: Option>, + pub noise_reduction_mode: Option>, + pub others: HashMap, +} + +impl CameraControls { + fn from_libcamera(control_list: Vec) -> Self { + let mut controls = CameraControls::default(); + for control in control_list { + let name = unsafe { control.id.get().get_name() }; + let did_name_match = match name.as_ref() { + "AeEnable" => (&control) + .try_into() + .map(|control| controls.ae_enable = Some(control)) + .is_ok(), + "AeMeteringMode" => (&control) + .try_into() + .map(|control| controls.ae_metering_mode = Some(control)) + .is_ok(), + "AeConstraintMode" => (&control) + .try_into() + .map(|control| controls.ae_constraint_mode = Some(control)) + .is_ok(), + "AeExposureMode" => (&control) + .try_into() + .map(|control| controls.ae_exposure_mode = Some(control)) + .is_ok(), + "ExposureValue" => (&control) + .try_into() + .map(|control| controls.exposure_value = Some(control)) + .is_ok(), + "ExposureTime" => (&control) + .try_into() + .map(|control| controls.exposure_time = Some(control)) + .is_ok(), + "AnalogueGain" => (&control) + .try_into() + .map(|control| controls.analogue_gain = Some(control)) + .is_ok(), + "Brightness" => (&control) + .try_into() + .map(|control| controls.brightness = Some(control)) + .is_ok(), + "Contract" => (&control) + .try_into() + .map(|control| controls.contract = Some(control)) + .is_ok(), + "AwbEnable" => (&control) + .try_into() + .map(|control| controls.awb_enable = Some(control)) + .is_ok(), + "AwbMode" => (&control) + .try_into() + .map(|control| controls.awb_mode = Some(control)) + .is_ok(), + "ColourGains" => (&control) + .try_into() + .map(|control| controls.colour_gains = Some(control)) + .is_ok(), + "Saturation" => (&control) + .try_into() + .map(|control| controls.saturation = Some(control)) + .is_ok(), + "Sharpness" => (&control) + .try_into() + .map(|control| controls.sharpness = Some(control)) + .is_ok(), + "ColourCorrectionMatrix" => (&control) + .try_into() + .map(|control| controls.colour_correction_matrix = Some(control)) + .is_ok(), + // "ScalerCrop" => (&control).try_into().map(|control| controls.scaler_crop = Some(control)).is_ok(), + "FrameDurationLimits" => (&control) + .try_into() + .map(|control| controls.frame_duration_limits = Some(control)) + .is_ok(), + "NoiseReductionMode" => (&control) + .try_into() + .map(|control| controls.noise_reduction_mode = Some(control)) + .is_ok(), + _ => false, + }; + if !did_name_match { + if let Some(control_value) = match unsafe { control.id.get().get_type() } { + ffi::CameraControlType::None => Some(CameraControlValue::None), + ffi::CameraControlType::Bool => (&control).try_into().ok().map(CameraControlValue::Bool), + ffi::CameraControlType::Byte => (&control).try_into().ok().map(CameraControlValue::Byte), + ffi::CameraControlType::Integer32 => (&control) + .try_into() + .ok() + .map(CameraControlValue::Integer32), + ffi::CameraControlType::Integer64 => (&control) + .try_into() + .ok() + .map(CameraControlValue::Integer64), + ffi::CameraControlType::Float => { + (&control).try_into().ok().map(CameraControlValue::Float) + } + _ => None, + // ffi::CameraControlType::String => (&control).try_into().ok().map(|control| CameraControlValue::String(control)), + // ffi::CameraControlType::Rectangle => (&control).try_into().ok().map(|control| CameraControlValue::Rectangle(control)), + // ffi::CameraControlType::Size => (&control).try_into().ok().map(|control| CameraControlValue::Size(control)), + } { + controls + .others + .insert(unsafe { control.id.get().get_id() }, (name, control_value)); + } else { + eprintln!("Camera control with conflicting types: {name}"); + } + } + } + controls + } + fn get_libcamera(&self) -> Vec<(u32, ffi::BindControlValue)> { + let mut controls = Vec::new(); + if let Some(ae_enable) = &self.ae_enable { + controls.push((1, unsafe { + ffi::new_control_value_bool(ae_enable.get_value()) + })); + } + if let Some(ae_metering_mode) = &self.ae_metering_mode { + controls.push((3, unsafe { + ffi::new_control_value_i32(ae_metering_mode.get_value()) + })); + } + if let Some(ae_constraint_mode) = &self.ae_constraint_mode { + controls.push((4, unsafe { + ffi::new_control_value_i32(ae_constraint_mode.get_value()) + })); + } + if let Some(ae_exposure_mode) = &self.ae_exposure_mode { + controls.push((5, unsafe { + ffi::new_control_value_i32(ae_exposure_mode.get_value()) + })); + } + if let Some(exposure_value) = &self.exposure_value { + controls.push((6, unsafe { + ffi::new_control_value_f32(exposure_value.get_value()) + })); + } + if let Some(exposure_time) = &self.exposure_time { + controls.push((7, unsafe { + ffi::new_control_value_i32(exposure_time.get_value()) + })); + } + if let Some(analogue_gain) = &self.analogue_gain { + controls.push((8, unsafe { + ffi::new_control_value_f32(analogue_gain.get_value()) + })); + } + if let Some(brightness) = &self.brightness { + controls.push((9, unsafe { + ffi::new_control_value_f32(brightness.get_value()) + })); + } + if let Some(contract) = &self.contract { + controls.push((10, unsafe { + ffi::new_control_value_f32(contract.get_value()) + })); + } + if let Some(awb_enable) = &self.awb_enable { + controls.push((12, unsafe { + ffi::new_control_value_bool(awb_enable.get_value()) + })); + } + if let Some(awb_mode) = &self.awb_mode { + controls.push((13, unsafe { + ffi::new_control_value_i32(awb_mode.get_value()) + })); + } + if let Some(colour_gains) = &self.colour_gains { + controls.push((15, unsafe { + ffi::new_control_value_f32(colour_gains.get_value()) + })); + } + if let Some(saturation) = &self.saturation { + controls.push((17, unsafe { + ffi::new_control_value_f32(saturation.get_value()) + })); + } + if let Some(sharpness) = &self.sharpness { + controls.push((19, unsafe { + ffi::new_control_value_f32(sharpness.get_value()) + })); + } + if let Some(colour_correction_matrix) = &self.colour_correction_matrix { + controls.push((21, unsafe { + ffi::new_control_value_f32(colour_correction_matrix.get_value()) + })); + } + // if let Some(scaler_crop) = &self.scaler_crop { + // controls.push((22, unsafe { ffi::new_control_value_rectangle(scaler_crop.get_value()) })); + // } + if let Some(frame_duration_limits) = &self.frame_duration_limits { + controls.push((25, unsafe { + ffi::new_control_value_i64(frame_duration_limits.get_value()) + })); + } + if let Some(noise_reduction_mode) = &self.noise_reduction_mode { + controls.push((39, unsafe { + ffi::new_control_value_i32(noise_reduction_mode.get_value()) + })); + } + for (id, (_name, value)) in &self.others { + if let Some(value) = match value { + CameraControlValue::None => None, + CameraControlValue::Bool(value) => { + Some(unsafe { ffi::new_control_value_bool(value.get_value()) }) + } + CameraControlValue::Byte(value) => { + Some(unsafe { ffi::new_control_value_u8(value.get_value()) }) + } + CameraControlValue::Integer32(value) => { + Some(unsafe { ffi::new_control_value_i32(value.get_value()) }) + } + CameraControlValue::Integer64(value) => { + Some(unsafe { ffi::new_control_value_i64(value.get_value()) }) + } + CameraControlValue::Float(value) => { + Some(unsafe { ffi::new_control_value_f32(value.get_value()) }) + } + // CameraControlValue::String(value) => Some(unsafe { ffi::new_control_value_string(value.get_value()) }), + // CameraControlValue::Rectangle(value) => Some(unsafe { ffi::new_control_value_rectangle(value.get_value()) }), + // CameraControlValue::Size(value) => Some(unsafe { ffi::new_control_value_size(value.get_value()) }), + } { + controls.push((*id, value)); + } + } + controls + } +} + /// Represents a camera pub struct Camera<'a> { _camera_manager: PhantomData<&'a CameraManager>, @@ -77,12 +481,17 @@ pub struct Camera<'a> { allocator: ffi::BindFrameBufferAllocator, streams: Vec, started: bool, + controls: CameraControls, } impl fmt::Debug for Camera<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Camera") .field("name", &self.name) + .field("config", &self.config) + .field("streams", &self.streams) + .field("started", &self.started) + .field("controls", &self.controls) .finish_non_exhaustive() } } @@ -115,6 +524,14 @@ impl Camera<'_> { pub fn get_config(&self) -> Option<&CameraConfig> { self.config.as_ref() } + /// Borrow the camera's controls + pub fn get_controls(&self) -> &CameraControls { + &self.controls + } + /// Borrow the camera's controls mutably + pub fn get_controls_mut(&mut self) -> &mut CameraControls { + &mut self.controls + } /// Start the camera so that it's ready to capture images. /// /// This should only be called once, future calls will do nothing and the camera's streams cannot be configured while it is started. @@ -218,6 +635,9 @@ impl Camera<'_> { .get_mut() .add_buffer(stream.stream.get(), buffer.buffer.get_mut()) }?; + for (control_id, control_value) in self.controls.get_libcamera() { + unsafe { req.get_mut().set_control(control_id, control_value.get()) }; + } unsafe { self.inner.get_mut().queue_request(req.get_mut()) }?; buffer.request = Some(req); stream.next_buffer += 1; diff --git a/src/lib.rs b/src/lib.rs index ee29d59..2560335 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,8 @@ pub enum LibcameraError { InvalidConfig, #[error("No buffer ready for capture (all buffers in use, capture pictures slower!)")] NoBufferReady, + #[error("Control value out of range!")] + InvalidControlValue, } type Result = std::result::Result; From 2cf4874c9922f36e8a48dd14910e33a992bf55c5 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 6 Jul 2022 15:50:10 -0600 Subject: [PATCH 20/49] feat: image decoding Also some cleanup. --- .gitignore | 3 +- Cargo.toml | 3 + libcamera-bridge/core.hpp | 1 + libcamera-bridge/pixel_format.cpp | 37 +++ src/bridge.rs | 4 +- src/bridge/test.rs | 2 +- src/camera.rs | 530 +++++++----------------------- src/config.rs | 28 +- src/controls.rs | 396 ++++++++++++++++++++++ src/image.rs | 311 ++++++++++++++++++ src/lib.rs | 12 +- src/prelude.rs | 2 +- src/test.rs | 34 +- 13 files changed, 930 insertions(+), 433 deletions(-) create mode 100644 src/controls.rs create mode 100644 src/image.rs diff --git a/.gitignore b/.gitignore index dfff5ae..d92ccbc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ /target Cargo.lock -plane_* +plane_*.bin +image_*.png diff --git a/Cargo.toml b/Cargo.toml index da65f74..02b5b04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,10 @@ edition = "2021" [dependencies] cxx = "1.0" thiserror = "1" +image = { version = "0.24", optional = true } [build-dependencies] cxx-build = "1.0" +[features] +default = [ "image" ] diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 3dfaa36..dde6b78 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -144,6 +144,7 @@ struct PixelFormat { explicit PixelFormat(libcamera::PixelFormat inner_) : inner(inner_) {} libcamera::PixelFormat into_inner(); + DefaultPixelFormat as_default_pixel_format() const; [[nodiscard]] rust::String raw_to_string() const; }; diff --git a/libcamera-bridge/pixel_format.cpp b/libcamera-bridge/pixel_format.cpp index e174dff..f76b12d 100644 --- a/libcamera-bridge/pixel_format.cpp +++ b/libcamera-bridge/pixel_format.cpp @@ -50,6 +50,43 @@ BindPixelFormat get_default_pixel_format(DefaultPixelFormat default_format) { return pixel_format; } +DefaultPixelFormat PixelFormat::as_default_pixel_format() const { + if (this->inner == libcamera::formats::R8) { + return DefaultPixelFormat::R8; + } + if (this->inner == libcamera::formats::RGB888) { + return DefaultPixelFormat::Rgb888; + } + if (this->inner == libcamera::formats::RGB565) { + return DefaultPixelFormat::Rgb565; + } + if (this->inner == libcamera::formats::BGR888) { + return DefaultPixelFormat::Bgr888; + } + if (this->inner == libcamera::formats::YUYV) { + return DefaultPixelFormat::Yuyv; + } + if (this->inner == libcamera::formats::YVYU) { + return DefaultPixelFormat::Yvyu; + } + if (this->inner == libcamera::formats::YUV420) { + return DefaultPixelFormat::Yuv420; + } + if (this->inner == libcamera::formats::YUV422) { + return DefaultPixelFormat::Yuv422; + } + if (this->inner == libcamera::formats::YVU422) { + return DefaultPixelFormat::Yvu422; + } + if (this->inner == libcamera::formats::YUV444) { + return DefaultPixelFormat::Yuv444; + } + if (this->inner == libcamera::formats::MJPEG) { + return DefaultPixelFormat::Mjpeg; + } + throw std::runtime_error("Unknown pixel format."); +} + libcamera::PixelFormat PixelFormat::into_inner() { return this->inner; } rust::String PixelFormat::raw_to_string() const { diff --git a/src/bridge.rs b/src/bridge.rs index 4dab4a0..c00f2c3 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -263,8 +263,10 @@ pub mod ffi { pub unsafe fn get_buffer_count(self: &StreamConfiguration) -> usize; pub unsafe fn raw_to_string(self: &StreamConfiguration) -> String; - type PixelFormat; pub unsafe fn get_default_pixel_format(default_format: DefaultPixelFormat) -> BindPixelFormat; + + type PixelFormat; + pub unsafe fn as_default_pixel_format(self: &PixelFormat) -> Result; pub unsafe fn raw_to_string(self: &PixelFormat) -> String; type Size; diff --git a/src/bridge/test.rs b/src/bridge/test.rs index a70b78a..4c2b989 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -136,7 +136,7 @@ fn test_unsafe_camera() { println!("Events: {:?}", unsafe { camera.get_mut().poll_events() }); for (i, plane) in planes.iter_mut().enumerate() { - std::fs::write(&format!("plane_{i}.jpeg"), unsafe { + std::fs::write(&format!("plane_{i}.bin"), unsafe { plane.get().read_to_vec() }) .unwrap(); diff --git a/src/camera.rs b/src/camera.rs index 90a6314..a08e804 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,10 +1,12 @@ use std::collections::HashMap; use std::fmt::{self, Debug}; use std::marker::PhantomData; -use std::ops::RangeInclusive; +use std::time::Instant; use crate::bridge::{ffi, GetInner}; -use crate::config::CameraConfig; +use crate::config::{CameraConfig, PixelFormat}; +use crate::controls::CameraControls; +use crate::image::{self, CameraImage, MultiImage}; use crate::{LibcameraError, Result}; pub use ffi::StreamRole; @@ -14,7 +16,7 @@ pub struct CameraManager { inner: ffi::BindCameraManager, } -impl fmt::Debug for CameraManager { +impl Debug for CameraManager { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("CameraManager").finish_non_exhaustive() } @@ -47,6 +49,8 @@ impl CameraManager { streams: Vec::new(), started: false, controls, + next_request_id: 0, + request_infos: HashMap::new(), }) } } @@ -58,418 +62,44 @@ impl Drop for CameraManager { } struct CameraBuffer { - buffer_id: u32, buffer: ffi::BindFrameBuffer, request: Option, planes: Vec, } -impl fmt::Debug for CameraBuffer { +impl Debug for CameraBuffer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("CameraBuffer") - .field("buffer_id", &self.buffer_id) .field("plane_count", &self.planes.len()) .finish_non_exhaustive() } } struct CameraStream { - stream_id: u32, + pixel_format: Option, + width: u32, + height: u32, stream: ffi::BindStream, next_buffer: usize, buffers: Vec, } -impl fmt::Debug for CameraStream { +impl Debug for CameraStream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("CameraStream") - .field("stream_id", &self.stream_id) + .field("pixel_format", &self.pixel_format) + .field("width", &self.width) + .field("height", &self.height) .field("next_buffer", &self.next_buffer) .field("buffers", &self.buffers) .finish_non_exhaustive() } } -/// Contains a value with an acceptable minimum and maximum and a default. -#[derive(Debug)] -pub struct MinMaxValue { - range: RangeInclusive, - default: T, - value: T, -} - -impl MinMaxValue { - /// Creates a new MinMaxValue out of a given min, max, and default - /// - /// # Returns - /// Returns None if default is not within min and max. - pub fn new(min: T, max: T, default: T) -> Result> { - let range = min..=max; - if range.contains(&default) { - Ok(MinMaxValue { - range: min..=max, - default, - value: default, - }) - } else { - Err(LibcameraError::InvalidControlValue) - } - } - /// Retrieve the default value - pub fn get_default(&self) -> T { - self.default - } - /// Retrieve the minimum value - pub fn min(&self) -> T { - *self.range.start() - } - /// Retrieve the maximum value - pub fn max(&self) -> T { - *self.range.end() - } - /// Gets the stored value - /// It is gurenteed to lie within MinMaxValue::min() and MinMaxValue::max(). - pub fn get_value(&self) -> T { - self.value - } - /// Verifies that value lies within the acceptable range for this value - /// - /// # Returns - /// `true` if the value lies within the acceptable range for this value and was stored, `false` otherwise. - pub fn set_value(&mut self, value: T) -> bool { - if self.range.contains(&value) { - self.value = value; - true - } else { - false - } - } -} - -impl TryFrom<&ffi::ControlPair> for MinMaxValue { - type Error = LibcameraError; - fn try_from(pair: &ffi::ControlPair) -> Result> { - MinMaxValue::new( - unsafe { pair.min.get().get_bool() }?, - unsafe { pair.max.get().get_bool() }?, - unsafe { pair.value.get().get_bool() }?, - ) - } -} - -impl TryFrom<&ffi::ControlPair> for MinMaxValue { - type Error = LibcameraError; - fn try_from(pair: &ffi::ControlPair) -> Result> { - MinMaxValue::new( - unsafe { pair.min.get().get_u8() }?, - unsafe { pair.max.get().get_u8() }?, - unsafe { pair.value.get().get_u8() }?, - ) - } -} - -impl TryFrom<&ffi::ControlPair> for MinMaxValue { - type Error = LibcameraError; - fn try_from(pair: &ffi::ControlPair) -> Result> { - MinMaxValue::new( - unsafe { pair.min.get().get_i32() }?, - unsafe { pair.max.get().get_i32() }?, - unsafe { pair.value.get().get_i32() }?, - ) - } -} - -impl TryFrom<&ffi::ControlPair> for MinMaxValue { - type Error = LibcameraError; - fn try_from(pair: &ffi::ControlPair) -> Result> { - MinMaxValue::new( - unsafe { pair.min.get().get_i64() }?, - unsafe { pair.max.get().get_i64() }?, - unsafe { pair.value.get().get_i64() }?, - ) - } -} - -impl TryFrom<&ffi::ControlPair> for MinMaxValue { - type Error = LibcameraError; - fn try_from(pair: &ffi::ControlPair) -> Result> { - MinMaxValue::new( - unsafe { pair.min.get().get_f32() }?, - unsafe { pair.max.get().get_f32() }?, - unsafe { pair.value.get().get_f32() }?, - ) - } -} - -/// Represents a camera control value with an unknown type -/// -/// Most of the time you probably want to use `CameraControls` instead. -#[non_exhaustive] -#[derive(Debug)] -pub enum CameraControlValue { - None, - Bool(MinMaxValue), - Byte(MinMaxValue), - Integer32(MinMaxValue), - Integer64(MinMaxValue), - Float(MinMaxValue), - // String(MinMaxValue), - // Rectangle(MinMaxValue), - // Size(MinMaxValue), -} - -/// Stores camera controls. -/// -/// Common controls are fields on this struct -#[non_exhaustive] -#[derive(Debug, Default)] -pub struct CameraControls { - pub ae_enable: Option>, - pub ae_metering_mode: Option>, - pub ae_constraint_mode: Option>, - pub ae_exposure_mode: Option>, - pub exposure_value: Option>, - pub exposure_time: Option>, - pub analogue_gain: Option>, - pub brightness: Option>, - pub contract: Option>, - pub awb_enable: Option>, - pub awb_mode: Option>, - pub colour_gains: Option>, - pub saturation: Option>, - pub sharpness: Option>, - pub colour_correction_matrix: Option>, - // pub scaler_crop: Option>, // Rectangle TODO - pub frame_duration_limits: Option>, - pub noise_reduction_mode: Option>, - pub others: HashMap, -} - -impl CameraControls { - fn from_libcamera(control_list: Vec) -> Self { - let mut controls = CameraControls::default(); - for control in control_list { - let name = unsafe { control.id.get().get_name() }; - let did_name_match = match name.as_ref() { - "AeEnable" => (&control) - .try_into() - .map(|control| controls.ae_enable = Some(control)) - .is_ok(), - "AeMeteringMode" => (&control) - .try_into() - .map(|control| controls.ae_metering_mode = Some(control)) - .is_ok(), - "AeConstraintMode" => (&control) - .try_into() - .map(|control| controls.ae_constraint_mode = Some(control)) - .is_ok(), - "AeExposureMode" => (&control) - .try_into() - .map(|control| controls.ae_exposure_mode = Some(control)) - .is_ok(), - "ExposureValue" => (&control) - .try_into() - .map(|control| controls.exposure_value = Some(control)) - .is_ok(), - "ExposureTime" => (&control) - .try_into() - .map(|control| controls.exposure_time = Some(control)) - .is_ok(), - "AnalogueGain" => (&control) - .try_into() - .map(|control| controls.analogue_gain = Some(control)) - .is_ok(), - "Brightness" => (&control) - .try_into() - .map(|control| controls.brightness = Some(control)) - .is_ok(), - "Contract" => (&control) - .try_into() - .map(|control| controls.contract = Some(control)) - .is_ok(), - "AwbEnable" => (&control) - .try_into() - .map(|control| controls.awb_enable = Some(control)) - .is_ok(), - "AwbMode" => (&control) - .try_into() - .map(|control| controls.awb_mode = Some(control)) - .is_ok(), - "ColourGains" => (&control) - .try_into() - .map(|control| controls.colour_gains = Some(control)) - .is_ok(), - "Saturation" => (&control) - .try_into() - .map(|control| controls.saturation = Some(control)) - .is_ok(), - "Sharpness" => (&control) - .try_into() - .map(|control| controls.sharpness = Some(control)) - .is_ok(), - "ColourCorrectionMatrix" => (&control) - .try_into() - .map(|control| controls.colour_correction_matrix = Some(control)) - .is_ok(), - // "ScalerCrop" => (&control).try_into().map(|control| controls.scaler_crop = Some(control)).is_ok(), - "FrameDurationLimits" => (&control) - .try_into() - .map(|control| controls.frame_duration_limits = Some(control)) - .is_ok(), - "NoiseReductionMode" => (&control) - .try_into() - .map(|control| controls.noise_reduction_mode = Some(control)) - .is_ok(), - _ => false, - }; - if !did_name_match { - if let Some(control_value) = match unsafe { control.id.get().get_type() } { - ffi::CameraControlType::None => Some(CameraControlValue::None), - ffi::CameraControlType::Bool => (&control).try_into().ok().map(CameraControlValue::Bool), - ffi::CameraControlType::Byte => (&control).try_into().ok().map(CameraControlValue::Byte), - ffi::CameraControlType::Integer32 => (&control) - .try_into() - .ok() - .map(CameraControlValue::Integer32), - ffi::CameraControlType::Integer64 => (&control) - .try_into() - .ok() - .map(CameraControlValue::Integer64), - ffi::CameraControlType::Float => { - (&control).try_into().ok().map(CameraControlValue::Float) - } - _ => None, - // ffi::CameraControlType::String => (&control).try_into().ok().map(|control| CameraControlValue::String(control)), - // ffi::CameraControlType::Rectangle => (&control).try_into().ok().map(|control| CameraControlValue::Rectangle(control)), - // ffi::CameraControlType::Size => (&control).try_into().ok().map(|control| CameraControlValue::Size(control)), - } { - controls - .others - .insert(unsafe { control.id.get().get_id() }, (name, control_value)); - } else { - eprintln!("Camera control with conflicting types: {name}"); - } - } - } - controls - } - fn get_libcamera(&self) -> Vec<(u32, ffi::BindControlValue)> { - let mut controls = Vec::new(); - if let Some(ae_enable) = &self.ae_enable { - controls.push((1, unsafe { - ffi::new_control_value_bool(ae_enable.get_value()) - })); - } - if let Some(ae_metering_mode) = &self.ae_metering_mode { - controls.push((3, unsafe { - ffi::new_control_value_i32(ae_metering_mode.get_value()) - })); - } - if let Some(ae_constraint_mode) = &self.ae_constraint_mode { - controls.push((4, unsafe { - ffi::new_control_value_i32(ae_constraint_mode.get_value()) - })); - } - if let Some(ae_exposure_mode) = &self.ae_exposure_mode { - controls.push((5, unsafe { - ffi::new_control_value_i32(ae_exposure_mode.get_value()) - })); - } - if let Some(exposure_value) = &self.exposure_value { - controls.push((6, unsafe { - ffi::new_control_value_f32(exposure_value.get_value()) - })); - } - if let Some(exposure_time) = &self.exposure_time { - controls.push((7, unsafe { - ffi::new_control_value_i32(exposure_time.get_value()) - })); - } - if let Some(analogue_gain) = &self.analogue_gain { - controls.push((8, unsafe { - ffi::new_control_value_f32(analogue_gain.get_value()) - })); - } - if let Some(brightness) = &self.brightness { - controls.push((9, unsafe { - ffi::new_control_value_f32(brightness.get_value()) - })); - } - if let Some(contract) = &self.contract { - controls.push((10, unsafe { - ffi::new_control_value_f32(contract.get_value()) - })); - } - if let Some(awb_enable) = &self.awb_enable { - controls.push((12, unsafe { - ffi::new_control_value_bool(awb_enable.get_value()) - })); - } - if let Some(awb_mode) = &self.awb_mode { - controls.push((13, unsafe { - ffi::new_control_value_i32(awb_mode.get_value()) - })); - } - if let Some(colour_gains) = &self.colour_gains { - controls.push((15, unsafe { - ffi::new_control_value_f32(colour_gains.get_value()) - })); - } - if let Some(saturation) = &self.saturation { - controls.push((17, unsafe { - ffi::new_control_value_f32(saturation.get_value()) - })); - } - if let Some(sharpness) = &self.sharpness { - controls.push((19, unsafe { - ffi::new_control_value_f32(sharpness.get_value()) - })); - } - if let Some(colour_correction_matrix) = &self.colour_correction_matrix { - controls.push((21, unsafe { - ffi::new_control_value_f32(colour_correction_matrix.get_value()) - })); - } - // if let Some(scaler_crop) = &self.scaler_crop { - // controls.push((22, unsafe { ffi::new_control_value_rectangle(scaler_crop.get_value()) })); - // } - if let Some(frame_duration_limits) = &self.frame_duration_limits { - controls.push((25, unsafe { - ffi::new_control_value_i64(frame_duration_limits.get_value()) - })); - } - if let Some(noise_reduction_mode) = &self.noise_reduction_mode { - controls.push((39, unsafe { - ffi::new_control_value_i32(noise_reduction_mode.get_value()) - })); - } - for (id, (_name, value)) in &self.others { - if let Some(value) = match value { - CameraControlValue::None => None, - CameraControlValue::Bool(value) => { - Some(unsafe { ffi::new_control_value_bool(value.get_value()) }) - } - CameraControlValue::Byte(value) => { - Some(unsafe { ffi::new_control_value_u8(value.get_value()) }) - } - CameraControlValue::Integer32(value) => { - Some(unsafe { ffi::new_control_value_i32(value.get_value()) }) - } - CameraControlValue::Integer64(value) => { - Some(unsafe { ffi::new_control_value_i64(value.get_value()) }) - } - CameraControlValue::Float(value) => { - Some(unsafe { ffi::new_control_value_f32(value.get_value()) }) - } - // CameraControlValue::String(value) => Some(unsafe { ffi::new_control_value_string(value.get_value()) }), - // CameraControlValue::Rectangle(value) => Some(unsafe { ffi::new_control_value_rectangle(value.get_value()) }), - // CameraControlValue::Size(value) => Some(unsafe { ffi::new_control_value_size(value.get_value()) }), - } { - controls.push((*id, value)); - } - } - controls - } +struct RequestInfo { + stream_id: usize, + buffer_id: usize, + timestamp: Instant, } /// Represents a camera @@ -482,9 +112,11 @@ pub struct Camera<'a> { streams: Vec, started: bool, controls: CameraControls, + next_request_id: u64, + request_infos: HashMap, } -impl fmt::Debug for Camera<'_> { +impl Debug for Camera<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Camera") .field("name", &self.name) @@ -492,6 +124,7 @@ impl fmt::Debug for Camera<'_> { .field("streams", &self.streams) .field("started", &self.started) .field("controls", &self.controls) + .field("next_request_id", &self.next_request_id) .finish_non_exhaustive() } } @@ -560,20 +193,24 @@ impl Camera<'_> { .ok_or(LibcameraError::InvalidConfig)? .streams() { + println!("Stream config: {stream_config:?}"); let mut stream = unsafe { stream_config.get_inner().get().stream() }; // Allocate buffers let _buffer_count = unsafe { self.allocator.get_mut().allocate(stream.get_mut()) }; - let stream_id = self.streams.len() as u32; + let (width, height) = stream_config.get_size(); let mut camera_stream = CameraStream { - stream_id, + pixel_format: stream_config.get_pixel_format(), + width, + height, stream, next_buffer: 0, buffers: Vec::new(), }; + println!("Camera stream: {camera_stream:?}"); // Map memory for buffers for mut buffer in unsafe { self.allocator.get().buffers(camera_stream.stream.get_mut()) } { - let buffer_id = camera_stream.buffers.len() as u32; - unsafe { buffer.get_mut().set_cookie(buffer_id) }; + let buffer_id = camera_stream.buffers.len(); + unsafe { buffer.get_mut().set_cookie(buffer_id as u32) }; let mut planes = Vec::new(); let mut mapped_buffers: HashMap, usize, usize)> = HashMap::new(); @@ -611,7 +248,6 @@ impl Camera<'_> { } camera_stream.buffers.push(CameraBuffer { - buffer_id, request: None, buffer, planes, @@ -623,12 +259,17 @@ impl Camera<'_> { Ok(true) } /// Start the process to capture an image from the camera. - pub fn capture_next_picture(&mut self, stream: usize) -> Result<()> { - let mut stream = &mut self.streams[stream]; + /// + /// # Returns + /// On success returns the `serial_id` of the request, which can be used to match with the correct request complete event. + /// + /// # Errors + /// Errors if there are no buffers currently available (all buffers are in-use, if this happens take pictures slower!) + pub fn capture_next_picture(&mut self, stream_id: usize) -> Result { + let mut stream = &mut self.streams[stream_id]; let buffer = &mut stream.buffers[stream.next_buffer]; if buffer.request.is_none() { - let request_id = buffer.buffer_id as u64 | ((stream.stream_id as u64) << 32); - println!("Queuing request with id {}", request_id); + let request_id = self.next_request_id; let mut req = unsafe { self.inner.get_mut().create_request(request_id) }?; unsafe { req @@ -638,11 +279,21 @@ impl Camera<'_> { for (control_id, control_value) in self.controls.get_libcamera() { unsafe { req.get_mut().set_control(control_id, control_value.get()) }; } + let timestamp = Instant::now(); unsafe { self.inner.get_mut().queue_request(req.get_mut()) }?; + self.request_infos.insert( + request_id, + RequestInfo { + stream_id, + buffer_id: stream.next_buffer, + timestamp, + }, + ); + self.next_request_id += 1; buffer.request = Some(req); stream.next_buffer += 1; stream.next_buffer %= stream.buffers.len(); - Ok(()) + Ok(request_id) } else { Err(LibcameraError::NoBufferReady) } @@ -655,21 +306,30 @@ impl Camera<'_> { .flat_map(|event| match event.message_type { ffi::CameraMessageType::RequestComplete => { println!("Ev: {event:?}"); - let stream_id = ((event.request_cookie >> 32) & 0xFFFFFFFF) as usize; - let buffer_id = (event.request_cookie & 0xFFFFFFFF) as usize; + let request_id = event.request_cookie; + let request_info = self.request_infos.remove(&request_id)?; println!( "Request completed on stream {}, buffer {}.", - stream_id, buffer_id + request_info.stream_id, request_info.buffer_id ); - let buffer = &mut self.streams[stream_id].buffers[buffer_id]; + let stream = &mut self.streams[request_info.stream_id]; + let buffer = &mut stream.buffers[request_info.buffer_id]; buffer.request = None; - Some(CameraEvent::RequestComplete( - buffer - .planes - .iter() - .map(|plane| unsafe { plane.get().read_to_vec() }) - .collect(), - )) + println!("Pixel format: {:?}", stream.pixel_format); + Some(CameraEvent::RequestComplete { + serial_id: request_id, + timestamp: request_info.timestamp, + image: RawCameraImage { + width: stream.width as usize, + height: stream.height as usize, + pixel_format: stream.pixel_format, + planes: buffer + .planes + .iter() + .map(|plane| unsafe { plane.get().read_to_vec() }) + .collect(), + }, + }) } _ => None, }) @@ -686,11 +346,57 @@ impl Drop for Camera<'_> { } } +/// Represents raw image data fetched from the camera +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RawCameraImage { + pub pixel_format: Option, + pub width: usize, + pub height: usize, + pub planes: Vec>, +} + +impl RawCameraImage { + /// Attempts to decode this camera image. + /// + /// Currently only supports Bgr, Rgb, Yuyv, and Yuv420 formats, and Mjpeg with the `image` feature. + pub fn try_decode(self) -> Option { + match (self.pixel_format, self.planes.as_slice()) { + (Some(PixelFormat::Bgr888), [data]) => { + image::BgrImage::from_planes(self.width, self.height, [data.to_owned()]) + .map(MultiImage::Bgr) + } + (Some(PixelFormat::Rgb888), [data]) => { + image::RgbImage::from_planes(self.width, self.height, [data.to_owned()]) + .map(MultiImage::Rgb) + } + (Some(PixelFormat::Yuyv), [data]) => { + image::YuyvImage::from_planes(self.width, self.height, [data.to_owned()]) + .map(MultiImage::Yuyv) + } + (Some(PixelFormat::Yuv420), [y, u, v]) => image::Yuv420Image::from_planes( + self.width, + self.height, + [y.to_owned(), u.to_owned(), v.to_owned()], + ) + .map(MultiImage::Yuv420), + #[cfg(feature = "image")] + (Some(PixelFormat::Mjpeg), [data]) => { + image::RgbImage::decode_jpeg(data).ok().map(MultiImage::Rgb) + } + _ => None, + } + } +} + /// Represents an event from the camera #[derive(Debug, Clone, PartialEq, Eq)] pub enum CameraEvent { /// Triggered when a capture request has completed, containing a vec of the resulting image planes. - RequestComplete(Vec>), + RequestComplete { + serial_id: u64, + timestamp: Instant, + image: RawCameraImage, + }, } /// Represents the result of applying a configuration to a camera. diff --git a/src/config.rs b/src/config.rs index 2c14808..be22e92 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ use crate::bridge::{ffi, GetInner}; use crate::Result; -pub use ffi::DefaultPixelFormat; +pub use ffi::DefaultPixelFormat as PixelFormat; /// Represents the configuration for a camera. pub struct CameraConfig { @@ -54,8 +54,8 @@ impl StreamConfig { pub(crate) fn get_inner(&self) -> &ffi::BindStreamConfiguration { &self.inner } - /// Set the pixel format for this stream to a [DefaultPixelFormat] - pub fn set_default_pixel_format(&mut self, fmt: DefaultPixelFormat) { + /// Set the pixel format for this stream to a [PixelFormat] + pub fn set_pixel_format(&mut self, fmt: PixelFormat) { unsafe { self .inner @@ -63,15 +63,35 @@ impl StreamConfig { .set_pixel_format(ffi::get_default_pixel_format(fmt)) }; } + /// Retrieve the current pixel format + /// + /// # Returns + /// Returns None if the pixel format is not a known pixel format. + pub fn get_pixel_format(&self) -> Option { + let pixel_format = unsafe { self.inner.get().get_pixel_format() }; + unsafe { pixel_format.get().as_default_pixel_format() }.ok() + } + /// Set the target image size for this stream. pub fn set_size(&mut self, width: u32, height: u32) { unsafe { self.inner.get_mut().set_size(ffi::new_size(width, height)) }; } + /// Get the target image size for this stream. + pub fn get_size(&self) -> (u32, u32) { + let size = unsafe { self.inner.get().get_size() }; + (unsafe { size.get().get_width() }, unsafe { + size.get().get_height() + }) + } + pub fn description(&self) -> String { + unsafe { self.inner.get().raw_to_string() } + } } impl fmt::Debug for StreamConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StreamConfig") - .field("description", &unsafe { self.inner.get().raw_to_string() }) + .field("size", &self.get_size()) + .field("pixel_format", &self.get_pixel_format()) .finish_non_exhaustive() } } diff --git a/src/controls.rs b/src/controls.rs new file mode 100644 index 0000000..d7b52c4 --- /dev/null +++ b/src/controls.rs @@ -0,0 +1,396 @@ +use std::collections::HashMap; +use std::fmt::Debug; +use std::ops::RangeInclusive; + +use crate::bridge::{ffi, GetInner}; +use crate::{LibcameraError, Result}; + +/// Contains a value with an acceptable minimum and maximum and a default. +#[derive(Debug)] +pub struct MinMaxValue { + range: RangeInclusive, + default: T, + value: T, +} + +impl MinMaxValue { + /// Creates a new MinMaxValue out of a given min, max, and default + /// + /// # Returns + /// Returns None if default is not within min and max. + pub fn new(min: T, max: T, default: T) -> Result> { + let range = min..=max; + if range.contains(&default) { + Ok(MinMaxValue { + range: min..=max, + default, + value: default, + }) + } else { + Err(LibcameraError::InvalidControlValue) + } + } + /// Retrieve the default value + pub fn get_default(&self) -> T { + self.default + } + /// Retrieve the minimum value + pub fn min(&self) -> T { + *self.range.start() + } + /// Retrieve the maximum value + pub fn max(&self) -> T { + *self.range.end() + } + /// Gets the stored value + /// + /// It is gurenteed to lie within MinMaxValue::min() and MinMaxValue::max(). + pub fn get_value(&self) -> T { + self.value + } + /// Gets the stored value if it is not equal to the default stored value. + pub fn get_value_if_changed(&self) -> Option { + if self.value != self.default { + Some(self.value) + } else { + None + } + } + /// Verifies that value lies within the acceptable range for this value + /// + /// # Returns + /// `true` if the value lies within the acceptable range for this value and was stored, `false` otherwise. + pub fn set_value(&mut self, value: T) -> bool { + if self.range.contains(&value) { + self.value = value; + true + } else { + false + } + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_bool() }?, + unsafe { pair.max.get().get_bool() }?, + unsafe { pair.value.get().get_bool() }?, + ) + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_u8() }?, + unsafe { pair.max.get().get_u8() }?, + unsafe { pair.value.get().get_u8() }?, + ) + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_i32() }?, + unsafe { pair.max.get().get_i32() }?, + unsafe { pair.value.get().get_i32() }?, + ) + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_i64() }?, + unsafe { pair.max.get().get_i64() }?, + unsafe { pair.value.get().get_i64() }?, + ) + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_f32() }?, + unsafe { pair.max.get().get_f32() }?, + unsafe { pair.value.get().get_f32() }?, + ) + } +} + +/// Represents a camera control value with an unknown type +/// +/// Most of the time you probably want to use `CameraControls` instead. +#[non_exhaustive] +#[derive(Debug)] +pub enum CameraControlValue { + None, + Bool(MinMaxValue), + Byte(MinMaxValue), + Integer32(MinMaxValue), + Integer64(MinMaxValue), + Float(MinMaxValue), + // String(MinMaxValue), + // Rectangle(MinMaxValue), + // Size(MinMaxValue), +} + +/// Stores camera controls. +/// +/// Common controls are fields on this struct +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct CameraControls { + pub ae_enable: Option>, + pub ae_metering_mode: Option>, + pub ae_constraint_mode: Option>, + pub ae_exposure_mode: Option>, + pub exposure_value: Option>, + pub exposure_time: Option>, + pub analogue_gain: Option>, + pub brightness: Option>, + pub contrast: Option>, + pub awb_enable: Option>, + pub awb_mode: Option>, + pub colour_gains: Option>, + pub saturation: Option>, + pub sharpness: Option>, + pub colour_correction_matrix: Option>, + // pub scaler_crop: Option>, // Rectangle TODO + pub frame_duration_limits: Option>, + pub noise_reduction_mode: Option>, + pub others: HashMap, +} + +impl CameraControls { + pub(crate) fn from_libcamera(control_list: Vec) -> Self { + let mut controls = CameraControls::default(); + for control in control_list { + let name = unsafe { control.id.get().get_name() }; + let did_name_match = match name.as_ref() { + "AeEnable" => (&control) + .try_into() + .map(|control| controls.ae_enable = Some(control)) + .is_ok(), + "AeMeteringMode" => (&control) + .try_into() + .map(|control| controls.ae_metering_mode = Some(control)) + .is_ok(), + "AeConstraintMode" => (&control) + .try_into() + .map(|control| controls.ae_constraint_mode = Some(control)) + .is_ok(), + "AeExposureMode" => (&control) + .try_into() + .map(|control| controls.ae_exposure_mode = Some(control)) + .is_ok(), + "ExposureValue" => (&control) + .try_into() + .map(|control| controls.exposure_value = Some(control)) + .is_ok(), + "ExposureTime" => (&control) + .try_into() + .map(|control| controls.exposure_time = Some(control)) + .is_ok(), + "AnalogueGain" => (&control) + .try_into() + .map(|control| controls.analogue_gain = Some(control)) + .is_ok(), + "Brightness" => (&control) + .try_into() + .map(|control| controls.brightness = Some(control)) + .is_ok(), + "Contrast" => (&control) + .try_into() + .map(|control| controls.contrast = Some(control)) + .is_ok(), + "AwbEnable" => (&control) + .try_into() + .map(|control| controls.awb_enable = Some(control)) + .is_ok(), + "AwbMode" => (&control) + .try_into() + .map(|control| controls.awb_mode = Some(control)) + .is_ok(), + "ColourGains" => (&control) + .try_into() + .map(|control| controls.colour_gains = Some(control)) + .is_ok(), + "Saturation" => (&control) + .try_into() + .map(|control| controls.saturation = Some(control)) + .is_ok(), + "Sharpness" => (&control) + .try_into() + .map(|control| controls.sharpness = Some(control)) + .is_ok(), + "ColourCorrectionMatrix" => (&control) + .try_into() + .map(|control| controls.colour_correction_matrix = Some(control)) + .is_ok(), + // "ScalerCrop" => (&control).try_into().map(|control| controls.scaler_crop = Some(control)).is_ok(), + "FrameDurationLimits" => (&control) + .try_into() + .map(|control| controls.frame_duration_limits = Some(control)) + .is_ok(), + "NoiseReductionMode" => (&control) + .try_into() + .map(|control| controls.noise_reduction_mode = Some(control)) + .is_ok(), + _ => false, + }; + if !did_name_match { + if let Some(control_value) = match unsafe { control.id.get().get_type() } { + ffi::CameraControlType::None => Some(CameraControlValue::None), + ffi::CameraControlType::Bool => (&control).try_into().ok().map(CameraControlValue::Bool), + ffi::CameraControlType::Byte => (&control).try_into().ok().map(CameraControlValue::Byte), + ffi::CameraControlType::Integer32 => (&control) + .try_into() + .ok() + .map(CameraControlValue::Integer32), + ffi::CameraControlType::Integer64 => (&control) + .try_into() + .ok() + .map(CameraControlValue::Integer64), + ffi::CameraControlType::Float => { + (&control).try_into().ok().map(CameraControlValue::Float) + } + _ => None, + // ffi::CameraControlType::String => (&control).try_into().ok().map(|control| CameraControlValue::String(control)), + // ffi::CameraControlType::Rectangle => (&control).try_into().ok().map(|control| CameraControlValue::Rectangle(control)), + // ffi::CameraControlType::Size => (&control).try_into().ok().map(|control| CameraControlValue::Size(control)), + } { + controls + .others + .insert(unsafe { control.id.get().get_id() }, (name, control_value)); + } else { + eprintln!("Camera control with conflicting types: {name}"); + } + } + } + controls + } + pub(crate) fn get_libcamera(&self) -> Vec<(u32, ffi::BindControlValue)> { + let mut controls = Vec::new(); + if let Some(ae_enable) = &self.ae_enable { + ae_enable.get_value_if_changed().map(|value| { + controls.push((1, unsafe { ffi::new_control_value_bool(value) })); + }); + } + if let Some(ae_metering_mode) = &self.ae_metering_mode { + ae_metering_mode.get_value_if_changed().map(|value| { + controls.push((3, unsafe { ffi::new_control_value_i32(value) })); + }); + } + if let Some(ae_constraint_mode) = &self.ae_constraint_mode { + ae_constraint_mode.get_value_if_changed().map(|value| { + controls.push((4, unsafe { ffi::new_control_value_i32(value) })); + }); + } + if let Some(ae_exposure_mode) = &self.ae_exposure_mode { + ae_exposure_mode.get_value_if_changed().map(|value| { + controls.push((5, unsafe { ffi::new_control_value_i32(value) })); + }); + } + if let Some(exposure_value) = &self.exposure_value { + exposure_value.get_value_if_changed().map(|value| { + controls.push((6, unsafe { ffi::new_control_value_f32(value) })); + }); + } + if let Some(exposure_time) = &self.exposure_time { + exposure_time.get_value_if_changed().map(|value| { + controls.push((7, unsafe { ffi::new_control_value_i32(value) })); + }); + } + if let Some(analogue_gain) = &self.analogue_gain { + analogue_gain.get_value_if_changed().map(|value| { + controls.push((8, unsafe { ffi::new_control_value_f32(value) })); + }); + } + if let Some(brightness) = &self.brightness { + brightness.get_value_if_changed().map(|value| { + controls.push((9, unsafe { ffi::new_control_value_f32(value) })); + }); + } + if let Some(contrast) = &self.contrast { + contrast.get_value_if_changed().map(|value| { + controls.push((10, unsafe { ffi::new_control_value_f32(value) })); + }); + } + if let Some(awb_enable) = &self.awb_enable { + awb_enable.get_value_if_changed().map(|value| { + controls.push((12, unsafe { ffi::new_control_value_bool(value) })); + }); + } + if let Some(awb_mode) = &self.awb_mode { + awb_mode.get_value_if_changed().map(|value| { + controls.push((13, unsafe { ffi::new_control_value_i32(value) })); + }); + } + if let Some(colour_gains) = &self.colour_gains { + colour_gains.get_value_if_changed().map(|value| { + controls.push((15, unsafe { ffi::new_control_value_f32(value) })); + }); + } + if let Some(saturation) = &self.saturation { + saturation.get_value_if_changed().map(|value| { + controls.push((17, unsafe { ffi::new_control_value_f32(value) })); + }); + } + if let Some(sharpness) = &self.sharpness { + sharpness.get_value_if_changed().map(|value| { + controls.push((19, unsafe { ffi::new_control_value_f32(value) })); + }); + } + if let Some(colour_correction_matrix) = &self.colour_correction_matrix { + colour_correction_matrix + .get_value_if_changed() + .map(|value| { + controls.push((21, unsafe { ffi::new_control_value_f32(value) })); + }); + } + if let Some(frame_duration_limits) = &self.frame_duration_limits { + frame_duration_limits.get_value_if_changed().map(|value| { + controls.push((25, unsafe { ffi::new_control_value_i64(value) })); + }); + } + if let Some(noise_reduction_mode) = &self.noise_reduction_mode { + noise_reduction_mode.get_value_if_changed().map(|value| { + controls.push((39, unsafe { ffi::new_control_value_i32(value) })); + }); + } + for (id, (_name, value)) in &self.others { + if let Some(value) = match value { + CameraControlValue::None => None, + CameraControlValue::Bool(value) => { + Some(unsafe { ffi::new_control_value_bool(value.get_value()) }) + } + CameraControlValue::Byte(value) => { + Some(unsafe { ffi::new_control_value_u8(value.get_value()) }) + } + CameraControlValue::Integer32(value) => { + Some(unsafe { ffi::new_control_value_i32(value.get_value()) }) + } + CameraControlValue::Integer64(value) => { + Some(unsafe { ffi::new_control_value_i64(value.get_value()) }) + } + CameraControlValue::Float(value) => { + Some(unsafe { ffi::new_control_value_f32(value.get_value()) }) + } + // CameraControlValue::String(value) => Some(unsafe { ffi::new_control_value_string(value.get_value()) }), + // CameraControlValue::Rectangle(value) => Some(unsafe { ffi::new_control_value_rectangle(value.get_value()) }), + // CameraControlValue::Size(value) => Some(unsafe { ffi::new_control_value_size(value.get_value()) }), + } { + controls.push((*id, value)); + } + } + controls + } +} diff --git a/src/image.rs b/src/image.rs new file mode 100644 index 0000000..dbcfcf3 --- /dev/null +++ b/src/image.rs @@ -0,0 +1,311 @@ +use crate::{LibcameraError, Result}; + +/// Represents an image. +pub trait CameraImage { + /// Create an image out of a size and the image data planes. + fn from_planes(width: usize, height: usize, planes: [Vec; PLANES]) -> Option + where + Self: Sized; + /// Convert this image into BGR format. + fn as_bgr(&self) -> Option; + /// Get the size of this image. + fn get_size(&self) -> (usize, usize); + /// Get the raw pixel planes for this image. + fn get_planes(&self) -> [&[u8]; PLANES]; +} + +/// Contains an image in any format. +#[derive(Debug, Clone)] +#[non_exhaustive] +pub enum MultiImage { + Bgr(BgrImage), + Rgb(RgbImage), + Yuyv(YuyvImage), + Yuv420(Yuv420Image), +} + +impl MultiImage { + /// Convert this image into a [`BgrImage`]. + pub fn as_bgr(&self) -> Option { + match self { + MultiImage::Bgr(img) => img.as_bgr(), + MultiImage::Rgb(img) => img.as_bgr(), + MultiImage::Yuyv(img) => img.as_bgr(), + MultiImage::Yuv420(img) => img.as_bgr(), + } + } + /// Get the size of this image. + pub fn get_size(&self) -> (usize, usize) { + match self { + MultiImage::Bgr(img) => img.get_size(), + MultiImage::Rgb(img) => img.get_size(), + MultiImage::Yuyv(img) => img.get_size(), + MultiImage::Yuv420(img) => img.get_size(), + } + } +} + +/// Contains an image in BGR Format. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BgrImage { + width: usize, + height: usize, + data: Vec, +} + +impl CameraImage<1> for BgrImage { + fn from_planes(width: usize, height: usize, planes: [Vec; 1]) -> Option { + let [data] = planes; + if width * height * 3 == data.len() { + Some(BgrImage { + width, + height, + data, + }) + } else { + None + } + } + fn as_bgr(&self) -> Option { + Some(self.clone()) + } + fn get_size(&self) -> (usize, usize) { + (self.width, self.height) + } + fn get_planes(&self) -> [&[u8]; 1] { + [&self.data] + } +} + +impl BgrImage { + pub fn as_rgb(&self) -> Option { + RgbImage::from_planes( + self.width, + self.height, + [self + .data + .chunks(3) + .flat_map(|chunk| { + if let &[b, g, r] = chunk { + [r, g, b] + } else { + panic!("Exact chunks not exact!"); + } + }) + .collect()], + ) + } +} + +/// Contains an image in RGB Format. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RgbImage { + width: usize, + height: usize, + data: Vec, +} + +impl CameraImage<1> for RgbImage { + fn from_planes(width: usize, height: usize, planes: [Vec; 1]) -> Option { + let [data] = planes; + if width * height * 3 == data.len() { + Some(RgbImage { + width, + height, + data, + }) + } else { + None + } + } + fn as_bgr(&self) -> Option { + BgrImage::from_planes( + self.width, + self.height, + [self + .data + .chunks(3) + .flat_map(|chunk| { + if let &[r, g, b] = chunk { + [b, g, r] + } else { + panic!("Exact chunks not exact!"); + } + }) + .collect()], + ) + } + fn get_size(&self) -> (usize, usize) { + (self.width, self.height) + } + fn get_planes(&self) -> [&[u8]; 1] { + [&self.data] + } +} + +/// These functions are only available with the `image` feature/crate. +#[cfg(feature = "image")] +impl RgbImage { + /// Decode an [`RgbImage`] from a JPEG image stream in a Vec. + /// + /// Available only with the `image` feature/crate. + pub fn decode_jpeg(data: &[u8]) -> Result { + let image = image::load_from_memory_with_format(data, image::ImageFormat::Jpeg)?; + println!("Image loaded"); + if let image::DynamicImage::ImageRgb8(img) = image { + let (width, height) = img.dimensions(); + println!("JPEG image size {width}x{height}."); + Ok( + RgbImage::from_planes(width as usize, height as usize, [img.into_raw()]) + .ok_or(LibcameraError::BadImageFormat)?, + ) + } else { + Err(LibcameraError::BadImageFormat) + } + } + /// Encode an [`RgbImage`] to a PNG image stream in a Vec. + /// + /// Available only with the `image` feature/crate. + pub fn encode_png(&self) -> Result> { + let mut buffer = std::io::Cursor::new(Vec::new()); + image::write_buffer_with_format( + &mut buffer, + &self.data, + self.width as u32, + self.height as u32, + image::ColorType::Rgb8, + image::ImageOutputFormat::Png, + )?; + Ok(buffer.into_inner()) + } +} + +fn yuv2rgb(y: u8, u: u8, v: u8) -> (u8, u8, u8) { + ( + (y as f32 + (1.370705 * (v as f32 - 128.0))).clamp(0.0, 255.0) as u8, + (y as f32 - (0.698001 * (v as f32 - 128.0)) - (0.337633 * (u as f32 - 128.0))).clamp(0.0, 255.0) + as u8, + (y as f32 + (1.732446 * (u as f32 - 128.0))).clamp(0.0, 255.0) as u8, + ) +} + +/// Contains an image in YUVU 4:2:2 Format. +#[derive(Debug, Clone)] +pub struct YuyvImage { + width: usize, + height: usize, + data: Vec, +} + +impl CameraImage<1> for YuyvImage { + fn from_planes(width: usize, height: usize, planes: [Vec; 1]) -> Option { + let [data] = planes; + if width * height * 2 == data.len() { + Some(YuyvImage { + width, + height, + data, + }) + } else { + None + } + } + fn as_bgr(&self) -> Option { + BgrImage::from_planes( + self.width, + self.height, + [self + .data + .chunks_exact(4) + .flat_map(|chunk| { + if let &[y1, u, y2, v] = chunk { + // Map each section of y*u*y*v* to two BGR pixels + let (r1, g1, b1) = yuv2rgb(y1, u, v); + let (r2, g2, b2) = yuv2rgb(y2, u, v); + [b1, g1, r1, b2, g2, r2] + } else { + panic!("Exact chunks not exact!"); + } + }) + .collect()], + ) + } + fn get_size(&self) -> (usize, usize) { + (self.width, self.height) + } + fn get_planes(&self) -> [&[u8]; 1] { + [&self.data] + } +} + +/// Contains an image in YUV 4:2:0 Format. +#[derive(Debug, Clone)] +pub struct Yuv420Image { + width: usize, + height: usize, + y_plane: Vec, + u_plane: Vec, + v_plane: Vec, +} + +impl CameraImage<3> for Yuv420Image { + fn from_planes(width: usize, height: usize, planes: [Vec; 3]) -> Option { + let [y_plane, u_plane, v_plane] = planes; + if width * height == y_plane.len() + && width / 2 * height / 2 == u_plane.len() + && width / 2 * height / 2 == v_plane.len() + { + Some(Yuv420Image { + width, + height, + y_plane, + u_plane, + v_plane, + }) + } else { + None + } + } + fn as_bgr(&self) -> Option { + BgrImage::from_planes( + self.width, + self.height, + [self + .y_plane + .chunks_exact(self.width) // Get each line of y* + .zip( + // Zip it with each line of u* and v* (half width+height) + self + .u_plane // For the u plane... + .chunks_exact(self.width / 2) + .map(|line| line.iter().flat_map(|&u| [u, u])) // Double the width + .zip( + // Zip the u and v planes + self + .v_plane // For the v plane... + .chunks_exact(self.width / 2) + .map(|line| line.iter().flat_map(|&v| [v, v])), // Double the width + ) + .flat_map(|line| [line.clone(), line]), // Double the height + ) + .flat_map(|(y_line, (u_line, v_line))| { + // Re-zip y*, u* and v* per-pixel instead of per-line. + y_line + .iter() + .zip(u_line.into_iter().zip(v_line.into_iter())) + }) + .flat_map(|(&y, (u, v))| { + // Convert to RGB + let (r, g, b) = yuv2rgb(y, u, v); + [r, g, b] + }) + .collect()], + ) + } + fn get_size(&self) -> (usize, usize) { + (self.width, self.height) + } + fn get_planes(&self) -> [&[u8]; 3] { + [&self.y_plane, &self.u_plane, &self.v_plane] + } +} diff --git a/src/lib.rs b/src/lib.rs index 2560335..c50ff7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,10 @@ -#![deny(clippy::all)] - use thiserror::Error; mod bridge; pub mod camera; pub mod config; +pub mod controls; +pub mod image; pub mod prelude; #[cfg(test)] mod test; @@ -21,6 +21,14 @@ pub enum LibcameraError { NoBufferReady, #[error("Control value out of range!")] InvalidControlValue, + #[error("Unknown ID in camera request")] + UnknownRequestId, + #[cfg(feature = "image")] + #[error("Image error: {0}")] + ImageError(#[from] ::image::ImageError), + #[cfg(feature = "image")] + #[error("Image error: Bad image format.")] + BadImageFormat, } type Result = std::result::Result; diff --git a/src/prelude.rs b/src/prelude.rs index 0c749ab..bb1e30f 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,2 +1,2 @@ pub use crate::camera::{CameraEvent, CameraManager, StreamRole}; -pub use crate::config::DefaultPixelFormat; +pub use crate::config::PixelFormat; diff --git a/src/test.rs b/src/test.rs index a924c25..c7dce47 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,34 +1,46 @@ -use crate::prelude::{CameraEvent, CameraManager, DefaultPixelFormat, StreamRole}; +use crate::prelude::{CameraEvent, CameraManager, PixelFormat, StreamRole}; +#[cfg(feature = "image")] #[test] fn test_camera() { let mut cm = CameraManager::new().unwrap(); println!("cm: {cm:?}"); let mut cam = cm.get_camera_by_name(&cm.get_camera_names()[0]).unwrap(); println!("cam: {cam:?}"); - let conf = cam.generate_config(&[StreamRole::StillCapture]).unwrap(); + let conf = cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); println!("conf: {conf:?}"); let stream = &mut conf.streams_mut()[0]; - stream.set_default_pixel_format(DefaultPixelFormat::Mjpeg); + stream.set_pixel_format(PixelFormat::Mjpeg); stream.set_size(640, 480); println!("Configuration applied: {:?}", cam.apply_config().unwrap()); cam.start_stream().unwrap(); println!("Started stream."); println!("Capturing frames..."); - for i in 0..10 { + for i in 0..50 { + let brightness = cam.get_controls_mut().brightness.as_mut().unwrap(); + brightness.set_value((brightness.get_value() + 0.6).rem_euclid(1.5) - 0.5); + let contrast = cam.get_controls_mut().contrast.as_mut().unwrap(); + contrast.set_value(contrast.get_value() + 0.02); cam.capture_next_picture(0).unwrap(); println!("Capturing image #{}", i); std::thread::sleep(std::time::Duration::from_millis(100)); let events = cam.poll_events().unwrap(); for event in events { match event { - CameraEvent::RequestComplete(planes) => { - println!("Got request back with {} image planes", planes.len()); - for (j, plane) in planes.iter().enumerate() { - let filename = format!("plane_{}_{}.jpeg", i, j); - std::fs::write(&filename, plane).unwrap(); - println!("Saved plane to '{filename}'."); - } + CameraEvent::RequestComplete { + serial_id, image, .. + } => { + let decoded_image = image.try_decode().unwrap(); + let rgb_image = decoded_image + .as_bgr() + .unwrap() + .as_rgb() + .unwrap() + .encode_png() + .unwrap(); + let filename = format!("image_{serial_id}.png"); + std::fs::write(&filename, rgb_image).unwrap(); + println!("Got responce back for request {serial_id} and saved to {filename}."); } } } From 401603aa3b9a1afe0656ac6105df915ef60c5df1 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 6 Jul 2022 16:02:21 -0600 Subject: [PATCH 21/49] feat: makefile improvements. --- makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/makefile b/makefile index 94486f1..59ac52d 100644 --- a/makefile +++ b/makefile @@ -12,4 +12,5 @@ fmt: format format: clang-format -style=file -i libcamera-bridge/* cargo fmt + cargo clippy --fix clang-tidy --format-style=file --fix --fix-errors --fix-notes libcamera-bridge/*.cpp -- -I/usr/local/include/libcamera -I./target/cxxbridge -I.. --std=c++17 From 3b8d494b31b237f7220f84212601292e32f9b221 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 6 Jul 2022 16:04:20 -0600 Subject: [PATCH 22/49] chore: clippy fix --- makefile | 2 +- src/controls.rs | 72 ++++++++++++++++++++++++------------------------- 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/makefile b/makefile index 59ac52d..acc5060 100644 --- a/makefile +++ b/makefile @@ -12,5 +12,5 @@ fmt: format format: clang-format -style=file -i libcamera-bridge/* cargo fmt - cargo clippy --fix clang-tidy --format-style=file --fix --fix-errors --fix-notes libcamera-bridge/*.cpp -- -I/usr/local/include/libcamera -I./target/cxxbridge -I.. --std=c++17 + cargo clippy --fix diff --git a/src/controls.rs b/src/controls.rs index d7b52c4..dbf5b3d 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -280,91 +280,89 @@ impl CameraControls { pub(crate) fn get_libcamera(&self) -> Vec<(u32, ffi::BindControlValue)> { let mut controls = Vec::new(); if let Some(ae_enable) = &self.ae_enable { - ae_enable.get_value_if_changed().map(|value| { + if let Some(value) = ae_enable.get_value_if_changed() { controls.push((1, unsafe { ffi::new_control_value_bool(value) })); - }); + } } if let Some(ae_metering_mode) = &self.ae_metering_mode { - ae_metering_mode.get_value_if_changed().map(|value| { + if let Some(value) = ae_metering_mode.get_value_if_changed() { controls.push((3, unsafe { ffi::new_control_value_i32(value) })); - }); + } } if let Some(ae_constraint_mode) = &self.ae_constraint_mode { - ae_constraint_mode.get_value_if_changed().map(|value| { + if let Some(value) = ae_constraint_mode.get_value_if_changed() { controls.push((4, unsafe { ffi::new_control_value_i32(value) })); - }); + } } if let Some(ae_exposure_mode) = &self.ae_exposure_mode { - ae_exposure_mode.get_value_if_changed().map(|value| { + if let Some(value) = ae_exposure_mode.get_value_if_changed() { controls.push((5, unsafe { ffi::new_control_value_i32(value) })); - }); + } } if let Some(exposure_value) = &self.exposure_value { - exposure_value.get_value_if_changed().map(|value| { + if let Some(value) = exposure_value.get_value_if_changed() { controls.push((6, unsafe { ffi::new_control_value_f32(value) })); - }); + } } if let Some(exposure_time) = &self.exposure_time { - exposure_time.get_value_if_changed().map(|value| { + if let Some(value) = exposure_time.get_value_if_changed() { controls.push((7, unsafe { ffi::new_control_value_i32(value) })); - }); + } } if let Some(analogue_gain) = &self.analogue_gain { - analogue_gain.get_value_if_changed().map(|value| { + if let Some(value) = analogue_gain.get_value_if_changed() { controls.push((8, unsafe { ffi::new_control_value_f32(value) })); - }); + } } if let Some(brightness) = &self.brightness { - brightness.get_value_if_changed().map(|value| { + if let Some(value) = brightness.get_value_if_changed() { controls.push((9, unsafe { ffi::new_control_value_f32(value) })); - }); + } } if let Some(contrast) = &self.contrast { - contrast.get_value_if_changed().map(|value| { + if let Some(value) = contrast.get_value_if_changed() { controls.push((10, unsafe { ffi::new_control_value_f32(value) })); - }); + } } if let Some(awb_enable) = &self.awb_enable { - awb_enable.get_value_if_changed().map(|value| { + if let Some(value) = awb_enable.get_value_if_changed() { controls.push((12, unsafe { ffi::new_control_value_bool(value) })); - }); + } } if let Some(awb_mode) = &self.awb_mode { - awb_mode.get_value_if_changed().map(|value| { + if let Some(value) = awb_mode.get_value_if_changed() { controls.push((13, unsafe { ffi::new_control_value_i32(value) })); - }); + } } if let Some(colour_gains) = &self.colour_gains { - colour_gains.get_value_if_changed().map(|value| { + if let Some(value) = colour_gains.get_value_if_changed() { controls.push((15, unsafe { ffi::new_control_value_f32(value) })); - }); + } } if let Some(saturation) = &self.saturation { - saturation.get_value_if_changed().map(|value| { + if let Some(value) = saturation.get_value_if_changed() { controls.push((17, unsafe { ffi::new_control_value_f32(value) })); - }); + } } if let Some(sharpness) = &self.sharpness { - sharpness.get_value_if_changed().map(|value| { + if let Some(value) = sharpness.get_value_if_changed() { controls.push((19, unsafe { ffi::new_control_value_f32(value) })); - }); + } } if let Some(colour_correction_matrix) = &self.colour_correction_matrix { - colour_correction_matrix - .get_value_if_changed() - .map(|value| { - controls.push((21, unsafe { ffi::new_control_value_f32(value) })); - }); + if let Some(value) = colour_correction_matrix.get_value_if_changed() { + controls.push((21, unsafe { ffi::new_control_value_f32(value) })); + } } if let Some(frame_duration_limits) = &self.frame_duration_limits { - frame_duration_limits.get_value_if_changed().map(|value| { + if let Some(value) = frame_duration_limits.get_value_if_changed() { controls.push((25, unsafe { ffi::new_control_value_i64(value) })); - }); + } } if let Some(noise_reduction_mode) = &self.noise_reduction_mode { - noise_reduction_mode.get_value_if_changed().map(|value| { + if let Some(value) = noise_reduction_mode.get_value_if_changed() { controls.push((39, unsafe { ffi::new_control_value_i32(value) })); - }); + } } for (id, (_name, value)) in &self.others { if let Some(value) = match value { From c1354d736f16ef9cf4e17e61ea4b25a49a41bc7f Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 6 Jul 2022 16:36:04 -0600 Subject: [PATCH 23/49] chore: doc comments and cleanup --- Cargo.toml | 2 ++ readme.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ src/bridge.rs | 1 + src/camera.rs | 17 ++++++++++++--- src/config.rs | 3 +++ src/controls.rs | 29 ++++++++++++++++++++++++++ src/error.rs | 32 ++++++++++++++++++++++++++++ src/image.rs | 9 +++++++- src/lib.rs | 34 +++++++++--------------------- src/test.rs | 5 ++--- 10 files changed, 156 insertions(+), 31 deletions(-) create mode 100644 readme.md create mode 100644 src/error.rs diff --git a/Cargo.toml b/Cargo.toml index 02b5b04..2ddd8c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,7 @@ [package] name = "libcamera-rs" +description = "Libcamera bindings for rust." +authors = ["John Brandt ", "Ben Heard "] version = "0.1.0" edition = "2021" diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..a375385 --- /dev/null +++ b/readme.md @@ -0,0 +1,55 @@ +# Libcamera-rs +[Libcamera](https://www.libcamera.org/) bindings for [Rust](https://www.rust-lang.org/). + +## Example usage to take a bunch of pictures and change some camera parameters: +```rust +use libcamera_rs::prelude::*; + +fn main() { + let mut cm = CameraManager::new().unwrap(); + // Get the first camera from the camera manager. + let mut cam = cm.get_camera_by_name(&cm.get_camera_names()[0]).unwrap(); + // Generate a configuration with one "viewfinder" optimized stream. + let conf = cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); + let stream = &mut conf.streams_mut()[0]; + // Attempt to assign the stream's pixel format and size. + stream.set_pixel_format(PixelFormat::Mjpeg); + stream.set_size(640, 480); + // Start the camera stream. + cam.start_stream().unwrap(); + // Take 10 pictures + for i in 0..10 { + // Change brightness and contrast + let brightness = cam.get_controls_mut().brightness.as_mut().unwrap(); + brightness.set_value((brightness.get_value() + 0.9).rem_euclid(1.5) - 0.5); + let contrast = cam.get_controls_mut().contrast.as_mut().unwrap(); + contrast.set_value(contrast.get_value() + 0.1); + // Queue the capture request + cam.capture_next_picture(0).unwrap(); + // Wait for a bit. + std::thread::sleep(std::time::Duration::from_millis(200)); + // Poll events (containing the images) + let events = cam.poll_events().unwrap(); + for event in events { + match event { + CameraEvent::RequestComplete { + serial_id, image, .. + } => { + // Reencode the image to PNG and save it. + let decoded_image = image.try_decode().unwrap(); + let rgb_image = decoded_image + .as_bgr() + .unwrap() + .as_rgb() + .encode_png() + .unwrap(); + let filename = format!("image_{serial_id}.png"); + std::fs::write(&filename, rgb_image).unwrap(); + } + } + } + } +} +``` + +Most functions should be well enough documented with rustdoc. diff --git a/src/bridge.rs b/src/bridge.rs index c00f2c3..7513b89 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -12,6 +12,7 @@ pub mod ffi { #[repr(i32)] #[derive(Debug)] enum StreamRole { + /// ??? Raw, /// Capturing still images StillCapture, diff --git a/src/camera.rs b/src/camera.rs index a08e804..47c4785 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -298,6 +298,9 @@ impl Camera<'_> { Err(LibcameraError::NoBufferReady) } } + /// Poll events from the camera. + /// + /// The results should be in order of when the camera sent them, but not neccesarily in order of when they were initially queued. Make sure to use `serial_id`, or the event `timestamp` to keep track of that if you need to. pub fn poll_events(&mut self) -> Result> { let events = unsafe { self.inner.get_mut().poll_events() }; Ok( @@ -318,7 +321,7 @@ impl Camera<'_> { println!("Pixel format: {:?}", stream.pixel_format); Some(CameraEvent::RequestComplete { serial_id: request_id, - timestamp: request_info.timestamp, + queue_timestamp: request_info.timestamp, image: RawCameraImage { width: stream.width as usize, height: stream.height as usize, @@ -346,12 +349,16 @@ impl Drop for Camera<'_> { } } -/// Represents raw image data fetched from the camera +/// Represents raw image data fetched from the camera. #[derive(Debug, Clone, PartialEq, Eq)] pub struct RawCameraImage { + /// The pixel format for the image, if it is known. pub pixel_format: Option, + /// The width of the image. pub width: usize, + /// The height of the image. pub height: usize, + /// The raw data planes for the image. pub planes: Vec>, } @@ -390,11 +397,15 @@ impl RawCameraImage { /// Represents an event from the camera #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub enum CameraEvent { /// Triggered when a capture request has completed, containing a vec of the resulting image planes. RequestComplete { + /// The same `serial_id` that was returned from the function that queued this request. serial_id: u64, - timestamp: Instant, + /// When this event was __queued__ to the camera. + queue_timestamp: Instant, + /// The raw image data for this request, might not actually contain a real image (at the moment there isn't any way of determining success as far as I can tell). image: RawCameraImage, }, } diff --git a/src/config.rs b/src/config.rs index be22e92..167e5d0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -26,9 +26,11 @@ impl CameraConfig { pub(crate) fn get_inner(&mut self) -> &mut ffi::BindCameraConfiguration { &mut self.inner } + /// Get a reference to the vec of stream configurations contained within this camera configuration. pub fn streams(&self) -> &Vec { &self.streams } + /// Get a mutable reference to the vec of stream configurations contained within this camera configuration. pub fn streams_mut(&mut self) -> &mut Vec { &mut self.streams } @@ -82,6 +84,7 @@ impl StreamConfig { size.get().get_height() }) } + /// Get a human-readable description of this stream configuraiton from libcamera. pub fn description(&self) -> String { unsafe { self.inner.get().raw_to_string() } } diff --git a/src/controls.rs b/src/controls.rs index dbf5b3d..8bc7241 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -131,11 +131,17 @@ impl TryFrom<&ffi::ControlPair> for MinMaxValue { #[non_exhaustive] #[derive(Debug)] pub enum CameraControlValue { + /// A control value not containing a value. None, + /// A control value containing a boolean, e.g. autoexpose enable. Bool(MinMaxValue), + /// A control value containing a single byte value. Byte(MinMaxValue), + /// A control value containing a 32-bit integer, e.g. exposure time. Integer32(MinMaxValue), + /// A control value containing a 64-bit integer, e.g. frame duration limit. Integer64(MinMaxValue), + /// A control value containing a 32-bit float, e.g. brightness. Float(MinMaxValue), // String(MinMaxValue), // Rectangle(MinMaxValue), @@ -148,24 +154,47 @@ pub enum CameraControlValue { #[non_exhaustive] #[derive(Debug, Default)] pub struct CameraControls { + /// Autoexposure enable. pub ae_enable: Option>, + /// Autoexposure metering mode. + /// **TODO**: This should be an enum. pub ae_metering_mode: Option>, + /// Autoexposure constraint mode. + /// **TODO**: This should be an enum. pub ae_constraint_mode: Option>, + /// Autoexposure mode. + /// **TODO**: This should be an enum. pub ae_exposure_mode: Option>, + /// Exposure "value". pub exposure_value: Option>, + /// Exposure time. pub exposure_time: Option>, + /// Analogue signal gain. pub analogue_gain: Option>, + /// Brightness pub brightness: Option>, + /// Contrast pub contrast: Option>, + /// Auto white balance enable. pub awb_enable: Option>, + /// Auto white balance mode. + /// **TODO**: This should be an enum. pub awb_mode: Option>, + /// Colour gains. pub colour_gains: Option>, + /// Saturation. pub saturation: Option>, + /// Sharpness. pub sharpness: Option>, + /// Colour correction "matrix". pub colour_correction_matrix: Option>, // pub scaler_crop: Option>, // Rectangle TODO + /// Frame duration limit. pub frame_duration_limits: Option>, + /// Noise reduction mode. + /// **TODO**: This should be an enum. pub noise_reduction_mode: Option>, + /// Values not directly handled by this struct but found on your camera, maps control IDs to a tuple containing a name for the control as well as the value. pub others: HashMap, } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..303066e --- /dev/null +++ b/src/error.rs @@ -0,0 +1,32 @@ +use thiserror::Error; + +/// An error. +#[derive(Debug, Error)] +pub enum LibcameraError { + /// A C++ exception + #[error("Inner C++ error: {0}")] + InnerError(#[from] cxx::Exception), + /// An error converting an integer + #[error("Int conversion error: {0}")] + IntConversion(#[from] std::num::TryFromIntError), + /// An error when validating configuration + #[error("Configuration Invalid or Missing")] + InvalidConfig, + /// An error produced when it an image capture is requested but there are no buffers available. + #[error("No buffer ready for capture (all buffers in use, capture pictures slower!)")] + NoBufferReady, + /// An error emitted when a control value is attempted to be set to a value outside of the acceptable range. + #[error("Control value out of range!")] + InvalidControlValue, + /// An error en/decoding an image. + #[cfg(feature = "image")] + #[error("Image error: {0}")] + ImageError(#[from] ::image::ImageError), + /// An error produced when the decoded JPEG image is not in RGB888 format. + #[cfg(feature = "image")] + #[error("Image error: Bad image format.")] + BadImageFormat, +} + +/// Internal result type for convenience. +pub type Result = std::result::Result; diff --git a/src/image.rs b/src/image.rs index dbcfcf3..0dcdcc1 100644 --- a/src/image.rs +++ b/src/image.rs @@ -18,9 +18,13 @@ pub trait CameraImage { #[derive(Debug, Clone)] #[non_exhaustive] pub enum MultiImage { + /// Image in the BGR format. Bgr(BgrImage), + /// Image in the RGB format. Rgb(RgbImage), + /// Image in the YUYV 4:2:2 format. Yuyv(YuyvImage), + /// Image in the YUV 4:2:0 format. Yuv420(Yuv420Image), } @@ -78,7 +82,8 @@ impl CameraImage<1> for BgrImage { } impl BgrImage { - pub fn as_rgb(&self) -> Option { + /// Convert this image into a BGR image. + pub fn as_rgb(&self) -> RgbImage { RgbImage::from_planes( self.width, self.height, @@ -94,6 +99,7 @@ impl BgrImage { }) .collect()], ) + .expect("Failed to convert RGB to BGR (this should never fail.)") } } @@ -118,6 +124,7 @@ impl CameraImage<1> for RgbImage { None } } + /// Convert this image into BGR format. This should never return None. fn as_bgr(&self) -> Option { BgrImage::from_planes( self.width, diff --git a/src/lib.rs b/src/lib.rs index c50ff7f..df23f16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,34 +1,20 @@ -use thiserror::Error; +#![warn(missing_docs)] +#![doc = include_str!("../readme.md")] mod bridge; +/// Handles interfacing with cameras, get started using a CameraManager. pub mod camera; +/// Camera and stream configuration, e.g. how many streams, resolutions, and pixel formats. pub mod config; +/// Camera (runtime) controls, e.g. brightness, contrast. pub mod controls; +/// Errors +pub mod error; +/// Decoding images from the camera pub mod image; +/// All the things you *should* need to get started. pub mod prelude; #[cfg(test)] mod test; -#[derive(Debug, Error)] -pub enum LibcameraError { - #[error("Inner C++ error: {0}")] - InnerError(#[from] cxx::Exception), - #[error("Int conversion error: {0}")] - IntConversion(#[from] std::num::TryFromIntError), - #[error("Configuration Invalid or Missing")] - InvalidConfig, - #[error("No buffer ready for capture (all buffers in use, capture pictures slower!)")] - NoBufferReady, - #[error("Control value out of range!")] - InvalidControlValue, - #[error("Unknown ID in camera request")] - UnknownRequestId, - #[cfg(feature = "image")] - #[error("Image error: {0}")] - ImageError(#[from] ::image::ImageError), - #[cfg(feature = "image")] - #[error("Image error: Bad image format.")] - BadImageFormat, -} - -type Result = std::result::Result; +pub use error::{LibcameraError, Result}; diff --git a/src/test.rs b/src/test.rs index c7dce47..e38969f 100644 --- a/src/test.rs +++ b/src/test.rs @@ -16,14 +16,14 @@ fn test_camera() { cam.start_stream().unwrap(); println!("Started stream."); println!("Capturing frames..."); - for i in 0..50 { + for i in 0..6 { let brightness = cam.get_controls_mut().brightness.as_mut().unwrap(); brightness.set_value((brightness.get_value() + 0.6).rem_euclid(1.5) - 0.5); let contrast = cam.get_controls_mut().contrast.as_mut().unwrap(); contrast.set_value(contrast.get_value() + 0.02); cam.capture_next_picture(0).unwrap(); println!("Capturing image #{}", i); - std::thread::sleep(std::time::Duration::from_millis(100)); + std::thread::sleep(std::time::Duration::from_millis(200)); let events = cam.poll_events().unwrap(); for event in events { match event { @@ -35,7 +35,6 @@ fn test_camera() { .as_bgr() .unwrap() .as_rgb() - .unwrap() .encode_png() .unwrap(); let filename = format!("image_{serial_id}.png"); From 1b7267b59930027adec88a7d721b4f9ee0254001 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Fri, 8 Jul 2022 14:03:32 -0600 Subject: [PATCH 24/49] feat: changes for ps-native integration --- libcamera-bridge/camera.cpp | 12 ++++++++++++ libcamera-bridge/core.hpp | 2 ++ src/bridge.rs | 4 ++++ src/camera.rs | 29 +++++++++++++++++++++-------- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index 4cde8b2..d7e9c92 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -161,3 +161,15 @@ rust::Vec Camera::poll_events() { } return messages; } + +rust::Vec +Camera::poll_events_with_cookie(unsigned long request_cookie) { + rust::Vec messages; + while (!this->message_queue.empty()) { + if (this->message_queue.front().request_cookie == request_cookie) { + messages.push_back(this->message_queue.front()); + message_queue.pop(); + } + } + return messages; +} diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index dde6b78..173af66 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -99,6 +99,8 @@ struct Camera { void stop(); rust::Vec get_controls() const; rust::Vec poll_events(); + rust::Vec + poll_events_with_cookie(unsigned long request_cookie); }; struct CameraConfiguration { diff --git a/src/bridge.rs b/src/bridge.rs index 7513b89..58c716f 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -242,6 +242,10 @@ pub mod ffi { pub unsafe fn stop(self: Pin<&mut Camera>) -> Result<()>; pub unsafe fn get_controls(self: &Camera) -> Vec; pub unsafe fn poll_events(self: Pin<&mut Camera>) -> Vec; + pub unsafe fn poll_events_with_cookie( + self: Pin<&mut Camera>, + request_cookie: u64, + ) -> Vec; type CameraConfiguration; pub unsafe fn size(self: &CameraConfiguration) -> usize; diff --git a/src/camera.rs b/src/camera.rs index 47c4785..e274433 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::fmt::{self, Debug}; use std::marker::PhantomData; +use std::sync::RwLock; use std::time::Instant; use crate::bridge::{ffi, GetInner}; @@ -13,9 +14,12 @@ pub use ffi::StreamRole; /// Manages cameras pub struct CameraManager { - inner: ffi::BindCameraManager, + inner: RwLock, } +unsafe impl Send for CameraManager {} +unsafe impl Sync for CameraManager {} + impl Debug for CameraManager { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("CameraManager").finish_non_exhaustive() @@ -28,15 +32,17 @@ impl CameraManager { let mut cm = unsafe { ffi::make_camera_manager() }; // The primary safety concern for the CM is that it must be started once before calling all functions. unsafe { cm.get_mut().start() }?; - Ok(CameraManager { inner: cm }) + Ok(CameraManager { + inner: RwLock::new(cm), + }) } /// Get a list of all attached cameras pub fn get_camera_names(&self) -> Vec { - unsafe { self.inner.get().get_camera_ids() } + unsafe { self.inner.read().unwrap().get().get_camera_ids() } } /// Get a camera with a given name - pub fn get_camera_by_name(&mut self, name: &str) -> Result> { - let mut cam = unsafe { self.inner.get_mut().get_camera_by_id(name) }?; + pub fn get_camera_by_name(&self, name: &str) -> Result> { + let mut cam = unsafe { self.inner.write().unwrap().get_mut().get_camera_by_id(name) }?; unsafe { cam.get_mut().acquire() }?; let allocator = unsafe { ffi::make_frame_buffer_allocator(cam.get_mut()) }; let controls = CameraControls::from_libcamera(unsafe { cam.get().get_controls() }); @@ -57,7 +63,7 @@ impl CameraManager { impl Drop for CameraManager { fn drop(&mut self) { - unsafe { self.inner.get_mut().stop() }; + unsafe { self.inner.write().unwrap().get_mut().stop() }; } } @@ -116,6 +122,9 @@ pub struct Camera<'a> { request_infos: HashMap, } +unsafe impl Send for Camera<'_> {} +unsafe impl Sync for Camera<'_> {} + impl Debug for Camera<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Camera") @@ -301,8 +310,12 @@ impl Camera<'_> { /// Poll events from the camera. /// /// The results should be in order of when the camera sent them, but not neccesarily in order of when they were initially queued. Make sure to use `serial_id`, or the event `timestamp` to keep track of that if you need to. - pub fn poll_events(&mut self) -> Result> { - let events = unsafe { self.inner.get_mut().poll_events() }; + pub fn poll_events(&mut self, match_id: Option) -> Result> { + let events = if let Some(match_id) = match_id { + unsafe { self.inner.get_mut().poll_events_with_cookie(match_id) } + } else { + unsafe { self.inner.get_mut().poll_events() } + }; Ok( events .into_iter() From 9c3cf2eb9e5d056b0a6e96d5735d6b659ec237cd Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Fri, 8 Jul 2022 15:45:39 -0600 Subject: [PATCH 25/49] fix: fix c++ integer type bit widths --- libcamera-bridge/camera.cpp | 4 +- libcamera-bridge/camera_configuration.cpp | 2 +- libcamera-bridge/control_id.cpp | 2 +- libcamera-bridge/control_value.cpp | 12 +++--- libcamera-bridge/core.hpp | 49 +++++++++++------------ libcamera-bridge/frame_buffer.cpp | 4 +- libcamera-bridge/memory_buffer.cpp | 4 +- libcamera-bridge/request.cpp | 4 +- libcamera-bridge/size.cpp | 10 ++--- 9 files changed, 45 insertions(+), 46 deletions(-) diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index d7e9c92..8c02ae4 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -84,7 +84,7 @@ void Camera::configure(CameraConfiguration &conf) { } } -BindRequest Camera::create_request(unsigned long cookie) { +BindRequest Camera::create_request(uint64_t cookie) { VALIDATE_POINTERS() std::unique_ptr req = this->inner->createRequest(cookie); @@ -163,7 +163,7 @@ rust::Vec Camera::poll_events() { } rust::Vec -Camera::poll_events_with_cookie(unsigned long request_cookie) { +Camera::poll_events_with_cookie(uint64_t request_cookie) { rust::Vec messages; while (!this->message_queue.empty()) { if (this->message_queue.front().request_cookie == request_cookie) { diff --git a/libcamera-bridge/camera_configuration.cpp b/libcamera-bridge/camera_configuration.cpp index 8fe5711..9cf7ed7 100644 --- a/libcamera-bridge/camera_configuration.cpp +++ b/libcamera-bridge/camera_configuration.cpp @@ -14,7 +14,7 @@ size_t CameraConfiguration::size() const { return this->inner->size(); } -BindStreamConfiguration CameraConfiguration::at(unsigned int idx) { +BindStreamConfiguration CameraConfiguration::at(uint32_t idx) { VALIDATE_POINTERS() libcamera::StreamConfiguration *str = &this->inner->at(idx); diff --git a/libcamera-bridge/control_id.cpp b/libcamera-bridge/control_id.cpp index 095c47a..6b1fd0c 100644 --- a/libcamera-bridge/control_id.cpp +++ b/libcamera-bridge/control_id.cpp @@ -8,7 +8,7 @@ rust::String ControlId::get_name() const { return this->inner->name(); } -unsigned int ControlId::get_id() const { +uint32_t ControlId::get_id() const { VALIDATE_POINTERS() return this->inner->id(); diff --git a/libcamera-bridge/control_value.cpp b/libcamera-bridge/control_value.cpp index 792220f..3a79f4f 100644 --- a/libcamera-bridge/control_value.cpp +++ b/libcamera-bridge/control_value.cpp @@ -56,25 +56,25 @@ bool ControlValue::get_bool() const { return this->inner.get(); } -unsigned char ControlValue::get_u8() const { +uint8_t ControlValue::get_u8() const { if (this->inner.type() != libcamera::ControlType::ControlTypeByte) { throw std::runtime_error("Bad type! Expected Byte."); } - return this->inner.get(); + return this->inner.get(); } -int ControlValue::get_i32() const { +int32_t ControlValue::get_i32() const { if (this->inner.type() != libcamera::ControlType::ControlTypeInteger32) { throw std::runtime_error("Bad type! Expected I32."); } - return this->inner.get(); + return this->inner.get(); } -long ControlValue::get_i64() const { +int64_t ControlValue::get_i64() const { if (this->inner.type() != libcamera::ControlType::ControlTypeInteger64) { throw std::runtime_error("Bad type! Expected I64."); } - return this->inner.get(); + return this->inner.get(); } float ControlValue::get_f32() const { diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 173af66..0cd33b4 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -80,27 +80,26 @@ struct Camera { std::mutex message_mutex; std::queue message_queue; - std::unordered_map controls_by_id; + std::unordered_map controls_by_id; public: explicit Camera(std::shared_ptr inner_); ~Camera(); std::shared_ptr into_shared(); - const libcamera::ControlId *get_control_by_id(unsigned int id) const; + const libcamera::ControlId *get_control_by_id(uint32_t id) const; void acquire(); void release(); BindCameraConfiguration generate_configuration( rust::Slice /*roles*/); void configure(CameraConfiguration &conf); - BindRequest create_request(unsigned long cookie); + BindRequest create_request(uint64_t cookie); void queue_request(Request &req); void start(); void stop(); rust::Vec get_controls() const; rust::Vec poll_events(); - rust::Vec - poll_events_with_cookie(unsigned long request_cookie); + rust::Vec poll_events_with_cookie(uint64_t request_cookie); }; struct CameraConfiguration { @@ -114,7 +113,7 @@ struct CameraConfiguration { libcamera::CameraConfiguration *into_ptr(); size_t size() const; - BindStreamConfiguration at(unsigned int idx); + BindStreamConfiguration at(uint32_t idx); CameraConfigurationStatus validate(); }; @@ -150,7 +149,7 @@ struct PixelFormat { [[nodiscard]] rust::String raw_to_string() const; }; -BindSize new_size(unsigned int width, unsigned int height); +BindSize new_size(uint32_t width, uint32_t height); struct Size { private: @@ -160,10 +159,10 @@ struct Size { explicit Size(libcamera::Size inner_) : inner(inner_) {} libcamera::Size into_inner(); - void set_width(unsigned int width); - [[nodiscard]] unsigned int get_width() const; - void set_height(unsigned int height); - [[nodiscard]] unsigned int get_height() const; + void set_width(uint32_t width); + [[nodiscard]] uint32_t get_width() const; + void set_height(uint32_t height); + [[nodiscard]] uint32_t get_height() const; [[nodiscard]] rust::String raw_to_string() const; }; @@ -203,8 +202,8 @@ struct FrameBuffer { libcamera::FrameBuffer *into_ptr(); [[nodiscard]] rust::Vec planes() const; - void set_cookie(unsigned int cookie); - unsigned int get_cookie() const; + void set_cookie(uint32_t cookie); + uint32_t get_cookie() const; }; size_t fd_len(int fd); @@ -228,15 +227,15 @@ BindMemoryBuffer mmap_plane(int fd, size_t len); struct MemoryBuffer { private: - const unsigned char *pointer; + const uint8_t *pointer; size_t length; public: - MemoryBuffer(const unsigned char *pointer_, size_t length_) + MemoryBuffer(const uint8_t *pointer_, size_t length_) : pointer(pointer_), length(length_) {} BindMemoryBuffer sub_buffer(size_t offset, size_t length); - [[nodiscard]] rust::Vec read_to_vec() const; + [[nodiscard]] rust::Vec read_to_vec() const; }; struct Request { @@ -249,8 +248,8 @@ struct Request { libcamera::Request *into_ptr(); void add_buffer(const Stream &stream, FrameBuffer &buffer); - BindControlValue get_control(unsigned int id) const; - void set_control(unsigned int id, const ControlValue &value); + BindControlValue get_control(uint32_t id) const; + void set_control(uint32_t id, const ControlValue &value); [[nodiscard]] rust::String raw_to_string() const; }; @@ -262,14 +261,14 @@ struct ControlId { explicit ControlId(const libcamera::ControlId *inner_) : inner{inner_} {} rust::String get_name() const; - unsigned int get_id() const; + uint32_t get_id() const; CameraControlType get_type() const; }; BindControlValue new_control_value_bool(bool value); -BindControlValue new_control_value_u8(unsigned char value); -BindControlValue new_control_value_i32(int value); -BindControlValue new_control_value_i64(long int value); +BindControlValue new_control_value_u8(uint8_t value); +BindControlValue new_control_value_i32(int32_t value); +BindControlValue new_control_value_i64(int64_t value); BindControlValue new_control_value_f32(float value); BindControlValue new_control_value_string(rust::String value); @@ -282,9 +281,9 @@ struct ControlValue { const libcamera::ControlValue &get_inner() const; bool get_bool() const; - unsigned char get_u8() const; - int get_i32() const; - long get_i64() const; + uint8_t get_u8() const; + int32_t get_i32() const; + int64_t get_i64() const; float get_f32() const; [[nodiscard]] rust::String raw_to_string() const; diff --git a/libcamera-bridge/frame_buffer.cpp b/libcamera-bridge/frame_buffer.cpp index 07172e0..41500ce 100644 --- a/libcamera-bridge/frame_buffer.cpp +++ b/libcamera-bridge/frame_buffer.cpp @@ -22,13 +22,13 @@ rust::Vec FrameBuffer::planes() const { return vec; } -void FrameBuffer::set_cookie(unsigned int cookie) { +void FrameBuffer::set_cookie(uint32_t cookie) { VALIDATE_POINTERS() this->inner->setCookie(cookie); } -unsigned int FrameBuffer::get_cookie() const { +uint32_t FrameBuffer::get_cookie() const { VALIDATE_POINTERS() return this->inner->cookie(); diff --git a/libcamera-bridge/memory_buffer.cpp b/libcamera-bridge/memory_buffer.cpp index 823008c..630c633 100644 --- a/libcamera-bridge/memory_buffer.cpp +++ b/libcamera-bridge/memory_buffer.cpp @@ -13,8 +13,8 @@ BindMemoryBuffer MemoryBuffer::sub_buffer(size_t offset, size_t length) { return buffer; } -rust::Vec MemoryBuffer::read_to_vec() const { - rust::Vec buf; +rust::Vec MemoryBuffer::read_to_vec() const { + rust::Vec buf; for (size_t i = 0; i < this->length; i++) { // The point of this class is to safely wrap raw memory pointers. // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) diff --git a/libcamera-bridge/request.cpp b/libcamera-bridge/request.cpp index 978f811..3f61253 100644 --- a/libcamera-bridge/request.cpp +++ b/libcamera-bridge/request.cpp @@ -19,7 +19,7 @@ libcamera::Request *Request::into_ptr() { #include -BindControlValue Request::get_control(unsigned int id) const { +BindControlValue Request::get_control(uint32_t id) const { VALIDATE_POINTERS() libcamera::ControlList &controls = this->inner->controls(); @@ -33,7 +33,7 @@ BindControlValue Request::get_control(unsigned int id) const { return control_value; } -void Request::set_control(unsigned int id, const ControlValue &value) { +void Request::set_control(uint32_t id, const ControlValue &value) { VALIDATE_POINTERS() libcamera::ControlList &controls = this->inner->controls(); diff --git a/libcamera-bridge/size.cpp b/libcamera-bridge/size.cpp index 903ff0a..6eafe6c 100644 --- a/libcamera-bridge/size.cpp +++ b/libcamera-bridge/size.cpp @@ -2,7 +2,7 @@ #include "libcamera-rs/src/bridge.rs.h" -BindSize new_size(unsigned int width, unsigned int height) { +BindSize new_size(uint32_t width, uint32_t height) { BindSize size{ .inner = std::make_unique(libcamera::Size(width, height)), }; @@ -11,12 +11,12 @@ BindSize new_size(unsigned int width, unsigned int height) { libcamera::Size Size::into_inner() { return this->inner; } -void Size::set_width(unsigned int width) { this->inner.width = width; } +void Size::set_width(uint32_t width) { this->inner.width = width; } -unsigned int Size::get_width() const { return this->inner.width; } +uint32_t Size::get_width() const { return this->inner.width; } -void Size::set_height(unsigned int height) { this->inner.height = height; } +void Size::set_height(uint32_t height) { this->inner.height = height; } -unsigned int Size::get_height() const { return this->inner.height; } +uint32_t Size::get_height() const { return this->inner.height; } rust::String Size::raw_to_string() const { return this->inner.toString(); } From c3d7f750c7703c09232298aa8f90621449917b18 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Fri, 8 Jul 2022 15:51:52 -0600 Subject: [PATCH 26/49] chore: remove unused submodules --- gnutls | 1 - libcamera | 1 - 2 files changed, 2 deletions(-) delete mode 160000 gnutls delete mode 160000 libcamera diff --git a/gnutls b/gnutls deleted file mode 160000 index 41c6b26..0000000 --- a/gnutls +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 41c6b26cf099803fc2515ca800e0439f152d5a0f diff --git a/libcamera b/libcamera deleted file mode 160000 index 1735d17..0000000 --- a/libcamera +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1735d176bc6139ae76dae0c84ab15bda5884764d From e588ca341ccd7525397064bcf4dacfe719cb73a8 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Fri, 8 Jul 2022 16:04:04 -0600 Subject: [PATCH 27/49] fix: update the framebuffer api to match libcamera changes --- libcamera-bridge/core.hpp | 4 ++-- libcamera-bridge/frame_buffer.cpp | 4 ++-- readme.md | 4 ++++ src/bridge.rs | 6 +++--- src/camera.rs | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 0cd33b4..9bc2adf 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -202,8 +202,8 @@ struct FrameBuffer { libcamera::FrameBuffer *into_ptr(); [[nodiscard]] rust::Vec planes() const; - void set_cookie(uint32_t cookie); - uint32_t get_cookie() const; + void set_cookie(uint64_t cookie); + uint64_t get_cookie() const; }; size_t fd_len(int fd); diff --git a/libcamera-bridge/frame_buffer.cpp b/libcamera-bridge/frame_buffer.cpp index 41500ce..e7467fa 100644 --- a/libcamera-bridge/frame_buffer.cpp +++ b/libcamera-bridge/frame_buffer.cpp @@ -22,13 +22,13 @@ rust::Vec FrameBuffer::planes() const { return vec; } -void FrameBuffer::set_cookie(uint32_t cookie) { +void FrameBuffer::set_cookie(uint64_t cookie) { VALIDATE_POINTERS() this->inner->setCookie(cookie); } -uint32_t FrameBuffer::get_cookie() const { +uint64_t FrameBuffer::get_cookie() const { VALIDATE_POINTERS() return this->inner->cookie(); diff --git a/readme.md b/readme.md index a375385..a629185 100644 --- a/readme.md +++ b/readme.md @@ -53,3 +53,7 @@ fn main() { ``` Most functions should be well enough documented with rustdoc. + +# TODO + +Currently, this *will* segfault if a thread containing a reference to a `Camera` panics. diff --git a/src/bridge.rs b/src/bridge.rs index 58c716f..f4c6c89 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -186,7 +186,7 @@ pub mod ffi { struct CameraMessage { message_type: CameraMessageType, request_cookie: u64, - buffer_cookie: u32, + buffer_cookie: u64, } #[repr(i32)] @@ -303,8 +303,8 @@ pub mod ffi { type FrameBuffer; pub unsafe fn planes(self: &FrameBuffer) -> Vec; - pub unsafe fn set_cookie(self: Pin<&mut FrameBuffer>, cookie: u32); - pub unsafe fn get_cookie(self: &FrameBuffer) -> u32; + pub unsafe fn set_cookie(self: Pin<&mut FrameBuffer>, cookie: u64); + pub unsafe fn get_cookie(self: &FrameBuffer) -> u64; type FrameBufferPlane; pub unsafe fn get_fd(self: &FrameBufferPlane) -> i32; diff --git a/src/camera.rs b/src/camera.rs index e274433..64d4fbc 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -219,7 +219,7 @@ impl Camera<'_> { // Map memory for buffers for mut buffer in unsafe { self.allocator.get().buffers(camera_stream.stream.get_mut()) } { let buffer_id = camera_stream.buffers.len(); - unsafe { buffer.get_mut().set_cookie(buffer_id as u32) }; + unsafe { buffer.get_mut().set_cookie(buffer_id as u64) }; let mut planes = Vec::new(); let mut mapped_buffers: HashMap, usize, usize)> = HashMap::new(); From 183e415d34bc3e7d75b6bbf6bd2f810e73e0b6b6 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Fri, 8 Jul 2022 16:08:47 -0600 Subject: [PATCH 28/49] fix: more c++ integer type bit widths --- libcamera-bridge/control_value.cpp | 6 +++--- libcamera-bridge/fd.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libcamera-bridge/control_value.cpp b/libcamera-bridge/control_value.cpp index 3a79f4f..a778b8e 100644 --- a/libcamera-bridge/control_value.cpp +++ b/libcamera-bridge/control_value.cpp @@ -9,21 +9,21 @@ BindControlValue new_control_value_bool(bool value) { return control_value; } -BindControlValue new_control_value_u8(unsigned char value) { +BindControlValue new_control_value_u8(uint8_t value) { BindControlValue control_value{ .inner = std::make_unique(libcamera::ControlValue(value)), }; return control_value; } -BindControlValue new_control_value_i32(int value) { +BindControlValue new_control_value_i32(int32_t value) { BindControlValue control_value{ .inner = std::make_unique(libcamera::ControlValue(value)), }; return control_value; } -BindControlValue new_control_value_i64(long int value) { +BindControlValue new_control_value_i64(int64_t value) { BindControlValue control_value{ .inner = std::make_unique(libcamera::ControlValue(value)), }; diff --git a/libcamera-bridge/fd.cpp b/libcamera-bridge/fd.cpp index 421f9b4..d1a1ff5 100644 --- a/libcamera-bridge/fd.cpp +++ b/libcamera-bridge/fd.cpp @@ -21,6 +21,6 @@ BindMemoryBuffer mmap_plane(int fd, size_t len) { throw error_from_code(errno); } BindMemoryBuffer buffer{.inner = std::make_unique( - static_cast(address), len)}; + static_cast(address), len)}; return buffer; } From 0674abc7581d6f852cbf71cfb8cd880f22d9b52d Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Mon, 11 Jul 2022 15:02:29 -0600 Subject: [PATCH 29/49] fix: cleanup and fixes --- readme.md | 5 ++++- src/camera.rs | 8 ++++++++ src/test.rs | 17 +++++++++++++++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index a629185..76e92b7 100644 --- a/readme.md +++ b/readme.md @@ -15,6 +15,8 @@ fn main() { // Attempt to assign the stream's pixel format and size. stream.set_pixel_format(PixelFormat::Mjpeg); stream.set_size(640, 480); + // Apply the configuration. + cam.apply_config().unwrap(); // Start the camera stream. cam.start_stream().unwrap(); // Take 10 pictures @@ -29,7 +31,7 @@ fn main() { // Wait for a bit. std::thread::sleep(std::time::Duration::from_millis(200)); // Poll events (containing the images) - let events = cam.poll_events().unwrap(); + let events = cam.poll_events(None).unwrap(); for event in events { match event { CameraEvent::RequestComplete { @@ -46,6 +48,7 @@ fn main() { let filename = format!("image_{serial_id}.png"); std::fs::write(&filename, rgb_image).unwrap(); } + _ => todo!() } } } diff --git a/src/camera.rs b/src/camera.rs index 64d4fbc..a6f490e 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -53,6 +53,7 @@ impl CameraManager { inner: cam, allocator, streams: Vec::new(), + configured: false, started: false, controls, next_request_id: 0, @@ -116,6 +117,7 @@ pub struct Camera<'a> { inner: ffi::BindCamera, allocator: ffi::BindFrameBufferAllocator, streams: Vec, + configured: bool, started: bool, controls: CameraControls, next_request_id: u64, @@ -141,6 +143,7 @@ impl Debug for Camera<'_> { impl Camera<'_> { /// Generate a configuration for this camera using the given set of stream roles to generate an corresponding set of streams. pub fn generate_config(&mut self, caps: &[StreamRole]) -> Result<&mut CameraConfig> { + self.configured = false; let config = unsafe { self.inner.get_mut().generate_configuration(caps) }?; self.config = Some(CameraConfig::wrap_inner(config)?); self.config.as_mut().ok_or(LibcameraError::InvalidConfig) @@ -155,6 +158,7 @@ impl Camera<'_> { _ => (false, Err(LibcameraError::InvalidConfig)), }; if set { + self.configured = true; unsafe { self.inner.get_mut().configure(config.get_inner().get_mut()) }?; } result @@ -183,6 +187,9 @@ impl Camera<'_> { /// # Panics /// This will panic if the buffer sizes produced by libcamera extend past the end of the actual camera memory buffer. pub fn start_stream(&mut self) -> Result { + if !self.configured { + return Err(LibcameraError::InvalidConfig); + } if self.started { // Do nothing if the camera was already started. return Ok(false); @@ -356,6 +363,7 @@ impl Camera<'_> { impl Drop for Camera<'_> { fn drop(&mut self) { + eprintln!("Dropping inner camera."); self.streams = Vec::new(); unsafe { self.inner.get_mut().stop() }.unwrap(); unsafe { self.inner.get_mut().release() }.unwrap(); diff --git a/src/test.rs b/src/test.rs index e38969f..1ed0d12 100644 --- a/src/test.rs +++ b/src/test.rs @@ -3,7 +3,7 @@ use crate::prelude::{CameraEvent, CameraManager, PixelFormat, StreamRole}; #[cfg(feature = "image")] #[test] fn test_camera() { - let mut cm = CameraManager::new().unwrap(); + let cm = CameraManager::new().unwrap(); println!("cm: {cm:?}"); let mut cam = cm.get_camera_by_name(&cm.get_camera_names()[0]).unwrap(); println!("cam: {cam:?}"); @@ -24,7 +24,7 @@ fn test_camera() { cam.capture_next_picture(0).unwrap(); println!("Capturing image #{}", i); std::thread::sleep(std::time::Duration::from_millis(200)); - let events = cam.poll_events().unwrap(); + let events = cam.poll_events(None).unwrap(); for event in events { match event { CameraEvent::RequestComplete { @@ -46,3 +46,16 @@ fn test_camera() { } println!("Done!"); } + +#[test] +#[should_panic] +fn panic_with_camera() { + // Wait to ensure we can lock the camera (when running many tests back-to-back). + std::thread::sleep(std::time::Duration::from_secs(5)); + let cm = CameraManager::new().unwrap(); + let mut cam = cm.get_camera_by_name(&cm.get_camera_names()[0]).unwrap(); + cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); + cam.apply_config().unwrap(); + cam.start_stream().unwrap(); + panic!("Ah!"); +} From 7110593b96b2d99aa227a8cf6ccaa2349c8eb8da Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Tue, 19 Jul 2022 13:14:32 -0600 Subject: [PATCH 30/49] Fix weird issue --- build.rs | 47 +++++++++++++++++------------ libcamera-bridge/camera_manager.cpp | 9 ++++-- libcamera-bridge/pixel_format.cpp | 12 -------- readme.md | 7 +++-- src/bridge.rs | 4 --- src/prelude.rs | 1 + src/test.rs | 14 ++++++++- 7 files changed, 54 insertions(+), 40 deletions(-) diff --git a/build.rs b/build.rs index 87c94c7..59b2223 100644 --- a/build.rs +++ b/build.rs @@ -1,30 +1,39 @@ +const SOURCES: [&str; 15] = [ + "libcamera-bridge/camera_manager.cpp", + "libcamera-bridge/camera.cpp", + "libcamera-bridge/camera_configuration.cpp", + "libcamera-bridge/stream_configuration.cpp", + "libcamera-bridge/pixel_format.cpp", + "libcamera-bridge/size.cpp", + "libcamera-bridge/stream.cpp", + "libcamera-bridge/frame_buffer_allocator.cpp", + "libcamera-bridge/frame_buffer.cpp", + "libcamera-bridge/frame_buffer_plane.cpp", + "libcamera-bridge/fd.cpp", + "libcamera-bridge/memory_buffer.cpp", + "libcamera-bridge/request.cpp", + "libcamera-bridge/control_id.cpp", + "libcamera-bridge/control_value.cpp", +]; + fn main() { + println!("cargo:rerun-if-changed=src/bridge.rs"); + println!("cargo:rerun-if-changed=libcamera-bridge/core.hpp"); + for source in &SOURCES { + println!("cargo:rerun-if-changed={source}"); + } + cxx_build::bridge("src/bridge.rs") - .file("libcamera-bridge/camera_manager.cpp") - .file("libcamera-bridge/camera.cpp") - .file("libcamera-bridge/camera_configuration.cpp") - .file("libcamera-bridge/stream_configuration.cpp") - .file("libcamera-bridge/pixel_format.cpp") - .file("libcamera-bridge/size.cpp") - .file("libcamera-bridge/stream.cpp") - .file("libcamera-bridge/frame_buffer_allocator.cpp") - .file("libcamera-bridge/frame_buffer.cpp") - .file("libcamera-bridge/frame_buffer_plane.cpp") - .file("libcamera-bridge/fd.cpp") - .file("libcamera-bridge/memory_buffer.cpp") - .file("libcamera-bridge/request.cpp") - .file("libcamera-bridge/control_id.cpp") - .file("libcamera-bridge/control_value.cpp") .flag_if_supported("-std=c++17") + .warnings(true) + .extra_warnings(true) + .files(SOURCES) .include("/usr/local/include/libcamera") .include("libcamera/build/include/libcamera") .compile("libcamera-bridge"); - println!("cargo:rerun-if-changed=src/bridge.rs"); - println!("cargo:rerun-if-changed=libcamera-bridge/*.cpp"); - println!("cargo:rerun-if-changed=libcamera-bridge/core.hpp"); - // link libcamera + println!("cargo:rustc-link-search=/usr/local/lib"); println!("cargo:rustc-link-lib=dylib=camera"); println!("cargo:rustc-link-lib=dylib=camera-base"); } diff --git a/libcamera-bridge/camera_manager.cpp b/libcamera-bridge/camera_manager.cpp index 993dba8..5cd8d0a 100644 --- a/libcamera-bridge/camera_manager.cpp +++ b/libcamera-bridge/camera_manager.cpp @@ -2,6 +2,8 @@ #include "libcamera-rs/src/bridge.rs.h" +#include + BindCameraManager make_camera_manager() { BindCameraManager manager{ .inner = std::make_unique( @@ -38,8 +40,11 @@ rust::Vec CameraManager::get_camera_ids() const { BindCamera CameraManager::get_camera_by_id(rust::Str id) { VALIDATE_POINTERS() - std::shared_ptr cam = this->inner->get(std::string(id)); - if (!cam) { + std::string cam_id = std::string(id); + std::shared_ptr cam = this->inner->get(cam_id); + size_t uc = cam.use_count(); // C++ header mismatch will cause this to be + // completely silly + if (!cam || uc > INT_MAX) { throw error_from_code(ENODEV); } BindCamera bind_cam{ diff --git a/libcamera-bridge/pixel_format.cpp b/libcamera-bridge/pixel_format.cpp index f76b12d..36c9a99 100644 --- a/libcamera-bridge/pixel_format.cpp +++ b/libcamera-bridge/pixel_format.cpp @@ -31,12 +31,6 @@ BindPixelFormat get_default_pixel_format(DefaultPixelFormat default_format) { case DefaultPixelFormat::Yuv422: fmt = &libcamera::formats::YUV422; break; - case DefaultPixelFormat::Yvu422: - fmt = &libcamera::formats::YVU422; - break; - case DefaultPixelFormat::Yuv444: - fmt = &libcamera::formats::YUV444; - break; case DefaultPixelFormat::Mjpeg: fmt = &libcamera::formats::MJPEG; break; @@ -75,12 +69,6 @@ DefaultPixelFormat PixelFormat::as_default_pixel_format() const { if (this->inner == libcamera::formats::YUV422) { return DefaultPixelFormat::Yuv422; } - if (this->inner == libcamera::formats::YVU422) { - return DefaultPixelFormat::Yvu422; - } - if (this->inner == libcamera::formats::YUV444) { - return DefaultPixelFormat::Yuv444; - } if (this->inner == libcamera::formats::MJPEG) { return DefaultPixelFormat::Mjpeg; } diff --git a/readme.md b/readme.md index 76e92b7..97177e1 100644 --- a/readme.md +++ b/readme.md @@ -57,6 +57,9 @@ fn main() { Most functions should be well enough documented with rustdoc. -# TODO +# NOTE -Currently, this *will* segfault if a thread containing a reference to a `Camera` panics. +When cross compiling for raspberry pi, ensure `_GLIBCXX_HAVE_ATOMIC_LOCK_POLICY` is unset in `c++config.h`. +Otherwise the shared pointer to the camera will be misinterpreted by the inner reference counted lock type. +This results in the shared pointer that is returned by libcamera appearing to the main thread to only have 1 reference, +which causes it to be immediately destructed, causing it to be invalid by get_camera is called. diff --git a/src/bridge.rs b/src/bridge.rs index f4c6c89..6eaffe6 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -51,10 +51,6 @@ pub mod ffi { Yuv420, /// 16bpp*, chroma subsampling (U, V half width), three channel image, YUV (4:2:2) encoing, order Y', U, V Yuv422, - /// 16bpp*, chroma subsampling (U, V half width), three channel image, YUV (4:2:2) encoing, order Y', V, U - Yvu422, - /// 24bpp, three channel image, YUV (4:4:4) encoing, order Y', U, V - Yuv444, /// MJPEG (motion JPEG) encoding, effectively one JPEG image per frame Mjpeg, } diff --git a/src/prelude.rs b/src/prelude.rs index bb1e30f..cc96e76 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,2 +1,3 @@ pub use crate::camera::{CameraEvent, CameraManager, StreamRole}; pub use crate::config::PixelFormat; +pub use crate::error::LibcameraError; diff --git a/src/test.rs b/src/test.rs index 1ed0d12..4229ded 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,4 +1,4 @@ -use crate::prelude::{CameraEvent, CameraManager, PixelFormat, StreamRole}; +use crate::prelude::{CameraEvent, CameraManager, LibcameraError, PixelFormat, StreamRole}; #[cfg(feature = "image")] #[test] @@ -59,3 +59,15 @@ fn panic_with_camera() { cam.start_stream().unwrap(); panic!("Ah!"); } + +#[test] +fn try_start_before_configure() { + let cm = CameraManager::new().unwrap(); + println!("camers: {:?}", cm.get_camera_names()); + let mut cam = cm.get_camera_by_name(&cm.get_camera_names()[0]).unwrap(); + assert!(matches!( + cam.start_stream(), + Err(LibcameraError::InvalidConfig) + )); + cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); +} From 47996dbc5c4a2cc8486701b2f4d0b7bdf3f22ba0 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Tue, 19 Jul 2022 15:28:28 -0600 Subject: [PATCH 31/49] feat: stopping the camera stream --- src/camera.rs | 17 ++++++++++++++++- src/controls.rs | 5 +++-- src/test.rs | 20 +++++++++++++++++++- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index a6f490e..3a052a3 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -274,6 +274,22 @@ impl Camera<'_> { unsafe { self.inner.get_mut().start() }?; Ok(true) } + /// Stop the camera stream + pub fn stop_stream(&mut self) -> Result<()> { + unsafe { self.inner.get_mut().stop() }?; + for stream_config in self + .config + .as_ref() + .ok_or(LibcameraError::InvalidConfig)? + .streams() + { + let mut stream = unsafe { stream_config.get_inner().get().stream() }; + unsafe { self.allocator.get_mut().free(stream.get_mut()) }?; + } + self.streams = Vec::new(); + self.started = false; + Ok(()) + } /// Start the process to capture an image from the camera. /// /// # Returns @@ -363,7 +379,6 @@ impl Camera<'_> { impl Drop for Camera<'_> { fn drop(&mut self) { - eprintln!("Dropping inner camera."); self.streams = Vec::new(); unsafe { self.inner.get_mut().stop() }.unwrap(); unsafe { self.inner.get_mut().release() }.unwrap(); diff --git a/src/controls.rs b/src/controls.rs index 8bc7241..6c2ab83 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -276,7 +276,8 @@ impl CameraControls { _ => false, }; if !did_name_match { - if let Some(control_value) = match unsafe { control.id.get().get_type() } { + let control_type = unsafe { control.id.get().get_type() }; + if let Some(control_value) = match control_type { ffi::CameraControlType::None => Some(CameraControlValue::None), ffi::CameraControlType::Bool => (&control).try_into().ok().map(CameraControlValue::Bool), ffi::CameraControlType::Byte => (&control).try_into().ok().map(CameraControlValue::Byte), @@ -300,7 +301,7 @@ impl CameraControls { .others .insert(unsafe { control.id.get().get_id() }, (name, control_value)); } else { - eprintln!("Camera control with conflicting types: {name}"); + eprintln!("Camera control with conflicting types: {name} is supposed to have type of {control_type:?}."); } } } diff --git a/src/test.rs b/src/test.rs index 4229ded..c65871e 100644 --- a/src/test.rs +++ b/src/test.rs @@ -10,7 +10,7 @@ fn test_camera() { let conf = cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); println!("conf: {conf:?}"); let stream = &mut conf.streams_mut()[0]; - stream.set_pixel_format(PixelFormat::Mjpeg); + stream.set_pixel_format(PixelFormat::Yuv420); stream.set_size(640, 480); println!("Configuration applied: {:?}", cam.apply_config().unwrap()); cam.start_stream().unwrap(); @@ -71,3 +71,21 @@ fn try_start_before_configure() { )); cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); } + +#[test] +fn stop_start_stream() { + let cm = CameraManager::new().unwrap(); + let mut cam = cm.get_camera_by_name(&cm.get_camera_names()[0]).unwrap(); + let conf = cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); + let stream = &mut conf.streams_mut()[0]; + stream.set_pixel_format(PixelFormat::Yuv420); + stream.set_size(640, 480); + cam.apply_config().unwrap(); + for _i in 0..6 { + cam.start_stream().unwrap(); + cam.capture_next_picture(0).unwrap(); + std::thread::sleep(std::time::Duration::from_millis(500)); + cam.poll_events(None).unwrap(); + cam.stop_stream().unwrap(); + } +} From f49a3f91306dfba15d6eb3e2544a4ea1a9a3d178 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Tue, 19 Jul 2022 17:14:54 -0600 Subject: [PATCH 32/49] feat: get camera control valid values --- libcamera-bridge/camera.cpp | 8 ++++++++ src/bridge.rs | 1 + src/bridge/test.rs | 7 ++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index 8c02ae4..3d95ab0 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -130,6 +130,13 @@ rust::Vec Camera::get_controls() const { rust::Vec controls; for (const auto &[control, value] : this->inner->controls()) { + rust::Vec possible_values; + for (const auto &value : value.values()) { + BindControlValue bind_value{ + .inner = std::make_unique(value), + }; + possible_values.push_back(std::move(bind_value)); + } ControlPair control_pair{ .id = { @@ -147,6 +154,7 @@ rust::Vec Camera::get_controls() const { { .inner = std::make_unique(value.def()), }, + .valid_values = std::move(possible_values), }; controls.push_back(std::move(control_pair)); } diff --git a/src/bridge.rs b/src/bridge.rs index 6eaffe6..500378f 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -204,6 +204,7 @@ pub mod ffi { min: BindControlValue, max: BindControlValue, value: BindControlValue, + valid_values: Vec, } extern "C++" { diff --git a/src/bridge/test.rs b/src/bridge/test.rs index 4c2b989..497733f 100644 --- a/src/bridge/test.rs +++ b/src/bridge/test.rs @@ -49,11 +49,16 @@ fn test_unsafe_camera() { let controls = unsafe { camera.get().get_controls() }; for control in &controls { println!( - "Camera control '{}': ID={}, type={:?}, value={}", + "Camera control '{}': ID={}, type={:?}, value={}, values={:?}", unsafe { control.id.get().get_name() }, unsafe { control.id.get().get_id() }, unsafe { control.id.get().get_type() }, unsafe { control.value.get().raw_to_string() }, + control + .valid_values + .iter() + .map(|v| unsafe { v.get().raw_to_string() }) + .collect::>(), ); } From b53eb5b59452543eacf869dbba9b2b93157bc1be Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 20 Jul 2022 11:59:58 -0600 Subject: [PATCH 33/49] feat: control mode enums --- libcamera-bridge/control_value.cpp | 51 +++- libcamera-bridge/core.hpp | 9 +- makefile | 2 +- src/bridge.rs | 21 +- src/controls.rs | 430 +++++++++++++++++++++++++---- src/error.rs | 7 +- 6 files changed, 462 insertions(+), 58 deletions(-) diff --git a/libcamera-bridge/control_value.cpp b/libcamera-bridge/control_value.cpp index a778b8e..06ba43e 100644 --- a/libcamera-bridge/control_value.cpp +++ b/libcamera-bridge/control_value.cpp @@ -37,7 +37,7 @@ BindControlValue new_control_value_f32(float value) { return control_value; } -BindControlValue new_control_value_string(rust::String value) { +BindControlValue new_control_value_string(rust::Str value) { BindControlValue control_value{ .inner = std::make_unique( libcamera::ControlValue(static_cast(value))), @@ -45,6 +45,22 @@ BindControlValue new_control_value_string(rust::String value) { return control_value; } +BindControlValue new_control_value_rectangle(ControlRectangle value) { + BindControlValue control_value{ + .inner = std::make_unique(libcamera::ControlValue( + libcamera::Rectangle(value.x, value.y, value.width, value.height))), + }; + return control_value; +} + +BindControlValue new_control_value_size(ControlSize value) { + BindControlValue control_value{ + .inner = std::make_unique( + libcamera::ControlValue(libcamera::Size(value.width, value.height))), + }; + return control_value; +} + const libcamera::ControlValue &ControlValue::get_inner() const { return this->inner; } @@ -84,6 +100,39 @@ float ControlValue::get_f32() const { return this->inner.get(); } +rust::String ControlValue::get_string() const { + if (this->inner.type() != libcamera::ControlType::ControlTypeString) { + throw std::runtime_error("Bad type! Expected String."); + } + return static_cast(this->inner.get()); +} + +ControlRectangle ControlValue::get_rectangle() const { + if (this->inner.type() != libcamera::ControlType::ControlTypeRectangle) { + throw std::runtime_error("Bad type! Expected Rectangle."); + } + auto rect = this->inner.get(); + ControlRectangle crect{ + .x = rect.x, + .y = rect.y, + .width = rect.width, + .height = rect.height, + }; + return crect; +} + +ControlSize ControlValue::get_size() const { + if (this->inner.type() != libcamera::ControlType::ControlTypeSize) { + throw std::runtime_error("Bad type! Expected Size."); + } + auto size = this->inner.get(); + ControlSize csize{ + .width = size.width, + .height = size.height, + }; + return csize; +} + rust::String ControlValue::raw_to_string() const { return this->inner.toString(); } diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 9bc2adf..775093f 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -44,6 +44,8 @@ enum class DefaultPixelFormat; enum class CameraControlType; enum class CameraMessageType; struct CameraMessage; +struct ControlRectangle; +struct ControlSize; using CameraConfigurationStatus = libcamera::CameraConfiguration::Status; @@ -270,7 +272,9 @@ BindControlValue new_control_value_u8(uint8_t value); BindControlValue new_control_value_i32(int32_t value); BindControlValue new_control_value_i64(int64_t value); BindControlValue new_control_value_f32(float value); -BindControlValue new_control_value_string(rust::String value); +BindControlValue new_control_value_string(rust::Str value); +BindControlValue new_control_value_rectangle(ControlRectangle value); +BindControlValue new_control_value_size(ControlSize value); struct ControlValue { private: @@ -285,6 +289,9 @@ struct ControlValue { int32_t get_i32() const; int64_t get_i64() const; float get_f32() const; + rust::String get_string() const; + ControlRectangle get_rectangle() const; + ControlSize get_size() const; [[nodiscard]] rust::String raw_to_string() const; }; diff --git a/makefile b/makefile index acc5060..2d292a9 100644 --- a/makefile +++ b/makefile @@ -13,4 +13,4 @@ format: clang-format -style=file -i libcamera-bridge/* cargo fmt clang-tidy --format-style=file --fix --fix-errors --fix-notes libcamera-bridge/*.cpp -- -I/usr/local/include/libcamera -I./target/cxxbridge -I.. --std=c++17 - cargo clippy --fix + cargo clippy --fix --allow-dirty --allow-staged diff --git a/src/bridge.rs b/src/bridge.rs index 500378f..da01af2 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -207,6 +207,20 @@ pub mod ffi { valid_values: Vec, } + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct ControlRectangle { + x: i32, + y: i32, + width: u32, + height: u32, + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct ControlSize { + width: u32, + height: u32, + } + extern "C++" { include!("libcamera-rs/libcamera-bridge/core.hpp"); @@ -343,13 +357,18 @@ pub mod ffi { pub unsafe fn new_control_value_i32(value: i32) -> BindControlValue; pub unsafe fn new_control_value_i64(value: i64) -> BindControlValue; pub unsafe fn new_control_value_f32(value: f32) -> BindControlValue; - pub unsafe fn new_control_value_string(value: String) -> BindControlValue; + pub unsafe fn new_control_value_string(value: &str) -> BindControlValue; + pub unsafe fn new_control_value_rectangle(value: ControlRectangle) -> BindControlValue; + pub unsafe fn new_control_value_size(value: ControlSize) -> BindControlValue; pub unsafe fn get_bool(self: &ControlValue) -> Result; pub unsafe fn get_u8(self: &ControlValue) -> Result; pub unsafe fn get_i32(self: &ControlValue) -> Result; pub unsafe fn get_i64(self: &ControlValue) -> Result; pub unsafe fn get_f32(self: &ControlValue) -> Result; + pub unsafe fn get_string(self: &ControlValue) -> Result; + pub unsafe fn get_rectangle(self: &ControlValue) -> Result; + pub unsafe fn get_size(self: &ControlValue) -> Result; pub unsafe fn raw_to_string(self: &ControlValue) -> String; } diff --git a/src/controls.rs b/src/controls.rs index 6c2ab83..7d87407 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -1,3 +1,4 @@ +use std::cmp::Ordering; use std::collections::HashMap; use std::fmt::Debug; use std::ops::RangeInclusive; @@ -7,13 +8,37 @@ use crate::{LibcameraError, Result}; /// Contains a value with an acceptable minimum and maximum and a default. #[derive(Debug)] -pub struct MinMaxValue { +pub struct MinMaxValue { range: RangeInclusive, default: T, value: T, } -impl MinMaxValue { +/// Things that can be clamped to a range. +pub trait Clampable { + /// Clamp self to fit inside range. + fn clamp(self, range: &RangeInclusive) -> Self + where + Self: Sized + PartialOrd + Clone, + { + if range.start() > &self { + range.start().clone() + } else if range.end() < &self { + range.end().clone() + } else { + self + } + } +} + +impl Clampable for bool {} +impl Clampable for u8 {} +impl Clampable for i32 {} +impl Clampable for i64 {} +impl Clampable for f32 {} +impl Clampable for String {} + +impl MinMaxValue { /// Creates a new MinMaxValue out of a given min, max, and default /// /// # Returns @@ -22,41 +47,41 @@ impl MinMaxValue { let range = min..=max; if range.contains(&default) { Ok(MinMaxValue { - range: min..=max, + range, + value: default.clone(), default, - value: default, }) } else { - Err(LibcameraError::InvalidControlValue) + Err(LibcameraError::InvalidControlValue(Box::new(default))) } } /// Retrieve the default value - pub fn get_default(&self) -> T { - self.default + pub fn get_default(&self) -> &T { + &self.default } /// Retrieve the minimum value - pub fn min(&self) -> T { - *self.range.start() + pub fn min(&self) -> &T { + self.range.start() } /// Retrieve the maximum value - pub fn max(&self) -> T { - *self.range.end() + pub fn max(&self) -> &T { + self.range.end() } /// Gets the stored value /// /// It is gurenteed to lie within MinMaxValue::min() and MinMaxValue::max(). - pub fn get_value(&self) -> T { - self.value + pub fn get_value(&self) -> &T { + &self.value } /// Gets the stored value if it is not equal to the default stored value. - pub fn get_value_if_changed(&self) -> Option { + pub fn get_value_if_changed(&self) -> Option<&T> { if self.value != self.default { - Some(self.value) + Some(&self.value) } else { None } } - /// Verifies that value lies within the acceptable range for this value + /// Verifies that value lies within the acceptable range for this value, then sets this value. /// /// # Returns /// `true` if the value lies within the acceptable range for this value and was stored, `false` otherwise. @@ -68,6 +93,10 @@ impl MinMaxValue { false } } + /// Set this value to the given value. + pub fn set_value_clamped(&mut self, value: T) { + self.value = value.clamp(&self.range) + } } impl TryFrom<&ffi::ControlPair> for MinMaxValue { @@ -103,6 +132,26 @@ impl TryFrom<&ffi::ControlPair> for MinMaxValue { } } +impl< + T: 'static + + TryFrom + + ControlEnum + + Clampable + + Copy + + Debug + + PartialOrd, + > TryFrom<&ffi::ControlPair> for MinMaxValue +{ + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_i32() }?.try_into()?, + unsafe { pair.max.get().get_i32() }?.try_into()?, + unsafe { pair.value.get().get_i32() }?.try_into()?, + ) + } +} + impl TryFrom<&ffi::ControlPair> for MinMaxValue { type Error = LibcameraError; fn try_from(pair: &ffi::ControlPair) -> Result> { @@ -125,6 +174,155 @@ impl TryFrom<&ffi::ControlPair> for MinMaxValue { } } +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_string() }?, + unsafe { pair.max.get().get_string() }?, + unsafe { pair.value.get().get_string() }?, + ) + } +} + +/// Represents a control value rectangle. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rectangle { + /// The starting x position + x: i32, + /// The starting y position + y: i32, + /// The width + width: u32, + /// The height + height: u32, +} + +impl From for Rectangle { + fn from(v: ffi::ControlRectangle) -> Rectangle { + Rectangle { + x: v.x, + y: v.y, + width: v.width, + height: v.height, + } + } +} + +impl From for ffi::ControlRectangle { + fn from(v: Rectangle) -> ffi::ControlRectangle { + ffi::ControlRectangle { + x: v.x, + y: v.y, + width: v.width, + height: v.height, + } + } +} + +impl PartialOrd for Rectangle { + fn partial_cmp(&self, other: &Rectangle) -> Option { + let x = self.x.cmp(&other.x); + let y = self.y.cmp(&other.y); + let w = self.width.cmp(&other.width); + let h = self.height.cmp(&other.height); + if (x == y && w == h && x == w) || (y == Ordering::Equal && w == y && h == y) { + Some(x) + } else if x == Ordering::Equal && w == x && h == x { + Some(y) + } else if x == Ordering::Equal && y == x && h == x { + Some(w) + } else if x == Ordering::Equal && y == x && w == x { + Some(h) + } else { + None + } + } +} + +impl Clampable for Rectangle { + fn clamp(self, range: &RangeInclusive) -> Self { + Rectangle { + x: Ord::clamp(self.x, range.start().x, range.end().x), + y: Ord::clamp(self.y, range.start().y, range.end().y), + width: Ord::clamp(self.width, range.start().width, range.end().height), + height: Ord::clamp(self.height, range.start().width, range.end().height), + } + } +} + +/// Represents a control value size. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Size { + /// The width. + width: u32, + /// The height. + height: u32, +} + +impl From for Size { + fn from(v: ffi::ControlSize) -> Size { + Size { + width: v.width, + height: v.height, + } + } +} + +impl From for ffi::ControlSize { + fn from(v: Size) -> ffi::ControlSize { + ffi::ControlSize { + width: v.width, + height: v.height, + } + } +} + +impl Clampable for Size { + fn clamp(self, range: &RangeInclusive) -> Self { + Size { + width: Ord::clamp(self.width, range.start().width, range.end().height), + height: Ord::clamp(self.height, range.start().width, range.end().height), + } + } +} + +impl PartialOrd for Size { + fn partial_cmp(&self, other: &Size) -> Option { + let w = self.width.cmp(&other.width); + let h = self.height.cmp(&other.height); + if w == h || h == Ordering::Equal { + Some(w) + } else if w == Ordering::Equal { + Some(h) + } else { + None + } + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_rectangle() }?.into(), + unsafe { pair.max.get().get_rectangle() }?.into(), + unsafe { pair.value.get().get_rectangle() }?.into(), + ) + } +} + +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_size() }?.into(), + unsafe { pair.max.get().get_size() }?.into(), + unsafe { pair.value.get().get_size() }?.into(), + ) + } +} + /// Represents a camera control value with an unknown type /// /// Most of the time you probably want to use `CameraControls` instead. @@ -143,9 +341,131 @@ pub enum CameraControlValue { Integer64(MinMaxValue), /// A control value containing a 32-bit float, e.g. brightness. Float(MinMaxValue), - // String(MinMaxValue), - // Rectangle(MinMaxValue), - // Size(MinMaxValue), + /// A control value containing a String. + String(MinMaxValue), + /// A control value containing a Rectangle + Rectangle(MinMaxValue), + /// A control value containing a Size. + Size(MinMaxValue), +} + +/// Camera auto exposure metering mode +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum AeMeteringMode { + /// Hah brittish spelling + CentreWeighted, + /// Spot + Spot, + /// Oooh fancy sounding + Matrix, + /// ??? + Custom, +} + +/// Camera auto exposure constraint mode +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum AeConstraintMode { + /// Normal + Normal, + /// Highlights??? + Highlight, + /// ??? + Custom, +} + +/// Camera auto exposure mode +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum AeExposureMode { + /// Normal + Normal, + /// Shorter than normal + Short, + /// Longer than normal + Long, +} + +/// Camera auto white balance mode +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum AwbMode { + /// Auto + Auto, + /// Incandescent + Incandescent, + /// Tungsten + Tungsten, + /// Fluorescent + Fluorescent, + /// Indoor + Indoor, + /// Daylight + Daylight, + /// Cloudy + Cloudy, + /// Custom + Custom, +} + +trait ControlEnum {} + +impl ControlEnum for AeMeteringMode {} +impl ControlEnum for AeConstraintMode {} +impl ControlEnum for AeExposureMode {} +impl ControlEnum for AwbMode {} + +impl Clampable for AeMeteringMode {} +impl Clampable for AeConstraintMode {} +impl Clampable for AeExposureMode {} +impl Clampable for AwbMode {} + +impl TryFrom for AeMeteringMode { + type Error = LibcameraError; + fn try_from(i: i32) -> Result { + match i { + 0 => Ok(Self::CentreWeighted), + 1 => Ok(Self::Spot), + 2 => Ok(Self::Matrix), + 3 => Ok(Self::Custom), + i => Err(LibcameraError::InvalidControlValue(Box::new(i))), + } + } +} +impl TryFrom for AeConstraintMode { + type Error = LibcameraError; + fn try_from(i: i32) -> Result { + match i { + 0 => Ok(Self::Normal), + 1 => Ok(Self::Highlight), + 2 => Ok(Self::Custom), + i => Err(LibcameraError::InvalidControlValue(Box::new(i))), + } + } +} +impl TryFrom for AeExposureMode { + type Error = LibcameraError; + fn try_from(i: i32) -> Result { + match i { + 0 => Ok(Self::Normal), + 1 => Ok(Self::Short), + 2 => Ok(Self::Long), + i => Err(LibcameraError::InvalidControlValue(Box::new(i))), + } + } +} +impl TryFrom for AwbMode { + type Error = LibcameraError; + fn try_from(i: i32) -> Result { + match i { + 0 => Ok(Self::Auto), + 1 => Ok(Self::Incandescent), + 2 => Ok(Self::Tungsten), + 3 => Ok(Self::Fluorescent), + 4 => Ok(Self::Indoor), + 5 => Ok(Self::Daylight), + 6 => Ok(Self::Cloudy), + 7 => Ok(Self::Custom), + i => Err(LibcameraError::InvalidControlValue(Box::new(i))), + } + } } /// Stores camera controls. @@ -158,13 +478,13 @@ pub struct CameraControls { pub ae_enable: Option>, /// Autoexposure metering mode. /// **TODO**: This should be an enum. - pub ae_metering_mode: Option>, + pub ae_metering_mode: Option>, /// Autoexposure constraint mode. /// **TODO**: This should be an enum. - pub ae_constraint_mode: Option>, + pub ae_constraint_mode: Option>, /// Autoexposure mode. /// **TODO**: This should be an enum. - pub ae_exposure_mode: Option>, + pub ae_exposure_mode: Option>, /// Exposure "value". pub exposure_value: Option>, /// Exposure time. @@ -179,7 +499,7 @@ pub struct CameraControls { pub awb_enable: Option>, /// Auto white balance mode. /// **TODO**: This should be an enum. - pub awb_mode: Option>, + pub awb_mode: Option>, /// Colour gains. pub colour_gains: Option>, /// Saturation. @@ -310,87 +630,87 @@ impl CameraControls { pub(crate) fn get_libcamera(&self) -> Vec<(u32, ffi::BindControlValue)> { let mut controls = Vec::new(); if let Some(ae_enable) = &self.ae_enable { - if let Some(value) = ae_enable.get_value_if_changed() { + if let Some(&value) = ae_enable.get_value_if_changed() { controls.push((1, unsafe { ffi::new_control_value_bool(value) })); } } if let Some(ae_metering_mode) = &self.ae_metering_mode { - if let Some(value) = ae_metering_mode.get_value_if_changed() { - controls.push((3, unsafe { ffi::new_control_value_i32(value) })); + if let Some(&value) = ae_metering_mode.get_value_if_changed() { + controls.push((3, unsafe { ffi::new_control_value_i32(value as i32) })); } } if let Some(ae_constraint_mode) = &self.ae_constraint_mode { - if let Some(value) = ae_constraint_mode.get_value_if_changed() { - controls.push((4, unsafe { ffi::new_control_value_i32(value) })); + if let Some(&value) = ae_constraint_mode.get_value_if_changed() { + controls.push((4, unsafe { ffi::new_control_value_i32(value as i32) })); } } if let Some(ae_exposure_mode) = &self.ae_exposure_mode { - if let Some(value) = ae_exposure_mode.get_value_if_changed() { - controls.push((5, unsafe { ffi::new_control_value_i32(value) })); + if let Some(&value) = ae_exposure_mode.get_value_if_changed() { + controls.push((5, unsafe { ffi::new_control_value_i32(value as i32) })); } } if let Some(exposure_value) = &self.exposure_value { - if let Some(value) = exposure_value.get_value_if_changed() { + if let Some(&value) = exposure_value.get_value_if_changed() { controls.push((6, unsafe { ffi::new_control_value_f32(value) })); } } if let Some(exposure_time) = &self.exposure_time { - if let Some(value) = exposure_time.get_value_if_changed() { + if let Some(&value) = exposure_time.get_value_if_changed() { controls.push((7, unsafe { ffi::new_control_value_i32(value) })); } } if let Some(analogue_gain) = &self.analogue_gain { - if let Some(value) = analogue_gain.get_value_if_changed() { + if let Some(&value) = analogue_gain.get_value_if_changed() { controls.push((8, unsafe { ffi::new_control_value_f32(value) })); } } if let Some(brightness) = &self.brightness { - if let Some(value) = brightness.get_value_if_changed() { + if let Some(&value) = brightness.get_value_if_changed() { controls.push((9, unsafe { ffi::new_control_value_f32(value) })); } } if let Some(contrast) = &self.contrast { - if let Some(value) = contrast.get_value_if_changed() { + if let Some(&value) = contrast.get_value_if_changed() { controls.push((10, unsafe { ffi::new_control_value_f32(value) })); } } if let Some(awb_enable) = &self.awb_enable { - if let Some(value) = awb_enable.get_value_if_changed() { + if let Some(&value) = awb_enable.get_value_if_changed() { controls.push((12, unsafe { ffi::new_control_value_bool(value) })); } } if let Some(awb_mode) = &self.awb_mode { - if let Some(value) = awb_mode.get_value_if_changed() { - controls.push((13, unsafe { ffi::new_control_value_i32(value) })); + if let Some(&value) = awb_mode.get_value_if_changed() { + controls.push((13, unsafe { ffi::new_control_value_i32(value as i32) })); } } if let Some(colour_gains) = &self.colour_gains { - if let Some(value) = colour_gains.get_value_if_changed() { + if let Some(&value) = colour_gains.get_value_if_changed() { controls.push((15, unsafe { ffi::new_control_value_f32(value) })); } } if let Some(saturation) = &self.saturation { - if let Some(value) = saturation.get_value_if_changed() { + if let Some(&value) = saturation.get_value_if_changed() { controls.push((17, unsafe { ffi::new_control_value_f32(value) })); } } if let Some(sharpness) = &self.sharpness { - if let Some(value) = sharpness.get_value_if_changed() { + if let Some(&value) = sharpness.get_value_if_changed() { controls.push((19, unsafe { ffi::new_control_value_f32(value) })); } } if let Some(colour_correction_matrix) = &self.colour_correction_matrix { - if let Some(value) = colour_correction_matrix.get_value_if_changed() { + if let Some(&value) = colour_correction_matrix.get_value_if_changed() { controls.push((21, unsafe { ffi::new_control_value_f32(value) })); } } if let Some(frame_duration_limits) = &self.frame_duration_limits { - if let Some(value) = frame_duration_limits.get_value_if_changed() { + if let Some(&value) = frame_duration_limits.get_value_if_changed() { controls.push((25, unsafe { ffi::new_control_value_i64(value) })); } } if let Some(noise_reduction_mode) = &self.noise_reduction_mode { - if let Some(value) = noise_reduction_mode.get_value_if_changed() { + if let Some(&value) = noise_reduction_mode.get_value_if_changed() { controls.push((39, unsafe { ffi::new_control_value_i32(value) })); } } @@ -398,23 +718,29 @@ impl CameraControls { if let Some(value) = match value { CameraControlValue::None => None, CameraControlValue::Bool(value) => { - Some(unsafe { ffi::new_control_value_bool(value.get_value()) }) + Some(unsafe { ffi::new_control_value_bool(*value.get_value()) }) } CameraControlValue::Byte(value) => { - Some(unsafe { ffi::new_control_value_u8(value.get_value()) }) + Some(unsafe { ffi::new_control_value_u8(*value.get_value()) }) } CameraControlValue::Integer32(value) => { - Some(unsafe { ffi::new_control_value_i32(value.get_value()) }) + Some(unsafe { ffi::new_control_value_i32(*value.get_value()) }) } CameraControlValue::Integer64(value) => { - Some(unsafe { ffi::new_control_value_i64(value.get_value()) }) + Some(unsafe { ffi::new_control_value_i64(*value.get_value()) }) } CameraControlValue::Float(value) => { - Some(unsafe { ffi::new_control_value_f32(value.get_value()) }) + Some(unsafe { ffi::new_control_value_f32(*value.get_value()) }) + } + CameraControlValue::String(value) => { + Some(unsafe { ffi::new_control_value_string(value.get_value()) }) + } + CameraControlValue::Rectangle(value) => Some(unsafe { + ffi::new_control_value_rectangle(ffi::ControlRectangle::from(*value.get_value())) + }), + CameraControlValue::Size(value) => { + Some(unsafe { ffi::new_control_value_size(ffi::ControlSize::from(*value.get_value())) }) } - // CameraControlValue::String(value) => Some(unsafe { ffi::new_control_value_string(value.get_value()) }), - // CameraControlValue::Rectangle(value) => Some(unsafe { ffi::new_control_value_rectangle(value.get_value()) }), - // CameraControlValue::Size(value) => Some(unsafe { ffi::new_control_value_size(value.get_value()) }), } { controls.push((*id, value)); } diff --git a/src/error.rs b/src/error.rs index 303066e..1d11432 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,8 +16,11 @@ pub enum LibcameraError { #[error("No buffer ready for capture (all buffers in use, capture pictures slower!)")] NoBufferReady, /// An error emitted when a control value is attempted to be set to a value outside of the acceptable range. - #[error("Control value out of range!")] - InvalidControlValue, + #[error("Control value ({0:?}) out of range!")] + InvalidControlValue(Box), + /// An error reading a control value + #[error("Unknown control value!")] + ControlValueError, /// An error en/decoding an image. #[cfg(feature = "image")] #[error("Image error: {0}")] From eae27841a06d17d31a4995eb0f1a674219a842eb Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 20 Jul 2022 13:51:47 -0600 Subject: [PATCH 34/49] feat: fix colour gains control --- libcamera-bridge/control_value.cpp | 74 ++++++-- libcamera-bridge/core.hpp | 7 + libcamera-bridge/request.cpp | 3 +- src/bridge.rs | 6 + src/controls.rs | 283 ++++++++++++++++------------- src/error.rs | 2 +- 6 files changed, 233 insertions(+), 142 deletions(-) diff --git a/libcamera-bridge/control_value.cpp b/libcamera-bridge/control_value.cpp index 06ba43e..0374f4f 100644 --- a/libcamera-bridge/control_value.cpp +++ b/libcamera-bridge/control_value.cpp @@ -37,6 +37,19 @@ BindControlValue new_control_value_f32(float value) { return control_value; } +BindControlValue +new_control_value_f32_array(rust::Slice values_rust) { + std::vector values; + for (float value : values_rust) { + values.push_back(value); + } + libcamera::Span span{values}; + BindControlValue control_value{ + .inner = std::make_unique(libcamera::ControlValue(span)), + }; + return control_value; +} + BindControlValue new_control_value_string(rust::Str value) { BindControlValue control_value{ .inner = std::make_unique( @@ -65,51 +78,79 @@ const libcamera::ControlValue &ControlValue::get_inner() const { return this->inner; } +CameraControlType ControlValue::get_type() const { + return static_cast(this->inner.type()); +} + +bool ControlValue::is_array() const { return this->inner.isArray(); } + +size_t ControlValue::len() const { return this->inner.numElements(); } + bool ControlValue::get_bool() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeBool) { - throw std::runtime_error("Bad type! Expected Bool."); + if (this->inner.type() != libcamera::ControlType::ControlTypeBool && + !this->inner.isArray()) { + throw std::runtime_error("Bad type! Expected Single Bool."); } return this->inner.get(); } uint8_t ControlValue::get_u8() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeByte) { - throw std::runtime_error("Bad type! Expected Byte."); + if (this->inner.type() != libcamera::ControlType::ControlTypeByte && + !this->inner.isArray()) { + throw std::runtime_error("Bad type! Expected Single Byte."); } return this->inner.get(); } int32_t ControlValue::get_i32() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeInteger32) { - throw std::runtime_error("Bad type! Expected I32."); + if (this->inner.type() != libcamera::ControlType::ControlTypeInteger32 && + !this->inner.isArray()) { + throw std::runtime_error("Bad type! Expected Single I32."); } return this->inner.get(); } int64_t ControlValue::get_i64() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeInteger64) { - throw std::runtime_error("Bad type! Expected I64."); + if (this->inner.type() != libcamera::ControlType::ControlTypeInteger64 && + !this->inner.isArray()) { + throw std::runtime_error("Bad type! Expected Single I64."); } return this->inner.get(); } float ControlValue::get_f32() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeFloat) { - throw std::runtime_error("Bad type! Expected Float."); + if (this->inner.type() != libcamera::ControlType::ControlTypeFloat && + !this->inner.isArray()) { + throw std::runtime_error("Bad type! Expected Single Float."); } return this->inner.get(); } +rust::Vec ControlValue::get_f32_array() const { + if (this->inner.type() != libcamera::ControlType::ControlTypeFloat && + this->inner.isArray()) { + throw std::runtime_error("Bad type! Expected Float Array."); + } + auto span = this->inner.get>(); + rust::Vec values; + for (float f : span) { + values.push_back(f); + } + return values; +} + rust::String ControlValue::get_string() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeString) { - throw std::runtime_error("Bad type! Expected String."); + if (this->inner.type() != libcamera::ControlType::ControlTypeString && + !this->inner.isArray()) { + throw std::runtime_error("Bad type! Expected Single String."); } return static_cast(this->inner.get()); } ControlRectangle ControlValue::get_rectangle() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeRectangle) { - throw std::runtime_error("Bad type! Expected Rectangle."); + if (this->inner.type() != libcamera::ControlType::ControlTypeRectangle && + !this->inner.isArray()) { + throw std::runtime_error("Bad type! Expected Single Rectangle."); } auto rect = this->inner.get(); ControlRectangle crect{ @@ -122,8 +163,9 @@ ControlRectangle ControlValue::get_rectangle() const { } ControlSize ControlValue::get_size() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeSize) { - throw std::runtime_error("Bad type! Expected Size."); + if (this->inner.type() != libcamera::ControlType::ControlTypeSize && + !this->inner.isArray()) { + throw std::runtime_error("Bad type! Expected Single Size."); } auto size = this->inner.get(); ControlSize csize{ diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 775093f..8732b66 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -272,6 +272,8 @@ BindControlValue new_control_value_u8(uint8_t value); BindControlValue new_control_value_i32(int32_t value); BindControlValue new_control_value_i64(int64_t value); BindControlValue new_control_value_f32(float value); +BindControlValue +new_control_value_f32_array(rust::Slice values_rust); BindControlValue new_control_value_string(rust::Str value); BindControlValue new_control_value_rectangle(ControlRectangle value); BindControlValue new_control_value_size(ControlSize value); @@ -284,11 +286,16 @@ struct ControlValue { explicit ControlValue(libcamera::ControlValue inner_) : inner{inner_} {} const libcamera::ControlValue &get_inner() const; + CameraControlType get_type() const; + bool is_array() const; + size_t len() const; + bool get_bool() const; uint8_t get_u8() const; int32_t get_i32() const; int64_t get_i64() const; float get_f32() const; + rust::Vec get_f32_array() const; rust::String get_string() const; ControlRectangle get_rectangle() const; ControlSize get_size() const; diff --git a/libcamera-bridge/request.cpp b/libcamera-bridge/request.cpp index 3f61253..b460a3d 100644 --- a/libcamera-bridge/request.cpp +++ b/libcamera-bridge/request.cpp @@ -25,7 +25,8 @@ BindControlValue Request::get_control(uint32_t id) const { libcamera::ControlList &controls = this->inner->controls(); if (!controls.contains(id)) { - throw std::runtime_error("No control with specified id."); + throw std::runtime_error( + "No control has been set in this request with the specified id."); } BindControlValue control_value{ .inner = std::make_unique(controls.get(id)), diff --git a/src/bridge.rs b/src/bridge.rs index da01af2..60f8758 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -357,15 +357,21 @@ pub mod ffi { pub unsafe fn new_control_value_i32(value: i32) -> BindControlValue; pub unsafe fn new_control_value_i64(value: i64) -> BindControlValue; pub unsafe fn new_control_value_f32(value: f32) -> BindControlValue; + pub unsafe fn new_control_value_f32_array(value: &[f32]) -> BindControlValue; pub unsafe fn new_control_value_string(value: &str) -> BindControlValue; pub unsafe fn new_control_value_rectangle(value: ControlRectangle) -> BindControlValue; pub unsafe fn new_control_value_size(value: ControlSize) -> BindControlValue; + pub unsafe fn get_type(self: &ControlValue) -> CameraControlType; + pub unsafe fn is_array(self: &ControlValue) -> bool; + pub unsafe fn len(self: &ControlValue) -> usize; + pub unsafe fn get_bool(self: &ControlValue) -> Result; pub unsafe fn get_u8(self: &ControlValue) -> Result; pub unsafe fn get_i32(self: &ControlValue) -> Result; pub unsafe fn get_i64(self: &ControlValue) -> Result; pub unsafe fn get_f32(self: &ControlValue) -> Result; + pub unsafe fn get_f32_array(self: &ControlValue) -> Result>; pub unsafe fn get_string(self: &ControlValue) -> Result; pub unsafe fn get_rectangle(self: &ControlValue) -> Result; pub unsafe fn get_size(self: &ControlValue) -> Result; diff --git a/src/controls.rs b/src/controls.rs index 7d87407..2a8e0b9 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -14,6 +14,10 @@ pub struct MinMaxValue { value: T, } +/// A pair of two float values. +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub struct FloatPair(pub f32, pub f32); + /// Things that can be clamped to a range. pub trait Clampable { /// Clamp self to fit inside range. @@ -38,6 +42,131 @@ impl Clampable for i64 {} impl Clampable for f32 {} impl Clampable for String {} +impl Clampable for FloatPair { + fn clamp(self, range: &RangeInclusive) -> Self { + FloatPair( + self.0.clamp(range.start().0, range.end().0), + self.1.clamp(range.start().1, range.end().1), + ) + } +} + +/// Represents a control value rectangle. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rectangle { + /// The starting x position + x: i32, + /// The starting y position + y: i32, + /// The width + width: u32, + /// The height + height: u32, +} + +impl From for Rectangle { + fn from(v: ffi::ControlRectangle) -> Rectangle { + Rectangle { + x: v.x, + y: v.y, + width: v.width, + height: v.height, + } + } +} + +impl From for ffi::ControlRectangle { + fn from(v: Rectangle) -> ffi::ControlRectangle { + ffi::ControlRectangle { + x: v.x, + y: v.y, + width: v.width, + height: v.height, + } + } +} + +impl PartialOrd for Rectangle { + fn partial_cmp(&self, other: &Rectangle) -> Option { + let x = self.x.cmp(&other.x); + let y = self.y.cmp(&other.y); + let w = self.width.cmp(&other.width); + let h = self.height.cmp(&other.height); + if (x == y && w == h && x == w) || (y == Ordering::Equal && w == y && h == y) { + Some(x) + } else if x == Ordering::Equal && w == x && h == x { + Some(y) + } else if x == Ordering::Equal && y == x && h == x { + Some(w) + } else if x == Ordering::Equal && y == x && w == x { + Some(h) + } else { + None + } + } +} + +impl Clampable for Rectangle { + fn clamp(self, range: &RangeInclusive) -> Self { + Rectangle { + x: Ord::clamp(self.x, range.start().x, range.end().x), + y: Ord::clamp(self.y, range.start().y, range.end().y), + width: Ord::clamp(self.width, range.start().width, range.end().height), + height: Ord::clamp(self.height, range.start().width, range.end().height), + } + } +} + +/// Represents a control value size. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Size { + /// The width. + width: u32, + /// The height. + height: u32, +} + +impl From for Size { + fn from(v: ffi::ControlSize) -> Size { + Size { + width: v.width, + height: v.height, + } + } +} + +impl From for ffi::ControlSize { + fn from(v: Size) -> ffi::ControlSize { + ffi::ControlSize { + width: v.width, + height: v.height, + } + } +} + +impl Clampable for Size { + fn clamp(self, range: &RangeInclusive) -> Self { + Size { + width: Ord::clamp(self.width, range.start().width, range.end().height), + height: Ord::clamp(self.height, range.start().width, range.end().height), + } + } +} + +impl PartialOrd for Size { + fn partial_cmp(&self, other: &Size) -> Option { + let w = self.width.cmp(&other.width); + let h = self.height.cmp(&other.height); + if w == h || h == Ordering::Equal { + Some(w) + } else if w == Ordering::Equal { + Some(h) + } else { + None + } + } +} + impl MinMaxValue { /// Creates a new MinMaxValue out of a given min, max, and default /// @@ -174,130 +303,36 @@ impl TryFrom<&ffi::ControlPair> for MinMaxValue { } } -impl TryFrom<&ffi::ControlPair> for MinMaxValue { +impl TryFrom> for FloatPair { type Error = LibcameraError; - fn try_from(pair: &ffi::ControlPair) -> Result> { - MinMaxValue::new( - unsafe { pair.min.get().get_string() }?, - unsafe { pair.max.get().get_string() }?, - unsafe { pair.value.get().get_string() }?, - ) - } -} - -/// Represents a control value rectangle. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Rectangle { - /// The starting x position - x: i32, - /// The starting y position - y: i32, - /// The width - width: u32, - /// The height - height: u32, -} - -impl From for Rectangle { - fn from(v: ffi::ControlRectangle) -> Rectangle { - Rectangle { - x: v.x, - y: v.y, - width: v.width, - height: v.height, - } - } -} - -impl From for ffi::ControlRectangle { - fn from(v: Rectangle) -> ffi::ControlRectangle { - ffi::ControlRectangle { - x: v.x, - y: v.y, - width: v.width, - height: v.height, - } - } -} - -impl PartialOrd for Rectangle { - fn partial_cmp(&self, other: &Rectangle) -> Option { - let x = self.x.cmp(&other.x); - let y = self.y.cmp(&other.y); - let w = self.width.cmp(&other.width); - let h = self.height.cmp(&other.height); - if (x == y && w == h && x == w) || (y == Ordering::Equal && w == y && h == y) { - Some(x) - } else if x == Ordering::Equal && w == x && h == x { - Some(y) - } else if x == Ordering::Equal && y == x && h == x { - Some(w) - } else if x == Ordering::Equal && y == x && w == x { - Some(h) + fn try_from(arr: Vec) -> Result { + if let &[a1, a2] = &arr[..] { + Ok(FloatPair(a1, a2)) } else { - None - } - } -} - -impl Clampable for Rectangle { - fn clamp(self, range: &RangeInclusive) -> Self { - Rectangle { - x: Ord::clamp(self.x, range.start().x, range.end().x), - y: Ord::clamp(self.y, range.start().y, range.end().y), - width: Ord::clamp(self.width, range.start().width, range.end().height), - height: Ord::clamp(self.height, range.start().width, range.end().height), - } - } -} - -/// Represents a control value size. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Size { - /// The width. - width: u32, - /// The height. - height: u32, -} - -impl From for Size { - fn from(v: ffi::ControlSize) -> Size { - Size { - width: v.width, - height: v.height, - } - } -} - -impl From for ffi::ControlSize { - fn from(v: Size) -> ffi::ControlSize { - ffi::ControlSize { - width: v.width, - height: v.height, + Err(LibcameraError::ControlValueError) } } } -impl Clampable for Size { - fn clamp(self, range: &RangeInclusive) -> Self { - Size { - width: Ord::clamp(self.width, range.start().width, range.end().height), - height: Ord::clamp(self.height, range.start().width, range.end().height), - } +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_f32_array() }?.try_into()?, + unsafe { pair.max.get().get_f32_array() }?.try_into()?, + unsafe { pair.value.get().get_f32_array() }?.try_into()?, + ) } } -impl PartialOrd for Size { - fn partial_cmp(&self, other: &Size) -> Option { - let w = self.width.cmp(&other.width); - let h = self.height.cmp(&other.height); - if w == h || h == Ordering::Equal { - Some(w) - } else if w == Ordering::Equal { - Some(h) - } else { - None - } +impl TryFrom<&ffi::ControlPair> for MinMaxValue { + type Error = LibcameraError; + fn try_from(pair: &ffi::ControlPair) -> Result> { + MinMaxValue::new( + unsafe { pair.min.get().get_string() }?, + unsafe { pair.max.get().get_string() }?, + unsafe { pair.value.get().get_string() }?, + ) } } @@ -477,13 +512,10 @@ pub struct CameraControls { /// Autoexposure enable. pub ae_enable: Option>, /// Autoexposure metering mode. - /// **TODO**: This should be an enum. pub ae_metering_mode: Option>, /// Autoexposure constraint mode. - /// **TODO**: This should be an enum. pub ae_constraint_mode: Option>, /// Autoexposure mode. - /// **TODO**: This should be an enum. pub ae_exposure_mode: Option>, /// Exposure "value". pub exposure_value: Option>, @@ -498,17 +530,18 @@ pub struct CameraControls { /// Auto white balance enable. pub awb_enable: Option>, /// Auto white balance mode. - /// **TODO**: This should be an enum. pub awb_mode: Option>, - /// Colour gains. - pub colour_gains: Option>, + /// Red/Blue colour gains. + pub colour_gains: Option>, /// Saturation. pub saturation: Option>, /// Sharpness. pub sharpness: Option>, - /// Colour correction "matrix". + /// Colour correction matrix. + /// **TODO**: Make this actually a 3x3 matrix pub colour_correction_matrix: Option>, - // pub scaler_crop: Option>, // Rectangle TODO + /// Scaler crop + pub scaler_crop: Option>, // Rectangle TODO /// Frame duration limit. pub frame_duration_limits: Option>, /// Noise reduction mode. @@ -686,7 +719,9 @@ impl CameraControls { } if let Some(colour_gains) = &self.colour_gains { if let Some(&value) = colour_gains.get_value_if_changed() { - controls.push((15, unsafe { ffi::new_control_value_f32(value) })); + controls.push((15, unsafe { + ffi::new_control_value_f32_array(&[value.0, value.1]) + })); } } if let Some(saturation) = &self.saturation { diff --git a/src/error.rs b/src/error.rs index 1d11432..1165dce 100644 --- a/src/error.rs +++ b/src/error.rs @@ -19,7 +19,7 @@ pub enum LibcameraError { #[error("Control value ({0:?}) out of range!")] InvalidControlValue(Box), /// An error reading a control value - #[error("Unknown control value!")] + #[error("Error converting control value!")] ControlValueError, /// An error en/decoding an image. #[cfg(feature = "image")] From 799f981b59330b7bf645c170094b0d8e7e6951d1 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 20 Jul 2022 14:01:12 -0600 Subject: [PATCH 35/49] fix: fix array controls --- libcamera-bridge/control_value.cpp | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/libcamera-bridge/control_value.cpp b/libcamera-bridge/control_value.cpp index 0374f4f..7e13f58 100644 --- a/libcamera-bridge/control_value.cpp +++ b/libcamera-bridge/control_value.cpp @@ -87,48 +87,48 @@ bool ControlValue::is_array() const { return this->inner.isArray(); } size_t ControlValue::len() const { return this->inner.numElements(); } bool ControlValue::get_bool() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeBool && - !this->inner.isArray()) { + if (this->inner.type() != libcamera::ControlType::ControlTypeBool || + this->inner.isArray()) { throw std::runtime_error("Bad type! Expected Single Bool."); } return this->inner.get(); } uint8_t ControlValue::get_u8() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeByte && - !this->inner.isArray()) { + if (this->inner.type() != libcamera::ControlType::ControlTypeByte || + this->inner.isArray()) { throw std::runtime_error("Bad type! Expected Single Byte."); } return this->inner.get(); } int32_t ControlValue::get_i32() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeInteger32 && - !this->inner.isArray()) { + if (this->inner.type() != libcamera::ControlType::ControlTypeInteger32 || + this->inner.isArray()) { throw std::runtime_error("Bad type! Expected Single I32."); } return this->inner.get(); } int64_t ControlValue::get_i64() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeInteger64 && - !this->inner.isArray()) { + if (this->inner.type() != libcamera::ControlType::ControlTypeInteger64 || + this->inner.isArray()) { throw std::runtime_error("Bad type! Expected Single I64."); } return this->inner.get(); } float ControlValue::get_f32() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeFloat && - !this->inner.isArray()) { + if (this->inner.type() != libcamera::ControlType::ControlTypeFloat || + this->inner.isArray()) { throw std::runtime_error("Bad type! Expected Single Float."); } return this->inner.get(); } rust::Vec ControlValue::get_f32_array() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeFloat && - this->inner.isArray()) { + if (this->inner.type() != libcamera::ControlType::ControlTypeFloat || + !this->inner.isArray()) { throw std::runtime_error("Bad type! Expected Float Array."); } auto span = this->inner.get>(); @@ -140,16 +140,16 @@ rust::Vec ControlValue::get_f32_array() const { } rust::String ControlValue::get_string() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeString && - !this->inner.isArray()) { + if (this->inner.type() != libcamera::ControlType::ControlTypeString || + this->inner.isArray()) { throw std::runtime_error("Bad type! Expected Single String."); } return static_cast(this->inner.get()); } ControlRectangle ControlValue::get_rectangle() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeRectangle && - !this->inner.isArray()) { + if (this->inner.type() != libcamera::ControlType::ControlTypeRectangle || + this->inner.isArray()) { throw std::runtime_error("Bad type! Expected Single Rectangle."); } auto rect = this->inner.get(); @@ -163,8 +163,8 @@ ControlRectangle ControlValue::get_rectangle() const { } ControlSize ControlValue::get_size() const { - if (this->inner.type() != libcamera::ControlType::ControlTypeSize && - !this->inner.isArray()) { + if (this->inner.type() != libcamera::ControlType::ControlTypeSize || + this->inner.isArray()) { throw std::runtime_error("Bad type! Expected Single Size."); } auto size = this->inner.get(); From 140d98d4086d3ce5e8b4ce463fd2db090fa4e350 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 20 Jul 2022 14:48:20 -0600 Subject: [PATCH 36/49] fix: fix weird controls with the wrong type --- src/controls.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/src/controls.rs b/src/controls.rs index 2a8e0b9..4397962 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -228,6 +228,41 @@ impl MinMaxValue } } +trait FromMinMaxInt { + fn from_int(from: i32) -> Self; +} + +impl From> for MinMaxValue { + fn from(input: MinMaxValue) -> MinMaxValue { + MinMaxValue { + range: O::from_int(*input.range.start())..=O::from_int(*input.range.end()), + value: O::from_int(input.value), + default: O::from_int(input.default), + } + } +} + +impl FromMinMaxInt for bool { + fn from_int(from: i32) -> Self { + from != 0 + } +} +impl FromMinMaxInt for u8 { + fn from_int(from: i32) -> Self { + from as u8 + } +} +impl FromMinMaxInt for i64 { + fn from_int(from: i32) -> Self { + from as i64 + } +} +impl FromMinMaxInt for f32 { + fn from_int(from: i32) -> Self { + from as f32 + } +} + impl TryFrom<&ffi::ControlPair> for MinMaxValue { type Error = LibcameraError; fn try_from(pair: &ffi::ControlPair) -> Result> { @@ -632,19 +667,30 @@ impl CameraControls { let control_type = unsafe { control.id.get().get_type() }; if let Some(control_value) = match control_type { ffi::CameraControlType::None => Some(CameraControlValue::None), - ffi::CameraControlType::Bool => (&control).try_into().ok().map(CameraControlValue::Bool), - ffi::CameraControlType::Byte => (&control).try_into().ok().map(CameraControlValue::Byte), + ffi::CameraControlType::Bool => (&control) + .try_into() + .or_else(|_| MinMaxValue::::try_from(&control).map(|v| v.into())) + .ok() + .map(CameraControlValue::Bool), + ffi::CameraControlType::Byte => (&control) + .try_into() + .or_else(|_| MinMaxValue::::try_from(&control).map(|v| v.into())) + .ok() + .map(CameraControlValue::Byte), ffi::CameraControlType::Integer32 => (&control) .try_into() .ok() .map(CameraControlValue::Integer32), ffi::CameraControlType::Integer64 => (&control) .try_into() + .or_else(|_| MinMaxValue::::try_from(&control).map(|v| v.into())) .ok() .map(CameraControlValue::Integer64), - ffi::CameraControlType::Float => { - (&control).try_into().ok().map(CameraControlValue::Float) - } + ffi::CameraControlType::Float => (&control) + .try_into() + .or_else(|_| MinMaxValue::::try_from(&control).map(|v| v.into())) + .ok() + .map(CameraControlValue::Float), _ => None, // ffi::CameraControlType::String => (&control).try_into().ok().map(|control| CameraControlValue::String(control)), // ffi::CameraControlType::Rectangle => (&control).try_into().ok().map(|control| CameraControlValue::Rectangle(control)), From cef10a06fe3f4bdd419087c11bf5c4680906fa46 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 20 Jul 2022 15:57:25 -0600 Subject: [PATCH 37/49] fix: more control value fixes --- libcamera-bridge/control_value.cpp | 12 +++ src/controls.rs | 160 ++++++++++++++--------------- 2 files changed, 89 insertions(+), 83 deletions(-) diff --git a/libcamera-bridge/control_value.cpp b/libcamera-bridge/control_value.cpp index 7e13f58..1a92b21 100644 --- a/libcamera-bridge/control_value.cpp +++ b/libcamera-bridge/control_value.cpp @@ -97,6 +97,10 @@ bool ControlValue::get_bool() const { uint8_t ControlValue::get_u8() const { if (this->inner.type() != libcamera::ControlType::ControlTypeByte || this->inner.isArray()) { + if (this->inner.type() == libcamera::ControlType::ControlTypeInteger32 && + !this->inner.isArray()) { + return static_cast(this->get_i32()); + } throw std::runtime_error("Bad type! Expected Single Byte."); } return this->inner.get(); @@ -113,6 +117,10 @@ int32_t ControlValue::get_i32() const { int64_t ControlValue::get_i64() const { if (this->inner.type() != libcamera::ControlType::ControlTypeInteger64 || this->inner.isArray()) { + if (this->inner.type() == libcamera::ControlType::ControlTypeInteger32 && + !this->inner.isArray()) { + return static_cast(this->get_i32()); + } throw std::runtime_error("Bad type! Expected Single I64."); } return this->inner.get(); @@ -121,6 +129,10 @@ int64_t ControlValue::get_i64() const { float ControlValue::get_f32() const { if (this->inner.type() != libcamera::ControlType::ControlTypeFloat || this->inner.isArray()) { + if (this->inner.type() == libcamera::ControlType::ControlTypeInteger32 && + !this->inner.isArray()) { + return static_cast(this->get_i32()); + } throw std::runtime_error("Bad type! Expected Single Float."); } return this->inner.get(); diff --git a/src/controls.rs b/src/controls.rs index 4397962..c286f90 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -9,7 +9,7 @@ use crate::{LibcameraError, Result}; /// Contains a value with an acceptable minimum and maximum and a default. #[derive(Debug)] pub struct MinMaxValue { - range: RangeInclusive, + range: Option>, default: T, value: T, } @@ -173,10 +173,17 @@ impl MinMaxValue /// # Returns /// Returns None if default is not within min and max. pub fn new(min: T, max: T, default: T) -> Result> { + if min >= max { + return Ok(MinMaxValue { + range: None, + value: default.clone(), + default, + }); + } let range = min..=max; if range.contains(&default) { Ok(MinMaxValue { - range, + range: Some(range), value: default.clone(), default, }) @@ -189,12 +196,12 @@ impl MinMaxValue &self.default } /// Retrieve the minimum value - pub fn min(&self) -> &T { - self.range.start() + pub fn min(&self) -> Option<&T> { + self.range.as_ref().map(|r| r.start()) } /// Retrieve the maximum value - pub fn max(&self) -> &T { - self.range.end() + pub fn max(&self) -> Option<&T> { + self.range.as_ref().map(|r| r.end()) } /// Gets the stored value /// @@ -215,54 +222,28 @@ impl MinMaxValue /// # Returns /// `true` if the value lies within the acceptable range for this value and was stored, `false` otherwise. pub fn set_value(&mut self, value: T) -> bool { - if self.range.contains(&value) { + if let Some(range) = &self.range { + if range.contains(&value) { + self.value = value; + true + } else { + false + } + } else { self.value = value; true - } else { - false } } /// Set this value to the given value. pub fn set_value_clamped(&mut self, value: T) { - self.value = value.clamp(&self.range) - } -} - -trait FromMinMaxInt { - fn from_int(from: i32) -> Self; -} - -impl From> for MinMaxValue { - fn from(input: MinMaxValue) -> MinMaxValue { - MinMaxValue { - range: O::from_int(*input.range.start())..=O::from_int(*input.range.end()), - value: O::from_int(input.value), - default: O::from_int(input.default), + if let Some(range) = &self.range { + self.value = value.clamp(range) + } else { + self.value = value; } } } -impl FromMinMaxInt for bool { - fn from_int(from: i32) -> Self { - from != 0 - } -} -impl FromMinMaxInt for u8 { - fn from_int(from: i32) -> Self { - from as u8 - } -} -impl FromMinMaxInt for i64 { - fn from_int(from: i32) -> Self { - from as i64 - } -} -impl FromMinMaxInt for f32 { - fn from_int(from: i32) -> Self { - from as f32 - } -} - impl TryFrom<&ffi::ControlPair> for MinMaxValue { type Error = LibcameraError; fn try_from(pair: &ffi::ControlPair) -> Result> { @@ -353,9 +334,18 @@ impl TryFrom<&ffi::ControlPair> for MinMaxValue { type Error = LibcameraError; fn try_from(pair: &ffi::ControlPair) -> Result> { MinMaxValue::new( - unsafe { pair.min.get().get_f32_array() }?.try_into()?, - unsafe { pair.max.get().get_f32_array() }?.try_into()?, - unsafe { pair.value.get().get_f32_array() }?.try_into()?, + unsafe { pair.min.get().get_f32_array() } + .map_err(LibcameraError::InnerError) + .and_then(|v| v.try_into()) + .or_else(|_| unsafe { pair.min.get().get_f32() }.map(|v| FloatPair(v, v)))?, + unsafe { pair.max.get().get_f32_array() } + .map_err(LibcameraError::InnerError) + .and_then(|v| v.try_into()) + .or_else(|_| unsafe { pair.min.get().get_f32() }.map(|v| FloatPair(v, v)))?, + unsafe { pair.value.get().get_f32_array() } + .map_err(LibcameraError::InnerError) + .and_then(|v| v.try_into()) + .or_else(|_| unsafe { pair.min.get().get_f32() }.map(|v| FloatPair(v, v)))?, ) } } @@ -411,6 +401,8 @@ pub enum CameraControlValue { Integer64(MinMaxValue), /// A control value containing a 32-bit float, e.g. brightness. Float(MinMaxValue), + /// A control value containing an array of 32-bit floats. + FloatArray(Vec>), /// A control value containing a String. String(MinMaxValue), /// A control value containing a Rectangle @@ -638,6 +630,7 @@ impl CameraControls { .is_ok(), "ColourGains" => (&control) .try_into() + .map_err(|e| eprintln!("Error converting: {e}")) .map(|control| controls.colour_gains = Some(control)) .is_ok(), "Saturation" => (&control) @@ -665,43 +658,35 @@ impl CameraControls { }; if !did_name_match { let control_type = unsafe { control.id.get().get_type() }; - if let Some(control_value) = match control_type { - ffi::CameraControlType::None => Some(CameraControlValue::None), - ffi::CameraControlType::Bool => (&control) - .try_into() - .or_else(|_| MinMaxValue::::try_from(&control).map(|v| v.into())) - .ok() - .map(CameraControlValue::Bool), - ffi::CameraControlType::Byte => (&control) - .try_into() - .or_else(|_| MinMaxValue::::try_from(&control).map(|v| v.into())) - .ok() - .map(CameraControlValue::Byte), - ffi::CameraControlType::Integer32 => (&control) - .try_into() - .ok() - .map(CameraControlValue::Integer32), - ffi::CameraControlType::Integer64 => (&control) - .try_into() - .or_else(|_| MinMaxValue::::try_from(&control).map(|v| v.into())) - .ok() - .map(CameraControlValue::Integer64), - ffi::CameraControlType::Float => (&control) - .try_into() - .or_else(|_| MinMaxValue::::try_from(&control).map(|v| v.into())) - .ok() - .map(CameraControlValue::Float), + let control_array = unsafe { control.value.get().is_array() }; + let control_value = match (control_type, control_array) { + (ffi::CameraControlType::None, false) => Some(Ok(CameraControlValue::None)), + (ffi::CameraControlType::Bool, false) => { + Some((&control).try_into().map(CameraControlValue::Bool)) + } + (ffi::CameraControlType::Byte, false) => { + Some((&control).try_into().map(CameraControlValue::Byte)) + } + (ffi::CameraControlType::Integer32, false) => { + Some((&control).try_into().map(CameraControlValue::Integer32)) + } + (ffi::CameraControlType::Integer64, false) => { + Some((&control).try_into().map(CameraControlValue::Integer64)) + } + (ffi::CameraControlType::Float, false) => { + Some((&control).try_into().map(CameraControlValue::Float)) + } _ => None, - // ffi::CameraControlType::String => (&control).try_into().ok().map(|control| CameraControlValue::String(control)), - // ffi::CameraControlType::Rectangle => (&control).try_into().ok().map(|control| CameraControlValue::Rectangle(control)), - // ffi::CameraControlType::Size => (&control).try_into().ok().map(|control| CameraControlValue::Size(control)), - } { - controls - .others - .insert(unsafe { control.id.get().get_id() }, (name, control_value)); - } else { - eprintln!("Camera control with conflicting types: {name} is supposed to have type of {control_type:?}."); - } + }; + match control_value { + Some(Ok(control_value)) => { + controls + .others + .insert(unsafe { control.id.get().get_id() }, (name, control_value)); + } + Some(Err(e)) => eprintln!("Camera control with conflicting types: {name} is supposed to have type of {control_type:?}, err: {e}"), + None => eprintln!("Unknown type for camera control {name}."), + }; } } controls @@ -813,6 +798,15 @@ impl CameraControls { CameraControlValue::Float(value) => { Some(unsafe { ffi::new_control_value_f32(*value.get_value()) }) } + CameraControlValue::FloatArray(value) => Some(unsafe { + ffi::new_control_value_f32_array( + value + .iter() + .map(|v| *v.get_value()) + .collect::>() + .as_slice(), + ) + }), CameraControlValue::String(value) => { Some(unsafe { ffi::new_control_value_string(value.get_value()) }) } From 69f007c7a64655590e2fff1717d7beb0a4e2a677 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 20 Jul 2022 16:21:11 -0600 Subject: [PATCH 38/49] fix: impl send for error --- src/controls.rs | 6 ++++-- src/error.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/controls.rs b/src/controls.rs index c286f90..05cd053 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -167,7 +167,7 @@ impl PartialOrd for Size { } } -impl MinMaxValue { +impl MinMaxValue { /// Creates a new MinMaxValue out of a given min, max, and default /// /// # Returns @@ -284,7 +284,9 @@ impl< + Clampable + Copy + Debug - + PartialOrd, + + PartialOrd + + Send + + Sync, > TryFrom<&ffi::ControlPair> for MinMaxValue { type Error = LibcameraError; diff --git a/src/error.rs b/src/error.rs index 1165dce..11ee2e9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,7 +17,7 @@ pub enum LibcameraError { NoBufferReady, /// An error emitted when a control value is attempted to be set to a value outside of the acceptable range. #[error("Control value ({0:?}) out of range!")] - InvalidControlValue(Box), + InvalidControlValue(Box), /// An error reading a control value #[error("Error converting control value!")] ControlValueError, From e4ca6cde0e122f21d75a19c1f5463fd6304f4321 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Fri, 22 Jul 2022 16:20:16 -0600 Subject: [PATCH 39/49] fix: minor fixes for raspberry pi --- libcamera-bridge/control_value.cpp | 2 ++ src/camera.rs | 3 +++ src/image.rs | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libcamera-bridge/control_value.cpp b/libcamera-bridge/control_value.cpp index 1a92b21..f7c4808 100644 --- a/libcamera-bridge/control_value.cpp +++ b/libcamera-bridge/control_value.cpp @@ -37,6 +37,8 @@ BindControlValue new_control_value_f32(float value) { return control_value; } +#include + BindControlValue new_control_value_f32_array(rust::Slice values_rust) { std::vector values; diff --git a/src/camera.rs b/src/camera.rs index 3a052a3..a1aba04 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -299,6 +299,9 @@ impl Camera<'_> { /// Errors if there are no buffers currently available (all buffers are in-use, if this happens take pictures slower!) pub fn capture_next_picture(&mut self, stream_id: usize) -> Result { let mut stream = &mut self.streams[stream_id]; + if stream.buffers.len() == 0 { + return Err(LibcameraError::NoBufferReady); + } let buffer = &mut stream.buffers[stream.next_buffer]; if buffer.request.is_none() { let request_id = self.next_request_id; diff --git a/src/image.rs b/src/image.rs index 0dcdcc1..0968c96 100644 --- a/src/image.rs +++ b/src/image.rs @@ -257,7 +257,7 @@ pub struct Yuv420Image { impl CameraImage<3> for Yuv420Image { fn from_planes(width: usize, height: usize, planes: [Vec; 3]) -> Option { - let [y_plane, u_plane, v_plane] = planes; + let [y_plane, v_plane, u_plane] = planes; // I wonder why this seems backwards? if width * height == y_plane.len() && width / 2 * height / 2 == u_plane.len() && width / 2 * height / 2 == v_plane.len() @@ -303,7 +303,7 @@ impl CameraImage<3> for Yuv420Image { }) .flat_map(|(&y, (u, v))| { // Convert to RGB - let (r, g, b) = yuv2rgb(y, u, v); + let (r, g, b) = yuv2rgb(y, v, u); [r, g, b] }) .collect()], From 38135fb17801a8ef46f2db77e6901537de713272 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Mon, 25 Jul 2022 16:54:02 -0600 Subject: [PATCH 40/49] fix: general fixes --- libcamera-bridge/memory_buffer.cpp | 1 + src/camera.rs | 135 ++++++++++++++++++++--------- 2 files changed, 93 insertions(+), 43 deletions(-) diff --git a/libcamera-bridge/memory_buffer.cpp b/libcamera-bridge/memory_buffer.cpp index 630c633..11e5532 100644 --- a/libcamera-bridge/memory_buffer.cpp +++ b/libcamera-bridge/memory_buffer.cpp @@ -15,6 +15,7 @@ BindMemoryBuffer MemoryBuffer::sub_buffer(size_t offset, size_t length) { rust::Vec MemoryBuffer::read_to_vec() const { rust::Vec buf; + buf.reserve(this->length); for (size_t i = 0; i < this->length; i++) { // The point of this class is to safely wrap raw memory pointers. // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) diff --git a/src/camera.rs b/src/camera.rs index a1aba04..b9cdfc9 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -274,22 +274,6 @@ impl Camera<'_> { unsafe { self.inner.get_mut().start() }?; Ok(true) } - /// Stop the camera stream - pub fn stop_stream(&mut self) -> Result<()> { - unsafe { self.inner.get_mut().stop() }?; - for stream_config in self - .config - .as_ref() - .ok_or(LibcameraError::InvalidConfig)? - .streams() - { - let mut stream = unsafe { stream_config.get_inner().get().stream() }; - unsafe { self.allocator.get_mut().free(stream.get_mut()) }?; - } - self.streams = Vec::new(); - self.started = false; - Ok(()) - } /// Start the process to capture an image from the camera. /// /// # Returns @@ -299,7 +283,7 @@ impl Camera<'_> { /// Errors if there are no buffers currently available (all buffers are in-use, if this happens take pictures slower!) pub fn capture_next_picture(&mut self, stream_id: usize) -> Result { let mut stream = &mut self.streams[stream_id]; - if stream.buffers.len() == 0 { + if stream.buffers.is_empty() { return Err(LibcameraError::NoBufferReady); } let buffer = &mut stream.buffers[stream.next_buffer]; @@ -336,7 +320,7 @@ impl Camera<'_> { /// Poll events from the camera. /// /// The results should be in order of when the camera sent them, but not neccesarily in order of when they were initially queued. Make sure to use `serial_id`, or the event `timestamp` to keep track of that if you need to. - pub fn poll_events(&mut self, match_id: Option) -> Result> { + pub fn poll_events<'a>(&'a mut self, match_id: Option) -> Result> { let events = if let Some(match_id) = match_id { unsafe { self.inner.get_mut().poll_events_with_cookie(match_id) } } else { @@ -347,29 +331,28 @@ impl Camera<'_> { .into_iter() .flat_map(|event| match event.message_type { ffi::CameraMessageType::RequestComplete => { - println!("Ev: {event:?}"); let request_id = event.request_cookie; let request_info = self.request_infos.remove(&request_id)?; - println!( - "Request completed on stream {}, buffer {}.", - request_info.stream_id, request_info.buffer_id - ); + // eprintln!( + // "Request completed on stream {}, buffer {}.", + // request_info.stream_id, request_info.buffer_id + // ); let stream = &mut self.streams[request_info.stream_id]; let buffer = &mut stream.buffers[request_info.buffer_id]; buffer.request = None; - println!("Pixel format: {:?}", stream.pixel_format); + let width = stream.width as usize; + let height = stream.height as usize; + let pixel_format = stream.pixel_format; + Some(CameraEvent::RequestComplete { serial_id: request_id, queue_timestamp: request_info.timestamp, - image: RawCameraImage { - width: stream.width as usize, - height: stream.height as usize, - pixel_format: stream.pixel_format, - planes: buffer - .planes - .iter() - .map(|plane| unsafe { plane.get().read_to_vec() }) - .collect(), + image: ImageBuffer { + width, + height, + pixel_format, + stream_id: request_info.stream_id, + buffer_id: request_info.buffer_id, }, }) } @@ -406,6 +389,7 @@ impl RawCameraImage { /// /// Currently only supports Bgr, Rgb, Yuyv, and Yuv420 formats, and Mjpeg with the `image` feature. pub fn try_decode(self) -> Option { + eprintln!("Tying to decode image..."); match (self.pixel_format, self.planes.as_slice()) { (Some(PixelFormat::Bgr888), [data]) => { image::BgrImage::from_planes(self.width, self.height, [data.to_owned()]) @@ -419,23 +403,40 @@ impl RawCameraImage { image::YuyvImage::from_planes(self.width, self.height, [data.to_owned()]) .map(MultiImage::Yuyv) } - (Some(PixelFormat::Yuv420), [y, u, v]) => image::Yuv420Image::from_planes( - self.width, - self.height, - [y.to_owned(), u.to_owned(), v.to_owned()], - ) - .map(MultiImage::Yuv420), + (Some(PixelFormat::Yuv420), [y, u, v]) => { + eprintln!( + "Decoding YUV with size {}x{} and plane sizes {} {} {}", + self.width, + self.height, + y.len(), + u.len(), + v.len() + ); + image::Yuv420Image::from_planes( + self.width, + self.height, + [y.to_owned(), u.to_owned(), v.to_owned()], + ) + .map(MultiImage::Yuv420) + } #[cfg(feature = "image")] (Some(PixelFormat::Mjpeg), [data]) => { image::RgbImage::decode_jpeg(data).ok().map(MultiImage::Rgb) } - _ => None, + (fmt, planes) => { + eprintln!( + "Image is of unknown format: {:?} with {} planes", + fmt, + planes.len() + ); + None + } } } } /// Represents an event from the camera -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] #[non_exhaustive] pub enum CameraEvent { /// Triggered when a capture request has completed, containing a vec of the resulting image planes. @@ -444,11 +445,59 @@ pub enum CameraEvent { serial_id: u64, /// When this event was __queued__ to the camera. queue_timestamp: Instant, - /// The raw image data for this request, might not actually contain a real image (at the moment there isn't any way of determining success as far as I can tell). - image: RawCameraImage, + /// A reference to the image buffer for this request. + /// Contains the raw image data for this request, might not actually contain a real image (at the moment there isn't any way of determining success as far as I can tell). + /// + /// The user is responsibe for immediately polling this otherwise the image might be overridden by a newer one (typically in around ~67ms). + image: ImageBuffer, }, } +/// References a camera buffer that you can read an image from. +#[derive(Clone)] +#[non_exhaustive] +pub struct ImageBuffer { + /// The pixel format for the image, if it is known. + pub pixel_format: Option, + /// The width of the image. + pub width: usize, + /// The height of the image. + pub height: usize, + stream_id: usize, + buffer_id: usize, +} + +impl Debug for ImageBuffer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ImageBuffer") + .field("pixel_format", &self.pixel_format) + .field("width", &self.width) + .field("height", &self.height) + .field("stream_id", &self.stream_id) + .field("buffer_id", &self.buffer_id) + .finish_non_exhaustive() + } +} + +impl ImageBuffer { + /// Read the image into a [RawCameraImage]. + /// + /// This function is *slow* especially in a debug build. + pub fn read_image(self, cam: &Camera<'_>) -> RawCameraImage { + eprintln!("Reading image..."); + RawCameraImage { + pixel_format: self.pixel_format, + width: self.width, + height: self.height, + planes: cam.streams[self.stream_id].buffers[self.buffer_id] + .planes + .iter() + .map(|plane| unsafe { plane.get().read_to_vec() }) + .collect(), + } + } +} + /// Represents the result of applying a configuration to a camera. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ConfigStatus { From 9a768b0f6531751933d8a5edb1ead8b83f979081 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Thu, 28 Jul 2022 16:43:27 -0600 Subject: [PATCH 41/49] chore: cleaning up --- Cargo.toml | 1 + libcamera-bridge/camera.cpp | 11 ++++---- libcamera-bridge/camera_manager.cpp | 9 +++---- libcamera-bridge/control_value.cpp | 6 ++--- libcamera-bridge/core.hpp | 6 ++--- libcamera-bridge/fd.cpp | 8 +++--- libcamera-bridge/request.cpp | 12 ++++----- readme.md | 2 +- src/camera.rs | 40 +++++++++++++++++------------ src/controls.rs | 7 ++--- src/image.rs | 6 +++-- src/test.rs | 20 +-------------- 12 files changed, 59 insertions(+), 69 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2ddd8c6..175d34b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] cxx = "1.0" thiserror = "1" +log = "0.4" image = { version = "0.24", optional = true } [build-dependencies] diff --git a/libcamera-bridge/camera.cpp b/libcamera-bridge/camera.cpp index 3d95ab0..175bba2 100644 --- a/libcamera-bridge/camera.cpp +++ b/libcamera-bridge/camera.cpp @@ -5,20 +5,21 @@ Camera::Camera(std::shared_ptr inner_) : inner{std::move(inner_)} { this->inner->bufferCompleted.connect( - this, [&](libcamera::Request *req, libcamera::FrameBuffer *fb) { + this, + [&](libcamera::Request *request, libcamera::FrameBuffer *framebuffer) { this->message_mutex.lock(); this->message_queue.push(CameraMessage{ .message_type = CameraMessageType::BufferComplete, - .request_cookie = req->cookie(), - .buffer_cookie = fb->cookie(), + .request_cookie = request->cookie(), + .buffer_cookie = framebuffer->cookie(), }); this->message_mutex.unlock(); }); - this->inner->requestCompleted.connect(this, [&](libcamera::Request *req) { + this->inner->requestCompleted.connect(this, [&](libcamera::Request *request) { this->message_mutex.lock(); this->message_queue.push(CameraMessage{ .message_type = CameraMessageType::RequestComplete, - .request_cookie = req->cookie(), + .request_cookie = request->cookie(), .buffer_cookie = 0, }); this->message_mutex.unlock(); diff --git a/libcamera-bridge/camera_manager.cpp b/libcamera-bridge/camera_manager.cpp index 5cd8d0a..c1290c4 100644 --- a/libcamera-bridge/camera_manager.cpp +++ b/libcamera-bridge/camera_manager.cpp @@ -37,14 +37,13 @@ rust::Vec CameraManager::get_camera_ids() const { return camera_ids; } -BindCamera CameraManager::get_camera_by_id(rust::Str id) { +BindCamera CameraManager::get_camera_by_id(rust::Str rust_id) { VALIDATE_POINTERS() - std::string cam_id = std::string(id); + std::string cam_id = std::string(rust_id); std::shared_ptr cam = this->inner->get(cam_id); - size_t uc = cam.use_count(); // C++ header mismatch will cause this to be - // completely silly - if (!cam || uc > INT_MAX) { + // C++ header mismatch will cause this to be completely silly + if (!cam || cam.use_count() > INT_MAX) { throw error_from_code(ENODEV); } BindCamera bind_cam{ diff --git a/libcamera-bridge/control_value.cpp b/libcamera-bridge/control_value.cpp index f7c4808..cdf87af 100644 --- a/libcamera-bridge/control_value.cpp +++ b/libcamera-bridge/control_value.cpp @@ -37,8 +37,6 @@ BindControlValue new_control_value_f32(float value) { return control_value; } -#include - BindControlValue new_control_value_f32_array(rust::Slice values_rust) { std::vector values; @@ -147,8 +145,8 @@ rust::Vec ControlValue::get_f32_array() const { } auto span = this->inner.get>(); rust::Vec values; - for (float f : span) { - values.push_back(f); + for (float value : span) { + values.push_back(value); } return values; } diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 8732b66..468c3bc 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -208,7 +208,7 @@ struct FrameBuffer { uint64_t get_cookie() const; }; -size_t fd_len(int fd); +size_t fd_len(int file); struct FrameBufferPlane { private: @@ -225,7 +225,7 @@ struct FrameBufferPlane { // File descriptor functions -BindMemoryBuffer mmap_plane(int fd, size_t len); +BindMemoryBuffer mmap_plane(int file, size_t len); struct MemoryBuffer { private: @@ -251,7 +251,7 @@ struct Request { void add_buffer(const Stream &stream, FrameBuffer &buffer); BindControlValue get_control(uint32_t id) const; - void set_control(uint32_t id, const ControlValue &value); + void set_control(uint32_t control_, const ControlValue &value); [[nodiscard]] rust::String raw_to_string() const; }; diff --git a/libcamera-bridge/fd.cpp b/libcamera-bridge/fd.cpp index d1a1ff5..2b2d8f9 100644 --- a/libcamera-bridge/fd.cpp +++ b/libcamera-bridge/fd.cpp @@ -5,16 +5,16 @@ #include #include -size_t fd_len(int fd) { - long ret = lseek(fd, 0, SEEK_END); +size_t fd_len(int file) { + long ret = lseek(file, 0, SEEK_END); if (ret < 0) { throw error_from_code(errno); } return ret; } -BindMemoryBuffer mmap_plane(int fd, size_t len) { - void *address = mmap(nullptr, len, PROT_READ, MAP_SHARED, fd, 0); +BindMemoryBuffer mmap_plane(int file, size_t len) { + void *address = mmap(nullptr, len, PROT_READ, MAP_SHARED, file, 0); // MAP_FAILED expands to a silly C-style cast. // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) if ((address == nullptr) || address == MAP_FAILED) { diff --git a/libcamera-bridge/request.cpp b/libcamera-bridge/request.cpp index b460a3d..7ea4708 100644 --- a/libcamera-bridge/request.cpp +++ b/libcamera-bridge/request.cpp @@ -17,29 +17,27 @@ libcamera::Request *Request::into_ptr() { return this->inner.get(); } -#include - -BindControlValue Request::get_control(uint32_t id) const { +BindControlValue Request::get_control(uint32_t control_id) const { VALIDATE_POINTERS() libcamera::ControlList &controls = this->inner->controls(); - if (!controls.contains(id)) { + if (!controls.contains(control_id)) { throw std::runtime_error( "No control has been set in this request with the specified id."); } BindControlValue control_value{ - .inner = std::make_unique(controls.get(id)), + .inner = std::make_unique(controls.get(control_id)), }; return control_value; } -void Request::set_control(uint32_t id, const ControlValue &value) { +void Request::set_control(uint32_t control_, const ControlValue &value) { VALIDATE_POINTERS() libcamera::ControlList &controls = this->inner->controls(); - controls.set(id, value.get_inner()); + controls.set(control_, value.get_inner()); } rust::String Request::raw_to_string() const { diff --git a/readme.md b/readme.md index 97177e1..6c5826c 100644 --- a/readme.md +++ b/readme.md @@ -38,7 +38,7 @@ fn main() { serial_id, image, .. } => { // Reencode the image to PNG and save it. - let decoded_image = image.try_decode().unwrap(); + let decoded_image = image.read_image(&cam).try_decode().unwrap(); let rgb_image = decoded_image .as_bgr() .unwrap() diff --git a/src/camera.rs b/src/camera.rs index b9cdfc9..198c9b9 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -4,6 +4,8 @@ use std::marker::PhantomData; use std::sync::RwLock; use std::time::Instant; +use log::{debug, trace}; + use crate::bridge::{ffi, GetInner}; use crate::config::{CameraConfig, PixelFormat}; use crate::controls::CameraControls; @@ -202,6 +204,8 @@ impl Camera<'_> { // We also must create a request for each buffer that we can re-use later every time we need an image. // Technically requests can have multiple buffers, but I don't think know why this would be the case and I don't think it's necessary. + debug!("Starting camera..."); + // For each stream... for stream_config in self .config @@ -209,7 +213,7 @@ impl Camera<'_> { .ok_or(LibcameraError::InvalidConfig)? .streams() { - println!("Stream config: {stream_config:?}"); + trace!("Stream config: {stream_config:?}"); let mut stream = unsafe { stream_config.get_inner().get().stream() }; // Allocate buffers let _buffer_count = unsafe { self.allocator.get_mut().allocate(stream.get_mut()) }; @@ -222,7 +226,7 @@ impl Camera<'_> { next_buffer: 0, buffers: Vec::new(), }; - println!("Camera stream: {camera_stream:?}"); + trace!("Camera stream: {camera_stream:?}"); // Map memory for buffers for mut buffer in unsafe { self.allocator.get().buffers(camera_stream.stream.get_mut()) } { let buffer_id = camera_stream.buffers.len(); @@ -320,7 +324,7 @@ impl Camera<'_> { /// Poll events from the camera. /// /// The results should be in order of when the camera sent them, but not neccesarily in order of when they were initially queued. Make sure to use `serial_id`, or the event `timestamp` to keep track of that if you need to. - pub fn poll_events<'a>(&'a mut self, match_id: Option) -> Result> { + pub fn poll_events(&mut self, match_id: Option) -> Result> { let events = if let Some(match_id) = match_id { unsafe { self.inner.get_mut().poll_events_with_cookie(match_id) } } else { @@ -333,10 +337,11 @@ impl Camera<'_> { ffi::CameraMessageType::RequestComplete => { let request_id = event.request_cookie; let request_info = self.request_infos.remove(&request_id)?; - // eprintln!( - // "Request completed on stream {}, buffer {}.", - // request_info.stream_id, request_info.buffer_id - // ); + trace!( + "Request completed on stream {}, buffer {}.", + request_info.stream_id, + request_info.buffer_id + ); let stream = &mut self.streams[request_info.stream_id]; let buffer = &mut stream.buffers[request_info.buffer_id]; buffer.request = None; @@ -389,7 +394,7 @@ impl RawCameraImage { /// /// Currently only supports Bgr, Rgb, Yuyv, and Yuv420 formats, and Mjpeg with the `image` feature. pub fn try_decode(self) -> Option { - eprintln!("Tying to decode image..."); + debug!("Tying to decode image..."); match (self.pixel_format, self.planes.as_slice()) { (Some(PixelFormat::Bgr888), [data]) => { image::BgrImage::from_planes(self.width, self.height, [data.to_owned()]) @@ -404,7 +409,7 @@ impl RawCameraImage { .map(MultiImage::Yuyv) } (Some(PixelFormat::Yuv420), [y, u, v]) => { - eprintln!( + trace!( "Decoding YUV with size {}x{} and plane sizes {} {} {}", self.width, self.height, @@ -424,7 +429,7 @@ impl RawCameraImage { image::RgbImage::decode_jpeg(data).ok().map(MultiImage::Rgb) } (fmt, planes) => { - eprintln!( + trace!( "Image is of unknown format: {:?} with {} planes", fmt, planes.len() @@ -484,16 +489,19 @@ impl ImageBuffer { /// /// This function is *slow* especially in a debug build. pub fn read_image(self, cam: &Camera<'_>) -> RawCameraImage { - eprintln!("Reading image..."); + trace!("Reading image from buffer..."); + let start = Instant::now(); + let planes = cam.streams[self.stream_id].buffers[self.buffer_id] + .planes + .iter() + .map(|plane| unsafe { plane.get().read_to_vec() }) + .collect(); + debug!("Read image from buffer in {:?}", start.elapsed()); RawCameraImage { pixel_format: self.pixel_format, width: self.width, height: self.height, - planes: cam.streams[self.stream_id].buffers[self.buffer_id] - .planes - .iter() - .map(|plane| unsafe { plane.get().read_to_vec() }) - .collect(), + planes, } } } diff --git a/src/controls.rs b/src/controls.rs index 05cd053..1eaec4c 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; use std::fmt::Debug; use std::ops::RangeInclusive; +use log::{error, warn}; + use crate::bridge::{ffi, GetInner}; use crate::{LibcameraError, Result}; @@ -632,7 +634,6 @@ impl CameraControls { .is_ok(), "ColourGains" => (&control) .try_into() - .map_err(|e| eprintln!("Error converting: {e}")) .map(|control| controls.colour_gains = Some(control)) .is_ok(), "Saturation" => (&control) @@ -686,8 +687,8 @@ impl CameraControls { .others .insert(unsafe { control.id.get().get_id() }, (name, control_value)); } - Some(Err(e)) => eprintln!("Camera control with conflicting types: {name} is supposed to have type of {control_type:?}, err: {e}"), - None => eprintln!("Unknown type for camera control {name}."), + Some(Err(e)) => error!("Camera control with conflicting types: {name} is supposed to have type of {control_type:?}, err: {e}"), + None => warn!("Unknown type for camera control {name}."), }; } } diff --git a/src/image.rs b/src/image.rs index 0968c96..e6c6e94 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,3 +1,5 @@ +use log::trace; + use crate::{LibcameraError, Result}; /// Represents an image. @@ -158,10 +160,10 @@ impl RgbImage { /// Available only with the `image` feature/crate. pub fn decode_jpeg(data: &[u8]) -> Result { let image = image::load_from_memory_with_format(data, image::ImageFormat::Jpeg)?; - println!("Image loaded"); + trace!("Image loaded"); if let image::DynamicImage::ImageRgb8(img) = image { let (width, height) = img.dimensions(); - println!("JPEG image size {width}x{height}."); + trace!("JPEG image size {width}x{height}."); Ok( RgbImage::from_planes(width as usize, height as usize, [img.into_raw()]) .ok_or(LibcameraError::BadImageFormat)?, diff --git a/src/test.rs b/src/test.rs index c65871e..b070fa1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -30,7 +30,7 @@ fn test_camera() { CameraEvent::RequestComplete { serial_id, image, .. } => { - let decoded_image = image.try_decode().unwrap(); + let decoded_image = image.read_image(&cam).try_decode().unwrap(); let rgb_image = decoded_image .as_bgr() .unwrap() @@ -71,21 +71,3 @@ fn try_start_before_configure() { )); cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); } - -#[test] -fn stop_start_stream() { - let cm = CameraManager::new().unwrap(); - let mut cam = cm.get_camera_by_name(&cm.get_camera_names()[0]).unwrap(); - let conf = cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); - let stream = &mut conf.streams_mut()[0]; - stream.set_pixel_format(PixelFormat::Yuv420); - stream.set_size(640, 480); - cam.apply_config().unwrap(); - for _i in 0..6 { - cam.start_stream().unwrap(); - cam.capture_next_picture(0).unwrap(); - std::thread::sleep(std::time::Duration::from_millis(500)); - cam.poll_events(None).unwrap(); - cam.stop_stream().unwrap(); - } -} From f86b1678d17bce5d59ddc95d274deb60fdee372e Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Fri, 29 Jul 2022 16:58:29 -0600 Subject: [PATCH 42/49] fix: fix colour decoding --- src/controls.rs | 7 ++++++- src/image.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/controls.rs b/src/controls.rs index 1eaec4c..9b702d6 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::fmt::Debug; use std::ops::RangeInclusive; -use log::{error, warn}; +use log::{error, trace, warn}; use crate::bridge::{ffi, GetInner}; use crate::{LibcameraError, Result}; @@ -823,6 +823,11 @@ impl CameraControls { controls.push((*id, value)); } } + for (id, control) in &controls { + trace!("Setting control {id} to {}", unsafe { + control.get().raw_to_string() + }); + } controls } } diff --git a/src/image.rs b/src/image.rs index e6c6e94..fbdc27f 100644 --- a/src/image.rs +++ b/src/image.rs @@ -259,7 +259,7 @@ pub struct Yuv420Image { impl CameraImage<3> for Yuv420Image { fn from_planes(width: usize, height: usize, planes: [Vec; 3]) -> Option { - let [y_plane, v_plane, u_plane] = planes; // I wonder why this seems backwards? + let [y_plane, u_plane, v_plane] = planes; if width * height == y_plane.len() && width / 2 * height / 2 == u_plane.len() && width / 2 * height / 2 == v_plane.len() From 4f3cbe9066f65c06fbc527d8f8d735c48a36c301 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Tue, 9 Aug 2022 16:31:00 -0600 Subject: [PATCH 43/49] fix: don't segfault when dropping a camera with outstanding requests --- src/camera.rs | 8 ++++++++ src/controls.rs | 12 ++++++------ src/test.rs | 30 +++++++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index 198c9b9..77fb620 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -370,6 +370,14 @@ impl Camera<'_> { impl Drop for Camera<'_> { fn drop(&mut self) { + // Ensure there are no outstanding requests before deallocating everything. + // TODO: It would potentially be a better idea to use a thread to hold on to important c++ references instead of blocking here. + while !self.request_infos.is_empty() { + std::thread::sleep(std::time::Duration::from_millis(50)); + for event in unsafe { self.inner.get_mut().poll_events() } { + self.request_infos.remove(&event.request_cookie); + } + } self.streams = Vec::new(); unsafe { self.inner.get_mut().stop() }.unwrap(); unsafe { self.inner.get_mut().release() }.unwrap(); diff --git a/src/controls.rs b/src/controls.rs index 9b702d6..920607c 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::fmt::Debug; use std::ops::RangeInclusive; -use log::{error, trace, warn}; +use log::{error, warn}; use crate::bridge::{ffi, GetInner}; use crate::{LibcameraError, Result}; @@ -823,11 +823,11 @@ impl CameraControls { controls.push((*id, value)); } } - for (id, control) in &controls { - trace!("Setting control {id} to {}", unsafe { - control.get().raw_to_string() - }); - } + // for (id, control) in &controls { + // trace!("Setting control {id} to {}", unsafe { + // control.get().raw_to_string() + // }); + // } controls } } diff --git a/src/test.rs b/src/test.rs index b070fa1..399d095 100644 --- a/src/test.rs +++ b/src/test.rs @@ -57,7 +57,7 @@ fn panic_with_camera() { cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); cam.apply_config().unwrap(); cam.start_stream().unwrap(); - panic!("Ah!"); + panic!("Success"); } #[test] @@ -71,3 +71,31 @@ fn try_start_before_configure() { )); cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); } + +#[test] +fn capture_then_drop() { + // Wait to ensure we can lock the camera (when running many tests back-to-back). + std::thread::sleep(std::time::Duration::from_secs(5)); + let cm = CameraManager::new().unwrap(); + let mut cam = cm.get_camera_by_name(&cm.get_camera_names()[0]).unwrap(); + cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); + cam.apply_config().unwrap(); + cam.start_stream().unwrap(); + cam.capture_next_picture(0).unwrap(); + cam.capture_next_picture(0).unwrap(); +} + +#[test] +#[should_panic] +fn capture_then_panic() { + // Wait to ensure we can lock the camera (when running many tests back-to-back). + std::thread::sleep(std::time::Duration::from_secs(5)); + let cm = CameraManager::new().unwrap(); + let mut cam = cm.get_camera_by_name(&cm.get_camera_names()[0]).unwrap(); + cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); + cam.apply_config().unwrap(); + cam.start_stream().unwrap(); + cam.capture_next_picture(0).unwrap(); + cam.capture_next_picture(0).unwrap(); + panic!("Success"); +} From 876788a5ce6e5326304d8391314bdfa4aef900b4 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 10 Aug 2022 13:01:35 -0600 Subject: [PATCH 44/49] chore: add another test --- .gitignore | 1 + Cargo.toml | 4 ++ src/camera.rs | 3 + src/controls.rs | 12 ++-- src/prelude.rs | 3 +- src/test.rs | 166 +++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 181 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index d92ccbc..4747bd1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ Cargo.lock plane_*.bin image_*.png +tmp.bgr diff --git a/Cargo.toml b/Cargo.toml index 175d34b..2c7c445 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,5 +14,9 @@ image = { version = "0.24", optional = true } [build-dependencies] cxx-build = "1.0" +[dev-dependencies] +lazy_static = "1" +simplelog = "0.12" + [features] default = [ "image" ] diff --git a/src/camera.rs b/src/camera.rs index 77fb620..bb73a94 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -370,6 +370,7 @@ impl Camera<'_> { impl Drop for Camera<'_> { fn drop(&mut self) { + log::trace!("Waiting for requests to complete before dropping camera..."); // Ensure there are no outstanding requests before deallocating everything. // TODO: It would potentially be a better idea to use a thread to hold on to important c++ references instead of blocking here. while !self.request_infos.is_empty() { @@ -378,9 +379,11 @@ impl Drop for Camera<'_> { self.request_infos.remove(&event.request_cookie); } } + log::trace!("Stopping camera..."); self.streams = Vec::new(); unsafe { self.inner.get_mut().stop() }.unwrap(); unsafe { self.inner.get_mut().release() }.unwrap(); + log::trace!("Dropping camera..."); } } diff --git a/src/controls.rs b/src/controls.rs index 920607c..9b702d6 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::fmt::Debug; use std::ops::RangeInclusive; -use log::{error, warn}; +use log::{error, trace, warn}; use crate::bridge::{ffi, GetInner}; use crate::{LibcameraError, Result}; @@ -823,11 +823,11 @@ impl CameraControls { controls.push((*id, value)); } } - // for (id, control) in &controls { - // trace!("Setting control {id} to {}", unsafe { - // control.get().raw_to_string() - // }); - // } + for (id, control) in &controls { + trace!("Setting control {id} to {}", unsafe { + control.get().raw_to_string() + }); + } controls } } diff --git a/src/prelude.rs b/src/prelude.rs index cc96e76..4744372 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,3 +1,4 @@ -pub use crate::camera::{CameraEvent, CameraManager, StreamRole}; +pub use crate::camera::{CameraEvent, CameraManager, ConfigStatus, StreamRole}; pub use crate::config::PixelFormat; pub use crate::error::LibcameraError; +pub use crate::image::CameraImage; diff --git a/src/test.rs b/src/test.rs index 399d095..bce0122 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,4 +1,6 @@ -use crate::prelude::{CameraEvent, CameraManager, LibcameraError, PixelFormat, StreamRole}; +use crate::prelude::{ + CameraEvent, CameraImage, CameraManager, ConfigStatus, LibcameraError, PixelFormat, StreamRole, +}; #[cfg(feature = "image")] #[test] @@ -99,3 +101,165 @@ fn capture_then_panic() { cam.capture_next_picture(0).unwrap(); panic!("Success"); } + +lazy_static::lazy_static! { + static ref CAMERA_MANAGER: CameraManager = { + let cm = CameraManager::new().unwrap(); + println!("Camera Manager: {cm:?}"); + cm + }; +} + +/// This test is designed to behave as similar as possible to ps-native. +#[test] +fn background_capture_thread() { + use simplelog::SimpleLogger; + use std::sync::mpsc::{self, channel}; + use std::sync::{Arc, RwLock}; + use std::thread; + use std::time::Duration; + + use crate::controls; + + SimpleLogger::init(simplelog::LevelFilter::Trace, simplelog::Config::default()).unwrap(); + + let mut cam = { + let selected_cam = CAMERA_MANAGER.get_camera_names()[0].clone(); + log::debug!("Selected camera: {selected_cam:?}"); + CAMERA_MANAGER.get_camera_by_name(&selected_cam).unwrap() + }; + log::debug!("Created camera."); + let camera_config = cam.generate_config(&[StreamRole::Viewfinder]).unwrap(); + let stream_config = &mut camera_config.streams_mut()[0]; + log::debug!("Updating stream config..."); + stream_config.set_pixel_format(PixelFormat::Yuv420); + stream_config.set_size(1280, 960); + log::debug!("Applying config..."); + let status = cam.apply_config().unwrap(); + if status == ConfigStatus::Changed { + log::debug!("Configuration updated by camera."); + } + log::debug!("Starting initial stream: {}", cam.start_stream().unwrap(),); + + log::info!("Camera ready!"); + let cam = Arc::new(RwLock::new(cam)); + let (req_tx, req_rx) = channel(); + let (frame_tx, frame_rx) = channel(); + let thread_join_handle = { + let cam = cam.clone(); + thread::spawn(move || { + for _i in 0..4 { + cam.write().unwrap().capture_next_picture(0).unwrap(); + } + let mut capture_next = false; + let mut capture_id = None; + loop { + log::trace!("[CAM] Camera Loop"); + match req_rx.try_recv() { + Ok(()) => capture_next = true, + Err(mpsc::TryRecvError::Disconnected) => { + log::debug!("Exiting Thread."); + break; + } + Err(mpsc::TryRecvError::Empty) => {} + }; + log::trace!("[CAM] Polling camera..."); + let mut cam = cam.write().unwrap(); + let events = cam.poll_events(None).unwrap(); + log::trace!("[CAM] Processing events..."); + for event in events { + match event { + CameraEvent::RequestComplete { + serial_id, image, .. + } => { + if capture_id == Some(serial_id) { + log::debug!("[CAM] Captured frame: {serial_id}"); + capture_id = None; + frame_tx.send(image.read_image(&cam)).unwrap(); + log::debug!("[CAM] Sent."); + } + log::trace!("[CAM] Captured image, queuing another request."); + let next = cam.capture_next_picture(0).unwrap(); + log::trace!("[CAM] Queued."); + if capture_id.is_none() && capture_next { + capture_id = Some(next); + capture_next = false; + log::debug!("[CAM] Capture next frame: {next}"); + } + } + } + } + log::trace!("[CAM] Done processing events, sleeping..."); + thread::sleep(Duration::from_millis(50)); + } + }) + }; + log::debug!("Setting default controls."); + { + let mut cam = cam.write().unwrap(); + let controls = cam.get_controls_mut(); + if let Some(brightness) = controls.brightness.as_mut() { + brightness.set_value_clamped(0.0); + } + if let Some(contrast) = controls.contrast.as_mut() { + contrast.set_value_clamped(1.1); + } + if let Some(awb_enable) = controls.awb_enable.as_mut() { + awb_enable.set_value_clamped(false); + } + if let Some(ae_enable) = controls.ae_enable.as_mut() { + ae_enable.set_value_clamped(false); + } + if let Some(colour_gains) = controls.colour_gains.as_mut() { + colour_gains.set_value_clamped(controls::FloatPair(1.63, 1.63)); + } + if let Some(exposure_time) = controls.exposure_time.as_mut() { + exposure_time.set_value_clamped(243); + } + } + thread::sleep(Duration::from_secs(1)); + log::info!("Starting image capture..."); + for i in 0..50 { + if (i % 4 > 1 || i > 40) && (i % 3 < 2 || i > 45) { + log::debug!("Setting controls..."); + let mut cam = cam.write().unwrap(); + let controls = cam.get_controls_mut(); + if let Some(colour_gains) = controls.colour_gains.as_mut() { + colour_gains.set_value_clamped(if i % 2 == 0 { + controls::FloatPair(1.63, 1.63) + } else { + controls::FloatPair(1.0, 1.0) + }); + } + if let Some(exposure_time) = controls.exposure_time.as_mut() { + exposure_time.set_value_clamped(243 + 10 - i * 2); + } + log::debug!("Controls set."); + } + thread::sleep(Duration::from_millis(if i == 35 { + 2000 + } else if i < 10 { + 200 + } else { + 0 + })); + let start = std::time::Instant::now(); + log::debug!("[IMG] Requesting image..."); + req_tx.send(()).unwrap(); + log::debug!("[IMG] Waiting to get image..."); + let image = frame_rx.recv().unwrap(); + log::debug!("[IMG] Took picture in {:?}", start.elapsed()); + let image = image.try_decode().unwrap(); + log::debug!("[IMG] Decoded image in {:?}", start.elapsed()); + let image = image.as_bgr().unwrap(); + log::debug!("[IMG] Converted colour space in {:?}.", start.elapsed()); + let (width, height) = image.get_size(); + log::debug!("[IMG] Image size: {width}x{height}"); + std::fs::write("tmp.bgr", image.get_planes()[0]).unwrap(); + } + log::debug!("[IMG] Dropping request tx."); + drop(req_tx); + log::debug!("[IMG] Joining thread."); + thread_join_handle.join().unwrap(); + log::info!("[IMG] Exiting"); +} From 3034572e7c9b0534d9333929f20917abafefa06c Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Wed, 17 Aug 2022 16:09:26 -0600 Subject: [PATCH 45/49] feat: Load NV12 images --- libcamera-bridge/pixel_format.cpp | 6 ++ src/bridge.rs | 2 + src/camera.rs | 11 ++++ src/image.rs | 103 ++++++++++++++++++++---------- src/test.rs | 2 +- 5 files changed, 89 insertions(+), 35 deletions(-) diff --git a/libcamera-bridge/pixel_format.cpp b/libcamera-bridge/pixel_format.cpp index 36c9a99..6275ff8 100644 --- a/libcamera-bridge/pixel_format.cpp +++ b/libcamera-bridge/pixel_format.cpp @@ -34,6 +34,9 @@ BindPixelFormat get_default_pixel_format(DefaultPixelFormat default_format) { case DefaultPixelFormat::Mjpeg: fmt = &libcamera::formats::MJPEG; break; + case DefaultPixelFormat::Nv12: + fmt = &libcamera::formats::NV12; + break; } if (fmt == nullptr) { throw std::runtime_error("Unknown default pixel format."); @@ -72,6 +75,9 @@ DefaultPixelFormat PixelFormat::as_default_pixel_format() const { if (this->inner == libcamera::formats::MJPEG) { return DefaultPixelFormat::Mjpeg; } + if (this->inner == libcamera::formats::NV12) { + return DefaultPixelFormat::Nv12; + } throw std::runtime_error("Unknown pixel format."); } diff --git a/src/bridge.rs b/src/bridge.rs index 60f8758..00e361e 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -51,6 +51,8 @@ pub mod ffi { Yuv420, /// 16bpp*, chroma subsampling (U, V half width), three channel image, YUV (4:2:2) encoing, order Y', U, V Yuv422, + /// 16bpp*, chroma subsampling (U, V half width + height), two channel image, YUV (4:2:0) encoing, order Y', U/V interlaced + Nv12, /// MJPEG (motion JPEG) encoding, effectively one JPEG image per frame Mjpeg, } diff --git a/src/camera.rs b/src/camera.rs index bb73a94..08345fb 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -439,6 +439,17 @@ impl RawCameraImage { (Some(PixelFormat::Mjpeg), [data]) => { image::RgbImage::decode_jpeg(data).ok().map(MultiImage::Rgb) } + (Some(PixelFormat::Nv12), [y, uv]) => { + trace!( + "Decoding NV12 with size {}x{} and plane sizes {} {}", + self.width, + self.height, + y.len(), + uv.len(), + ); + image::Nv12Image::from_planes(self.width, self.height, [y.to_owned(), uv.to_owned()]) + .map(MultiImage::Nv12) + } (fmt, planes) => { trace!( "Image is of unknown format: {:?} with {} planes", diff --git a/src/image.rs b/src/image.rs index fbdc27f..227a461 100644 --- a/src/image.rs +++ b/src/image.rs @@ -28,6 +28,8 @@ pub enum MultiImage { Yuyv(YuyvImage), /// Image in the YUV 4:2:0 format. Yuv420(Yuv420Image), + /// Image in the NV12 (YUV 4:2:0, interleaved U/V) format. + Nv12(Nv12Image), } impl MultiImage { @@ -38,6 +40,7 @@ impl MultiImage { MultiImage::Rgb(img) => img.as_bgr(), MultiImage::Yuyv(img) => img.as_bgr(), MultiImage::Yuv420(img) => img.as_bgr(), + MultiImage::Nv12(img) => img.as_bgr(), } } /// Get the size of this image. @@ -47,6 +50,7 @@ impl MultiImage { MultiImage::Rgb(img) => img.get_size(), MultiImage::Yuyv(img) => img.get_size(), MultiImage::Yuv420(img) => img.get_size(), + MultiImage::Nv12(img) => img.get_size(), } } } @@ -276,40 +280,22 @@ impl CameraImage<3> for Yuv420Image { } } fn as_bgr(&self) -> Option { - BgrImage::from_planes( - self.width, - self.height, - [self - .y_plane - .chunks_exact(self.width) // Get each line of y* - .zip( - // Zip it with each line of u* and v* (half width+height) - self - .u_plane // For the u plane... - .chunks_exact(self.width / 2) - .map(|line| line.iter().flat_map(|&u| [u, u])) // Double the width - .zip( - // Zip the u and v planes - self - .v_plane // For the v plane... - .chunks_exact(self.width / 2) - .map(|line| line.iter().flat_map(|&v| [v, v])), // Double the width - ) - .flat_map(|line| [line.clone(), line]), // Double the height - ) - .flat_map(|(y_line, (u_line, v_line))| { - // Re-zip y*, u* and v* per-pixel instead of per-line. - y_line - .iter() - .zip(u_line.into_iter().zip(v_line.into_iter())) - }) - .flat_map(|(&y, (u, v))| { - // Convert to RGB - let (r, g, b) = yuv2rgb(y, v, u); - [r, g, b] - }) - .collect()], - ) + let mut new_plane = Vec::new(); + new_plane.reserve_exact(self.width * self.height * 3); + for y in 0..self.height { + for x in 0..self.width { + let (y, u, v) = ( + self.y_plane[y * self.width + x], + self.u_plane[y / 2 * self.width / 2 + x / 2], + self.v_plane[y / 2 * self.width / 2 + x / 2], + ); + let (r, g, b) = yuv2rgb(y, u, v); + new_plane.push(b); + new_plane.push(g); + new_plane.push(r); + } + } + BgrImage::from_planes(self.width, self.height, [new_plane]) } fn get_size(&self) -> (usize, usize) { (self.width, self.height) @@ -318,3 +304,52 @@ impl CameraImage<3> for Yuv420Image { [&self.y_plane, &self.u_plane, &self.v_plane] } } + +/// Contains an image in NV12 (YUV 4:2:0, interleaved U/V) Format. +#[derive(Debug, Clone)] +pub struct Nv12Image { + width: usize, + height: usize, + y_plane: Vec, + uv_plane: Vec, +} + +impl CameraImage<2> for Nv12Image { + fn from_planes(width: usize, height: usize, planes: [Vec; 2]) -> Option { + let [y_plane, uv_plane] = planes; + if width * height == y_plane.len() && width / 2 * height / 2 * 2 == uv_plane.len() { + Some(Nv12Image { + width, + height, + y_plane, + uv_plane, + }) + } else { + None + } + } + fn as_bgr(&self) -> Option { + let mut new_plane = Vec::new(); + new_plane.reserve_exact(self.width * self.height * 3); + for y in 0..self.height { + for x in 0..self.width { + let (y, u, v) = ( + self.y_plane[y * self.width + x], + self.uv_plane[(y / 2 * self.width / 2 + x / 2) * 2], + self.uv_plane[(y / 2 * self.width / 2 + x / 2) * 2 + 1], + ); + let (r, g, b) = yuv2rgb(y, u, v); + new_plane.push(b); + new_plane.push(g); + new_plane.push(r); + } + } + BgrImage::from_planes(self.width, self.height, [new_plane]) + } + fn get_size(&self) -> (usize, usize) { + (self.width, self.height) + } + fn get_planes(&self) -> [&[u8]; 2] { + [&self.y_plane, &self.uv_plane] + } +} diff --git a/src/test.rs b/src/test.rs index bce0122..2bf3d06 100644 --- a/src/test.rs +++ b/src/test.rs @@ -105,7 +105,7 @@ fn capture_then_panic() { lazy_static::lazy_static! { static ref CAMERA_MANAGER: CameraManager = { let cm = CameraManager::new().unwrap(); - println!("Camera Manager: {cm:?}"); + log::debug!("Camera Manager: {cm:?}"); cm }; } From 3e6c5a2942051170f2de1de6db4a842ac33f75f6 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Thu, 18 Aug 2022 12:00:50 -0600 Subject: [PATCH 46/49] fix: use chunks_exact instead of chunks to prevent a potential panic while loading an image --- src/image.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/image.rs b/src/image.rs index 227a461..a32cc8d 100644 --- a/src/image.rs +++ b/src/image.rs @@ -95,7 +95,7 @@ impl BgrImage { self.height, [self .data - .chunks(3) + .chunks_exact(3) .flat_map(|chunk| { if let &[b, g, r] = chunk { [r, g, b] @@ -137,7 +137,7 @@ impl CameraImage<1> for RgbImage { self.height, [self .data - .chunks(3) + .chunks_exact(3) .flat_map(|chunk| { if let &[r, g, b] = chunk { [b, g, r] From 1b03dfb33e2f0756da4a145718878a11476cca9d Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Thu, 18 Aug 2022 12:33:23 -0600 Subject: [PATCH 47/49] chore: add license --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 2c7c445..d29dfb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ description = "Libcamera bindings for rust." authors = ["John Brandt ", "Ben Heard "] version = "0.1.0" edition = "2021" +license = "ISC" [dependencies] cxx = "1.0" From ef03528f1b99f18d0038593c7c4960f1d41c264b Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Thu, 18 Aug 2022 15:16:55 -0600 Subject: [PATCH 48/49] chore: disable noisy logging --- src/camera.rs | 10 +++++----- src/controls.rs | 5 ----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index 08345fb..203ad2a 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -337,11 +337,11 @@ impl Camera<'_> { ffi::CameraMessageType::RequestComplete => { let request_id = event.request_cookie; let request_info = self.request_infos.remove(&request_id)?; - trace!( - "Request completed on stream {}, buffer {}.", - request_info.stream_id, - request_info.buffer_id - ); + // trace!( + // "Request completed on stream {}, buffer {}.", + // request_info.stream_id, + // request_info.buffer_id + // ); let stream = &mut self.streams[request_info.stream_id]; let buffer = &mut stream.buffers[request_info.buffer_id]; buffer.request = None; diff --git a/src/controls.rs b/src/controls.rs index 9b702d6..740a247 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -823,11 +823,6 @@ impl CameraControls { controls.push((*id, value)); } } - for (id, control) in &controls { - trace!("Setting control {id} to {}", unsafe { - control.get().raw_to_string() - }); - } controls } } From 2c0be51e5b34e685026d6ccad775b97f3122e500 Mon Sep 17 00:00:00 2001 From: BEN1JEN Date: Mon, 22 Aug 2022 14:45:40 -0600 Subject: [PATCH 49/49] feat: improve image buffer reading performance by roughly 10x --- libcamera-bridge/core.hpp | 2 ++ libcamera-bridge/memory_buffer.cpp | 11 +++++++++++ src/bridge.rs | 2 ++ src/camera.rs | 8 ++++++-- src/controls.rs | 2 +- 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/libcamera-bridge/core.hpp b/libcamera-bridge/core.hpp index 468c3bc..6899666 100644 --- a/libcamera-bridge/core.hpp +++ b/libcamera-bridge/core.hpp @@ -238,6 +238,8 @@ struct MemoryBuffer { BindMemoryBuffer sub_buffer(size_t offset, size_t length); [[nodiscard]] rust::Vec read_to_vec() const; + size_t get_len() const; + size_t read_to_mut_slice(rust::Slice buf) const; }; struct Request { diff --git a/libcamera-bridge/memory_buffer.cpp b/libcamera-bridge/memory_buffer.cpp index 11e5532..08447f5 100644 --- a/libcamera-bridge/memory_buffer.cpp +++ b/libcamera-bridge/memory_buffer.cpp @@ -1,5 +1,8 @@ #include "core.hpp" +#include +#include + #include "libcamera-rs/src/bridge.rs.h" BindMemoryBuffer MemoryBuffer::sub_buffer(size_t offset, size_t length) { @@ -23,3 +26,11 @@ rust::Vec MemoryBuffer::read_to_vec() const { } return buf; } + +size_t MemoryBuffer::get_len() const { return this->length; } + +size_t MemoryBuffer::read_to_mut_slice(rust::Slice buf) const { + size_t len_to_read = std::min(this->length, buf.size()); + memcpy(buf.data(), this->pointer, len_to_read); + return len_to_read; +} diff --git a/src/bridge.rs b/src/bridge.rs index 00e361e..d81a6c6 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -335,6 +335,8 @@ pub mod ffi { length: usize, ) -> Result; pub unsafe fn read_to_vec(self: &MemoryBuffer) -> Vec; + pub unsafe fn get_len(self: &MemoryBuffer) -> usize; + pub unsafe fn read_to_mut_slice(self: &MemoryBuffer, buf: &mut [u8]) -> usize; type Request; pub unsafe fn add_buffer( diff --git a/src/camera.rs b/src/camera.rs index 203ad2a..b559c2a 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -511,12 +511,16 @@ impl ImageBuffer { /// /// This function is *slow* especially in a debug build. pub fn read_image(self, cam: &Camera<'_>) -> RawCameraImage { - trace!("Reading image from buffer..."); let start = Instant::now(); let planes = cam.streams[self.stream_id].buffers[self.buffer_id] .planes .iter() - .map(|plane| unsafe { plane.get().read_to_vec() }) + .map(|plane| { + //let buf = unsafe { plane.get().read_to_vec() }; + let mut buf = vec![0; unsafe { plane.get().get_len() }]; + unsafe { plane.get().read_to_mut_slice(&mut buf) }; + buf + }) .collect(); debug!("Read image from buffer in {:?}", start.elapsed()); RawCameraImage { diff --git a/src/controls.rs b/src/controls.rs index 740a247..1eaec4c 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::fmt::Debug; use std::ops::RangeInclusive; -use log::{error, trace, warn}; +use log::{error, warn}; use crate::bridge::{ffi, GetInner}; use crate::{LibcameraError, Result};