Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement base libcamera bindings #2

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
119769d
chore: Style and lint checking
VedaRePowered Jun 15, 2022
2625e72
More bindings
VedaRePowered Jun 15, 2022
9656d57
feat: Signal callbacks and frame buffer allocator
VedaRePowered Jun 15, 2022
f286961
chore: Minor restructure and cleanup
VedaRePowered Jun 15, 2022
c4e7444
feat: Allocate Buffers
VedaRePowered Jun 15, 2022
b2bbcfb
More work on getting frame buffers to work
VedaRePowered Jun 16, 2022
174b83b
Get everything but queue_camera_request working
VedaRePowered Jun 16, 2022
96ba58b
fix: Refactor to fix use-after-free
VedaRePowered Jun 21, 2022
097148a
feat: Capture images
VedaRePowered Jun 21, 2022
6ea5b09
feat: Camera Configuration
VedaRePowered Jun 22, 2022
d0411c2
chore: setup clang-tidy
VedaRePowered Jun 29, 2022
9f27054
chore: make functions that can be const const
VedaRePowered Jun 29, 2022
3ef26ca
feat: more work on the safe API
VedaRePowered Jun 30, 2022
0a76b6a
feat: start capture function on camera
VedaRePowered Jun 30, 2022
6f81cf3
feat: polling events from the camera
VedaRePowered Jun 30, 2022
acc56e9
feat: camera events
VedaRePowered Jul 4, 2022
912dac0
feat: capture images via safe api
VedaRePowered Jul 4, 2022
02d7e1d
feat: camera control values
VedaRePowered Jul 5, 2022
c0d5cf1
Add camera controls
VedaRePowered Jul 6, 2022
2cf4874
feat: image decoding
VedaRePowered Jul 6, 2022
401603a
feat: makefile improvements.
VedaRePowered Jul 6, 2022
3b8d494
chore: clippy fix
VedaRePowered Jul 6, 2022
c1354d7
chore: doc comments and cleanup
VedaRePowered Jul 6, 2022
1b7267b
feat: changes for ps-native integration
VedaRePowered Jul 8, 2022
9c3cf2e
fix: fix c++ integer type bit widths
VedaRePowered Jul 8, 2022
c3d7f75
chore: remove unused submodules
VedaRePowered Jul 8, 2022
e588ca3
fix: update the framebuffer api to match libcamera changes
VedaRePowered Jul 8, 2022
183e415
fix: more c++ integer type bit widths
VedaRePowered Jul 8, 2022
0674abc
fix: cleanup and fixes
VedaRePowered Jul 11, 2022
7110593
Fix weird issue
VedaRePowered Jul 19, 2022
47996db
feat: stopping the camera stream
VedaRePowered Jul 19, 2022
f49a3f9
feat: get camera control valid values
VedaRePowered Jul 19, 2022
b53eb5b
feat: control mode enums
VedaRePowered Jul 20, 2022
eae2784
feat: fix colour gains control
VedaRePowered Jul 20, 2022
799f981
fix: fix array controls
VedaRePowered Jul 20, 2022
140d98d
fix: fix weird controls with the wrong type
VedaRePowered Jul 20, 2022
cef10a0
fix: more control value fixes
VedaRePowered Jul 20, 2022
69f007c
fix: impl send for error
VedaRePowered Jul 20, 2022
e4ca6cd
fix: minor fixes for raspberry pi
VedaRePowered Jul 22, 2022
38135fb
fix: general fixes
VedaRePowered Jul 25, 2022
9a768b0
chore: cleaning up
VedaRePowered Jul 28, 2022
f86b167
fix: fix colour decoding
VedaRePowered Jul 29, 2022
4f3cbe9
fix: don't segfault when dropping a camera with outstanding requests
VedaRePowered Aug 9, 2022
876788a
chore: add another test
VedaRePowered Aug 10, 2022
3034572
feat: Load NV12 images
VedaRePowered Aug 17, 2022
3e6c5a2
fix: use chunks_exact instead of chunks to prevent a potential panic …
VedaRePowered Aug 18, 2022
1b03dfb
chore: add license
VedaRePowered Aug 18, 2022
ef03528
chore: disable noisy logging
VedaRePowered Aug 18, 2022
2c0be51
feat: improve image buffer reading performance by roughly 10x
VedaRePowered Aug 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
2 changes: 2 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
BasedOnStyle: LLVM
IndentWidth: 2
13 changes: 13 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -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"
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
.idea
.vscode
*.bak~

/target
Cargo.lock

plane_*.bin
image_*.png
tmp.bgr
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +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
1 change: 1 addition & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tab_spaces = 2
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
[package]
name = "libcamera-rs"
description = "Libcamera bindings for rust."
authors = ["John Brandt <[email protected]>", "Ben Heard <[email protected]>"]
version = "0.1.0"
edition = "2021"
license = "ISC"

[dependencies]
cxx = "1.0"
thiserror = "1"
log = "0.4"
image = { version = "0.24", optional = true }

[build-dependencies]
cxx-build = "1.0"

[dev-dependencies]
lazy_static = "1"
simplelog = "0.12"

[features]
default = [ "image" ]
46 changes: 35 additions & 11 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,15 +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() {
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");
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}");
}

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");
cxx_build::bridge("src/bridge.rs")
.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");

// link libcamera
println!("cargo:rustc-link-lib=dylib=camera");
println!("cargo:rustc-link-lib=dylib=camera-base");
// 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");
}
184 changes: 184 additions & 0 deletions libcamera-bridge/camera.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#include "core.hpp"

#include "libcamera-rs/src/bridge.rs.h"

Camera::Camera(std::shared_ptr<libcamera::Camera> inner_)
: inner{std::move(inner_)} {
this->inner->bufferCompleted.connect(
this,
[&](libcamera::Request *request, libcamera::FrameBuffer *framebuffer) {
this->message_mutex.lock();
this->message_queue.push(CameraMessage{
.message_type = CameraMessageType::BufferComplete,
.request_cookie = request->cookie(),
.buffer_cookie = framebuffer->cookie(),
});
this->message_mutex.unlock();
});
this->inner->requestCompleted.connect(this, [&](libcamera::Request *request) {
this->message_mutex.lock();
this->message_queue.push(CameraMessage{
.message_type = CameraMessageType::RequestComplete,
.request_cookie = request->cookie(),
.buffer_cookie = 0,
});
this->message_mutex.unlock();
});
}

Camera::~Camera() {
this->inner->bufferCompleted.disconnect();
this->inner->requestCompleted.disconnect();
}

std::shared_ptr<libcamera::Camera> Camera::into_shared() {
VALIDATE_POINTERS()

return this->inner;
}

void Camera::acquire() {
VALIDATE_POINTERS()

int ret = this->inner->acquire();
if (ret < 0) {
throw error_from_code(-ret);
}
}

void Camera::release() {
VALIDATE_POINTERS()

int ret = this->inner->release();
if (ret < 0) {
throw error_from_code(-ret);
}
}

BindCameraConfiguration
Camera::generate_configuration(rust::Slice<const libcamera::StreamRole> roles) {
VALIDATE_POINTERS()

std::vector<libcamera::StreamRole> roles_vec;
for (libcamera::StreamRole role : roles) {
roles_vec.push_back(role);
}

std::unique_ptr<libcamera::CameraConfiguration> conf =
this->inner->generateConfiguration(roles_vec);
if (!conf) {
throw error_from_code(ENODEV);
}

BindCameraConfiguration bind_conf{
.inner = std::make_unique<CameraConfiguration>(std::move(conf)),
};
return bind_conf;
}

void Camera::configure(CameraConfiguration &conf) {
VALIDATE_POINTERS()

int ret = this->inner->configure(conf.into_ptr());
if (ret < 0) {
throw error_from_code(-ret);
}
}

BindRequest Camera::create_request(uint64_t cookie) {
VALIDATE_POINTERS()

std::unique_ptr<libcamera::Request> req = this->inner->createRequest(cookie);
if (!req) {
throw error_from_code(ENODEV);
}

BindRequest bind_req{
.inner = std::make_unique<Request>(std::move(req)),
};
return bind_req;
}

void Camera::queue_request(Request &req) {
VALIDATE_POINTERS()

int ret = this->inner->queueRequest(req.into_ptr());
if (ret < 0) {
throw error_from_code(-ret);
}
}

void Camera::start() {
VALIDATE_POINTERS()

int ret = this->inner->start();
if (ret < 0) {
throw error_from_code(-ret);
}
}

void Camera::stop() {
VALIDATE_POINTERS()

int ret = this->inner->stop();
if (ret < 0) {
throw error_from_code(-ret);
}
}

rust::Vec<ControlPair> Camera::get_controls() const {
VALIDATE_POINTERS()

rust::Vec<ControlPair> controls;
for (const auto &[control, value] : this->inner->controls()) {
rust::Vec<BindControlValue> possible_values;
for (const auto &value : value.values()) {
BindControlValue bind_value{
.inner = std::make_unique<ControlValue>(value),
};
possible_values.push_back(std::move(bind_value));
}
ControlPair control_pair{
.id =
{
.inner = std::make_unique<ControlId>(control),
},
.min =
{
.inner = std::make_unique<ControlValue>(value.min()),
},
.max =
{
.inner = std::make_unique<ControlValue>(value.max()),
},
.value =
{
.inner = std::make_unique<ControlValue>(value.def()),
},
.valid_values = std::move(possible_values),
};
controls.push_back(std::move(control_pair));
}
return controls;
}

rust::Vec<CameraMessage> Camera::poll_events() {
rust::Vec<CameraMessage> messages;
while (!this->message_queue.empty()) {
messages.push_back(this->message_queue.front());
message_queue.pop();
}
return messages;
}

rust::Vec<CameraMessage>
Camera::poll_events_with_cookie(uint64_t request_cookie) {
rust::Vec<CameraMessage> 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;
}
34 changes: 34 additions & 0 deletions libcamera-bridge/camera_configuration.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "core.hpp"

#include "libcamera-rs/src/bridge.rs.h"

libcamera::CameraConfiguration *CameraConfiguration::into_ptr() {
VALIDATE_POINTERS()

return this->inner.get();
}

size_t CameraConfiguration::size() const {
VALIDATE_POINTERS()

return this->inner->size();
}

BindStreamConfiguration CameraConfiguration::at(uint32_t idx) {
VALIDATE_POINTERS()

libcamera::StreamConfiguration *str = &this->inner->at(idx);
if (str == nullptr) {
throw std::runtime_error("No stream configuration with specified id.");
}
BindStreamConfiguration conf{
.inner = std::make_unique<StreamConfiguration>(str),
};
return conf;
}

CameraConfigurationStatus CameraConfiguration::validate() {
VALIDATE_POINTERS()

return this->inner->validate();
}
53 changes: 53 additions & 0 deletions libcamera-bridge/camera_manager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "core.hpp"

#include "libcamera-rs/src/bridge.rs.h"

#include <bits/stdc++.h>

BindCameraManager make_camera_manager() {
BindCameraManager manager{
.inner = std::make_unique<CameraManager>(
std::make_unique<libcamera::CameraManager>()),
};
return manager;
}

void CameraManager::start() {
VALIDATE_POINTERS()

int ret = this->inner->start();
if (ret < 0) {
throw error_from_code(-ret);
}
}

void CameraManager::stop() {
VALIDATE_POINTERS()

this->inner->stop();
}

rust::Vec<rust::String> CameraManager::get_camera_ids() const {
VALIDATE_POINTERS()

rust::Vec<rust::String> camera_ids;
for (std::shared_ptr<libcamera::Camera> cam : this->inner->cameras()) {
camera_ids.push_back(cam->id());
}
return camera_ids;
}

BindCamera CameraManager::get_camera_by_id(rust::Str rust_id) {
VALIDATE_POINTERS()

std::string cam_id = std::string(rust_id);
std::shared_ptr<libcamera::Camera> cam = this->inner->get(cam_id);
// 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{
.inner = std::make_unique<Camera>(cam),
};
return bind_cam;
}
Loading