From 151cd8c7ca0f3bf5e014ff8120f02146a8c962d4 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 25 Oct 2024 10:02:24 -0400 Subject: [PATCH 01/54] Add larod-sys crate --- Cargo.lock | 15 +++++++++++++++ Cargo.toml | 2 ++ crates/larod-sys/Cargo.toml | 11 +++++++++++ crates/larod-sys/build.rs | 23 +++++++++++++++++++++++ crates/larod-sys/src/bindings.rs | 3 +++ crates/larod-sys/src/lib.rs | 14 ++++++++++++++ crates/larod-sys/wrapper.h | 1 + crates/larod/Cargo.toml | 8 ++++++++ crates/larod/src/lib.rs | 14 ++++++++++++++ 9 files changed, 91 insertions(+) create mode 100644 crates/larod-sys/Cargo.toml create mode 100644 crates/larod-sys/build.rs create mode 100644 crates/larod-sys/src/bindings.rs create mode 100644 crates/larod-sys/src/lib.rs create mode 100644 crates/larod-sys/wrapper.h create mode 100644 crates/larod/Cargo.toml create mode 100644 crates/larod/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 15d29792..07a109e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1236,6 +1236,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "larod" +version = "0.1.0" +dependencies = [ + "larod-sys", +] + +[[package]] +name = "larod-sys" +version = "0.1.0" +dependencies = [ + "bindgen", + "pkg-config", +] + [[package]] name = "lazy_static" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index 3d718f16..0bda46b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,8 @@ axstorage = { path = "crates/axstorage" } axstorage-sys = { path = "crates/axstorage-sys" } bbox = { path = "crates/bbox" } bbox-sys = { path = "crates/bbox-sys" } +larod = { path = "crates/larod" } +larod-sys = { path = "crates/larod-sys" } licensekey = { path = "crates/licensekey" } licensekey-sys = { path = "crates/licensekey-sys" } mdb = { path = "crates/mdb" } diff --git a/crates/larod-sys/Cargo.toml b/crates/larod-sys/Cargo.toml new file mode 100644 index 00000000..9e518dde --- /dev/null +++ b/crates/larod-sys/Cargo.toml @@ -0,0 +1,11 @@ +[package] +build = "build.rs" +name = "larod-sys" +version = "0.1.0" +edition.workspace = true + +[build-dependencies] +bindgen = { workspace = true } +pkg-config = { workspace = true } + +[dependencies] diff --git a/crates/larod-sys/build.rs b/crates/larod-sys/build.rs new file mode 100644 index 00000000..4485f5ec --- /dev/null +++ b/crates/larod-sys/build.rs @@ -0,0 +1,23 @@ +use std::{env, path}; + +fn populated_bindings(dst: &path::PathBuf) { + let library = pkg_config::Config::new().probe("liblarod").unwrap(); + let mut bindings = bindgen::Builder::default() + .header("wrapper.h") + .allowlist_recursively(false) + .allowlist_function("^(larod.*)$") + .allowlist_type("^(_?larod.*)$") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .layout_tests(false); + for path in library.include_paths { + bindings = bindings.clang_args(&["-F", path.to_str().unwrap()]); + } + bindings.generate().unwrap().write_to_file(dst).unwrap(); +} + +fn main() { + let dst = path::PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); + if env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default() != "x86_64" { + populated_bindings(&dst); + } +} diff --git a/crates/larod-sys/src/bindings.rs b/crates/larod-sys/src/bindings.rs new file mode 100644 index 00000000..2efefc0a --- /dev/null +++ b/crates/larod-sys/src/bindings.rs @@ -0,0 +1,3 @@ +/* automatically generated by rust-bindgen 0.69.4 */ + + diff --git a/crates/larod-sys/src/lib.rs b/crates/larod-sys/src/lib.rs new file mode 100644 index 00000000..b93cf3ff --- /dev/null +++ b/crates/larod-sys/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/larod-sys/wrapper.h b/crates/larod-sys/wrapper.h new file mode 100644 index 00000000..bda0612d --- /dev/null +++ b/crates/larod-sys/wrapper.h @@ -0,0 +1 @@ +#include diff --git a/crates/larod/Cargo.toml b/crates/larod/Cargo.toml new file mode 100644 index 00000000..f00f6840 --- /dev/null +++ b/crates/larod/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "larod" +version = "0.1.0" +edition.workspace = true + +[dependencies] + +larod-sys = { workspace = true } diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs new file mode 100644 index 00000000..b93cf3ff --- /dev/null +++ b/crates/larod/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 8d3defa45e1c55e3fe16b45773312632e225e3b2 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 25 Oct 2024 11:31:09 -0400 Subject: [PATCH 02/54] Add bindings. --- crates/larod-sys/src/bindings.rs | 552 ++++++++++++++++++++++++++++++- 1 file changed, 551 insertions(+), 1 deletion(-) diff --git a/crates/larod-sys/src/bindings.rs b/crates/larod-sys/src/bindings.rs index 2efefc0a..6ea38066 100644 --- a/crates/larod-sys/src/bindings.rs +++ b/crates/larod-sys/src/bindings.rs @@ -1,3 +1,553 @@ /* automatically generated by rust-bindgen 0.69.4 */ - +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct larodDevice { + _unused: [u8; 0], +} +pub const larodAccess_LAROD_ACCESS_INVALID: larodAccess = 0; +pub const larodAccess_LAROD_ACCESS_PRIVATE: larodAccess = 1; +pub const larodAccess_LAROD_ACCESS_PUBLIC: larodAccess = 2; +pub type larodAccess = ::std::os::raw::c_uint; +pub const larodErrorCode_LAROD_ERROR_NONE: larodErrorCode = 0; +pub const larodErrorCode_LAROD_ERROR_JOB: larodErrorCode = -1; +pub const larodErrorCode_LAROD_ERROR_LOAD_MODEL: larodErrorCode = -2; +pub const larodErrorCode_LAROD_ERROR_FD: larodErrorCode = -3; +pub const larodErrorCode_LAROD_ERROR_MODEL_NOT_FOUND: larodErrorCode = -4; +pub const larodErrorCode_LAROD_ERROR_PERMISSION: larodErrorCode = -5; +pub const larodErrorCode_LAROD_ERROR_CONNECTION: larodErrorCode = -6; +pub const larodErrorCode_LAROD_ERROR_CREATE_SESSION: larodErrorCode = -7; +pub const larodErrorCode_LAROD_ERROR_KILL_SESSION: larodErrorCode = -8; +pub const larodErrorCode_LAROD_ERROR_INVALID_CHIP_ID: larodErrorCode = -9; +pub const larodErrorCode_LAROD_ERROR_INVALID_ACCESS: larodErrorCode = -10; +pub const larodErrorCode_LAROD_ERROR_DELETE_MODEL: larodErrorCode = -11; +pub const larodErrorCode_LAROD_ERROR_TENSOR_MISMATCH: larodErrorCode = -12; +pub const larodErrorCode_LAROD_ERROR_VERSION_MISMATCH: larodErrorCode = -13; +pub const larodErrorCode_LAROD_ERROR_ALLOC: larodErrorCode = -14; +pub const larodErrorCode_LAROD_ERROR_POWER_NOT_AVAILABLE: larodErrorCode = -15; +pub const larodErrorCode_LAROD_ERROR_MAX_ERRNO: larodErrorCode = 1024; +pub type larodErrorCode = ::std::os::raw::c_int; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_INVALID: larodTensorDataType = 0; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_UNSPECIFIED: larodTensorDataType = 1; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_BOOL: larodTensorDataType = 2; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_UINT8: larodTensorDataType = 3; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_INT8: larodTensorDataType = 4; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_UINT16: larodTensorDataType = 5; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_INT16: larodTensorDataType = 6; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_UINT32: larodTensorDataType = 7; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_INT32: larodTensorDataType = 8; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_UINT64: larodTensorDataType = 9; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_INT64: larodTensorDataType = 10; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_FLOAT16: larodTensorDataType = 11; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_FLOAT32: larodTensorDataType = 12; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_FLOAT64: larodTensorDataType = 13; +pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_MAX: larodTensorDataType = 14; +pub type larodTensorDataType = ::std::os::raw::c_uint; +pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_INVALID: larodTensorLayout = 0; +pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_UNSPECIFIED: larodTensorLayout = 1; +pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_NHWC: larodTensorLayout = 2; +pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_NCHW: larodTensorLayout = 3; +pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_420SP: larodTensorLayout = 4; +pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_MAX: larodTensorLayout = 5; +pub type larodTensorLayout = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct larodError { + pub code: larodErrorCode, + pub msg: *const ::std::os::raw::c_char, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct larodTensorDims { + pub dims: [usize; 12usize], + pub len: usize, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct larodTensorPitches { + pub pitches: [usize; 12usize], + pub len: usize, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct larodModel { + _unused: [u8; 0], +} +pub type larodLoadModelCallback = ::std::option::Option< + unsafe extern "C" fn( + model: *mut larodModel, + userData: *mut ::std::os::raw::c_void, + error: *mut larodError, + ), +>; +pub type larodRunJobCallback = ::std::option::Option< + unsafe extern "C" fn(userData: *mut ::std::os::raw::c_void, error: *mut larodError), +>; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct larodConnection { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct larodJobRequest { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct larodTensor { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct larodMap { + _unused: [u8; 0], +} +extern "C" { + pub fn larodClearError(error: *mut *mut larodError); +} +extern "C" { + pub fn larodConnect(conn: *mut *mut larodConnection, error: *mut *mut larodError) -> bool; +} +extern "C" { + pub fn larodDisconnect(conn: *mut *mut larodConnection, error: *mut *mut larodError) -> bool; +} +extern "C" { + pub fn larodGetNumSessions( + conn: *mut larodConnection, + numSessions: *mut u64, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetDevice( + conn: *const larodConnection, + name: *const ::std::os::raw::c_char, + instance: u32, + error: *mut *mut larodError, + ) -> *const larodDevice; +} +extern "C" { + pub fn larodGetDeviceName( + dev: *const larodDevice, + error: *mut *mut larodError, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn larodGetDeviceInstance( + dev: *const larodDevice, + instance: *mut u32, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodListDevices( + conn: *mut larodConnection, + numDevices: *mut usize, + error: *mut *mut larodError, + ) -> *mut *const larodDevice; +} +extern "C" { + pub fn larodLoadModel( + conn: *mut larodConnection, + fd: ::std::os::raw::c_int, + dev: *const larodDevice, + access: larodAccess, + name: *const ::std::os::raw::c_char, + params: *const larodMap, + error: *mut *mut larodError, + ) -> *mut larodModel; +} +extern "C" { + pub fn larodLoadModelAsync( + conn: *mut larodConnection, + inFd: ::std::os::raw::c_int, + dev: *const larodDevice, + access: larodAccess, + name: *const ::std::os::raw::c_char, + params: *const larodMap, + callback: larodLoadModelCallback, + userData: *mut ::std::os::raw::c_void, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetModel( + conn: *mut larodConnection, + modelId: u64, + error: *mut *mut larodError, + ) -> *mut larodModel; +} +extern "C" { + pub fn larodGetModels( + conn: *mut larodConnection, + numModels: *mut usize, + error: *mut *mut larodError, + ) -> *mut *mut larodModel; +} +extern "C" { + pub fn larodDestroyModel(model: *mut *mut larodModel); +} +extern "C" { + pub fn larodDestroyModels(models: *mut *mut *mut larodModel, numModels: usize); +} +extern "C" { + pub fn larodDeleteModel( + conn: *mut larodConnection, + model: *mut larodModel, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetModelId(model: *const larodModel, error: *mut *mut larodError) -> u64; +} +extern "C" { + pub fn larodGetModelDevice( + model: *const larodModel, + error: *mut *mut larodError, + ) -> *const larodDevice; +} +extern "C" { + pub fn larodGetModelSize(model: *const larodModel, error: *mut *mut larodError) -> usize; +} +extern "C" { + pub fn larodGetModelName( + model: *const larodModel, + error: *mut *mut larodError, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn larodGetModelAccess( + model: *const larodModel, + error: *mut *mut larodError, + ) -> larodAccess; +} +extern "C" { + pub fn larodGetModelNumInputs(model: *const larodModel, error: *mut *mut larodError) -> usize; +} +extern "C" { + pub fn larodGetModelNumOutputs(model: *const larodModel, error: *mut *mut larodError) -> usize; +} +extern "C" { + pub fn larodGetModelInputByteSizes( + model: *const larodModel, + numInputs: *mut usize, + error: *mut *mut larodError, + ) -> *mut usize; +} +extern "C" { + pub fn larodGetModelOutputByteSizes( + model: *const larodModel, + numOutputs: *mut usize, + error: *mut *mut larodError, + ) -> *mut usize; +} +extern "C" { + pub fn larodCreateModelInputs( + model: *const larodModel, + numTensors: *mut usize, + error: *mut *mut larodError, + ) -> *mut *mut larodTensor; +} +extern "C" { + pub fn larodCreateModelOutputs( + model: *const larodModel, + numTensors: *mut usize, + error: *mut *mut larodError, + ) -> *mut *mut larodTensor; +} +extern "C" { + pub fn larodAllocModelInputs( + conn: *mut larodConnection, + model: *const larodModel, + fdPropFlags: u32, + numTensors: *mut usize, + params: *mut larodMap, + error: *mut *mut larodError, + ) -> *mut *mut larodTensor; +} +extern "C" { + pub fn larodAllocModelOutputs( + conn: *mut larodConnection, + model: *const larodModel, + fdPropFlags: u32, + numTensors: *mut usize, + params: *mut larodMap, + error: *mut *mut larodError, + ) -> *mut *mut larodTensor; +} +extern "C" { + pub fn larodCreateTensors( + numTensors: usize, + error: *mut *mut larodError, + ) -> *mut *mut larodTensor; +} +extern "C" { + pub fn larodDestroyTensors( + conn: *mut larodConnection, + tensors: *mut *mut *mut larodTensor, + numTensors: usize, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodSetTensorDims( + tensor: *mut larodTensor, + dims: *const larodTensorDims, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetTensorDims( + tensor: *const larodTensor, + error: *mut *mut larodError, + ) -> *const larodTensorDims; +} +extern "C" { + pub fn larodSetTensorPitches( + tensor: *mut larodTensor, + pitches: *const larodTensorPitches, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetTensorPitches( + tensor: *const larodTensor, + error: *mut *mut larodError, + ) -> *const larodTensorPitches; +} +extern "C" { + pub fn larodSetTensorDataType( + tensor: *mut larodTensor, + dataType: larodTensorDataType, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetTensorDataType( + tensor: *const larodTensor, + error: *mut *mut larodError, + ) -> larodTensorDataType; +} +extern "C" { + pub fn larodSetTensorLayout( + tensor: *mut larodTensor, + layout: larodTensorLayout, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetTensorLayout( + tensor: *const larodTensor, + error: *mut *mut larodError, + ) -> larodTensorLayout; +} +extern "C" { + pub fn larodSetTensorFd( + tensor: *mut larodTensor, + fd: ::std::os::raw::c_int, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetTensorFd( + tensor: *const larodTensor, + error: *mut *mut larodError, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn larodSetTensorFdSize( + tensor: *mut larodTensor, + size: usize, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetTensorFdSize( + tensor: *const larodTensor, + size: *mut usize, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodSetTensorFdOffset( + tensor: *mut larodTensor, + offset: i64, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetTensorFdOffset(tensor: *const larodTensor, error: *mut *mut larodError) -> i64; +} +extern "C" { + pub fn larodTrackTensor( + conn: *mut larodConnection, + tensor: *mut larodTensor, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodSetTensorFdProps( + tensor: *mut larodTensor, + fdPropFlags: u32, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetTensorFdProps( + tensor: *const larodTensor, + fdPropFlags: *mut u32, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodGetTensorName( + tensor: *const larodTensor, + error: *mut *mut larodError, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn larodGetTensorByteSize( + tensor: *const larodTensor, + byteSize: *mut usize, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodCreateMap(error: *mut *mut larodError) -> *mut larodMap; +} +extern "C" { + pub fn larodDestroyMap(map: *mut *mut larodMap); +} +extern "C" { + pub fn larodMapSetStr( + map: *mut larodMap, + key: *const ::std::os::raw::c_char, + value: *const ::std::os::raw::c_char, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodMapSetInt( + map: *mut larodMap, + key: *const ::std::os::raw::c_char, + value: i64, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodMapSetIntArr2( + map: *mut larodMap, + key: *const ::std::os::raw::c_char, + value0: i64, + value1: i64, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodMapSetIntArr4( + map: *mut larodMap, + key: *const ::std::os::raw::c_char, + value0: i64, + value1: i64, + value2: i64, + value3: i64, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodMapGetStr( + map: *mut larodMap, + key: *const ::std::os::raw::c_char, + error: *mut *mut larodError, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn larodMapGetInt( + map: *mut larodMap, + key: *const ::std::os::raw::c_char, + value: *mut i64, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodMapGetIntArr2( + map: *mut larodMap, + key: *const ::std::os::raw::c_char, + error: *mut *mut larodError, + ) -> *const i64; +} +extern "C" { + pub fn larodMapGetIntArr4( + map: *mut larodMap, + key: *const ::std::os::raw::c_char, + error: *mut *mut larodError, + ) -> *const i64; +} +extern "C" { + pub fn larodCreateJobRequest( + model: *const larodModel, + inputTensors: *mut *mut larodTensor, + numInputs: usize, + outputTensors: *mut *mut larodTensor, + numOutputs: usize, + params: *mut larodMap, + error: *mut *mut larodError, + ) -> *mut larodJobRequest; +} +extern "C" { + pub fn larodDestroyJobRequest(jobReq: *mut *mut larodJobRequest); +} +extern "C" { + pub fn larodSetJobRequestModel( + jobReq: *mut larodJobRequest, + model: *const larodModel, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodSetJobRequestInputs( + jobReq: *mut larodJobRequest, + tensors: *mut *mut larodTensor, + numTensors: usize, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodSetJobRequestOutputs( + jobReq: *mut larodJobRequest, + tensors: *mut *mut larodTensor, + numTensors: usize, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodSetJobRequestPriority( + jobReq: *mut larodJobRequest, + priority: u8, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodSetJobRequestParams( + jobReq: *mut larodJobRequest, + params: *const larodMap, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodRunJob( + conn: *mut larodConnection, + jobReq: *const larodJobRequest, + error: *mut *mut larodError, + ) -> bool; +} +extern "C" { + pub fn larodRunJobAsync( + conn: *mut larodConnection, + jobReq: *const larodJobRequest, + callback: larodRunJobCallback, + userData: *mut ::std::os::raw::c_void, + error: *mut *mut larodError, + ) -> bool; +} From 709d58ce5fd6f7d68ad25925bde154eeb3b244b9 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Sat, 26 Oct 2024 11:59:06 -0400 Subject: [PATCH 03/54] Drop back to ACAP SDK 1.3. Get larod crate fleshed out a little to do testing. --- .devcontainer/Dockerfile | 8 ++-- .devhost/install-rust.sh | 4 +- .devhost/install-sdk.sh | 4 +- .devhost/install-venv.sh | 10 ++--- crates/larod-sys/src/lib.rs | 17 ++----- crates/larod/src/lib.rs | 89 ++++++++++++++++++++++++++++++++++--- 6 files changed, 101 insertions(+), 31 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 44fdcbf9..f9193a62 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,12 +2,12 @@ ARG REPO=axisecp ARG SDK=acap-native-sdk ARG UBUNTU_VERSION=22.04 # Keep in sync with `install-sdk.sh`. -ARG VERSION=1.15 +ARG VERSION=1.3 ARG BASE_IMAGE=debian:bookworm-20240423 -FROM --platform=linux/amd64 ${REPO}/${SDK}:${VERSION}-aarch64-ubuntu${UBUNTU_VERSION} AS sdk-aarch64 -FROM --platform=linux/amd64 ${REPO}/${SDK}:${VERSION}-armv7hf-ubuntu${UBUNTU_VERSION} AS sdk-armv7hf -FROM --platform=linux/amd64 ${BASE_IMAGE} +FROM --platform=linux/arm64 ${REPO}/${SDK}:${VERSION}-aarch64-ubuntu${UBUNTU_VERSION} AS sdk-aarch64 +FROM --platform=linux/arm64 ${REPO}/${SDK}:${VERSION}-armv7hf-ubuntu${UBUNTU_VERSION} AS sdk-armv7hf +FROM --platform=linux/arm64 ${BASE_IMAGE} COPY --from=sdk-aarch64 /opt/axis/acapsdk/axis-acap-manifest-tools /opt/axis/acapsdk/axis-acap-manifest-tools COPY --from=sdk-aarch64 /opt/axis/acapsdk/environment-setup-cortexa53-crypto-poky-linux /opt/axis/acapsdk/environment-setup-cortexa53-crypto-poky-linux diff --git a/.devhost/install-rust.sh b/.devhost/install-rust.sh index c0b7baf8..0f2ebf8c 100755 --- a/.devhost/install-rust.sh +++ b/.devhost/install-rust.sh @@ -3,9 +3,9 @@ set -eux curl \ --output /tmp/rustup-init \ - "https://static.rust-lang.org/rustup/archive/1.26.0/x86_64-unknown-linux-gnu/rustup-init" + "https://static.rust-lang.org/rustup/archive/1.26.0/aarch64-unknown-linux-gnu/rustup-init" -echo "0b2f6c8f85a3d02fde2efc0ced4657869d73fccfce59defb4e8d29233116e6db /tmp/rustup-init" \ +echo "673e336c81c65e6b16dcdede33f4cc9ed0f08bde1dbe7a935f113605292dc800 /tmp/rustup-init" \ | sha256sum -c - chmod +x /tmp/rustup-init diff --git a/.devhost/install-sdk.sh b/.devhost/install-sdk.sh index 56bab1e8..36f0d660 100755 --- a/.devhost/install-sdk.sh +++ b/.devhost/install-sdk.sh @@ -5,7 +5,7 @@ set -eux DIRECTORY="${1}" # Keep version in sync with `Dockerfile`. -docker run axisecp/acap-native-sdk:1.15-armv7hf-ubuntu22.04 tar \ +docker run axisecp/acap-native-sdk:1.3-armv7hf-ubuntu22.04 tar \ --create \ --directory /opt/ \ --file - \ @@ -18,7 +18,7 @@ docker run axisecp/acap-native-sdk:1.15-armv7hf-ubuntu22.04 tar \ --strip-components 1 # Keep version in sync with `Dockerfile`. -docker run axisecp/acap-native-sdk:1.15-aarch64-ubuntu22.04 tar \ +docker run axisecp/acap-native-sdk:1.3-aarch64-ubuntu22.04 tar \ --create \ --directory /opt/ \ --file - \ diff --git a/.devhost/install-venv.sh b/.devhost/install-venv.sh index 6c079cd8..40374e42 100755 --- a/.devhost/install-venv.sh +++ b/.devhost/install-venv.sh @@ -21,15 +21,15 @@ PIP_CONSTRAINT=constraints.txt pip install --requirement requirements.txt # automatically. curl \ --location \ - --output /tmp/node-v18.16.1-linux-x64.tar.gz \ - "https://nodejs.org/dist/v18.16.1/node-v18.16.1-linux-x64.tar.gz" + --output /tmp/node-v18.16.1-linux-arm64.tar.gz \ + "https://nodejs.org/dist/v18.16.1/node-v18.16.1-linux-arm64.tar.gz" -echo "59582f51570d0857de6333620323bdeee5ae36107318f86ce5eca24747cabf5b /tmp/node-v18.16.1-linux-x64.tar.gz" \ +echo "555b5c521e068acc976e672978ba0f5b1a0c030192b50639384c88143f4460bc /tmp/node-v18.16.1-linux-arm64.tar.gz" \ | sha256sum -c - -tar -xf "/tmp/node-v18.16.1-linux-x64.tar.gz" --strip-components 1 -C "${VIRTUAL_ENV}" +tar -xf "/tmp/node-v18.16.1-linux-arm64.tar.gz" --strip-components 1 -C "${VIRTUAL_ENV}" -rm /tmp/node-v18.16.1-linux-x64.tar.gz +rm /tmp/node-v18.16.1-linux-arm64.tar.gz # Install `devcontainer` into venv npm install -g @devcontainers/cli@0.65.0 diff --git a/crates/larod-sys/src/lib.rs b/crates/larod-sys/src/lib.rs index b93cf3ff..36fd376f 100644 --- a/crates/larod-sys/src/lib.rs +++ b/crates/larod-sys/src/lib.rs @@ -1,14 +1,5 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +#[cfg(not(target_arch = "x86_64"))] +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +#[cfg(target_arch = "x86_64")] +include!("bindings.rs"); diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index b93cf3ff..21864083 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -1,14 +1,93 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right +use larod_sys::*; +use std::{ffi::CStr, ptr}; + +type Result = std::result::Result; + +// Define our error types. These may be customized for our error handling cases. +// Now we will be able to write our own errors, defer to an underlying error +// implementation, or do something in between. +#[derive(Debug, Clone)] +struct LarodError { + msg: String, + code: LarodErrorCode, +} + +impl From<*mut larodError> for LarodError { + fn from(e: *mut larodError) -> Self { + let le = unsafe { *e }; + let msg: String = unsafe { CStr::from_ptr(le.msg).to_str().unwrap().into() }; + let code: LarodErrorCode = le.code.into(); + Self { msg, code } + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +enum LarodErrorCode { + NONE, + JOB, + LOAD_MODEL, + FD, + MODEL_NOT_FOUND, + PERMISSION, + CONNECTION, + CREATE_SESSION, + KILL_SESSION, + INVALID_CHIP_ID, + INVALID_ACCESS, + DELETE_MODEL, + TENSOR_MISMATCH, + VERSION_MISMATCH, + ALLOC, + POWER_NOT_AVAILABLE, + MAX_ERRNO, +} + +impl From for LarodErrorCode { + fn from(code: larodErrorCode) -> LarodErrorCode { + match code { + larodErrorCode_LAROD_ERROR_NONE => LarodErrorCode::NONE, + larodErrorCode_LAROD_ERROR_JOB => LarodErrorCode::JOB, + larodErrorCode_LAROD_ERROR_LOAD_MODEL => LarodErrorCode::LOAD_MODEL, + larodErrorCode_LAROD_ERROR_FD => LarodErrorCode::FD, + larodErrorCode_LAROD_ERROR_MODEL_NOT_FOUND => LarodErrorCode::MODEL_NOT_FOUND, + larodErrorCode_LAROD_ERROR_PERMISSION => LarodErrorCode::PERMISSION, + larodErrorCode_LAROD_ERROR_CONNECTION => LarodErrorCode::CONNECTION, + larodErrorCode_LAROD_ERROR_CREATE_SESSION => LarodErrorCode::CREATE_SESSION, + larodErrorCode_LAROD_ERROR_KILL_SESSION => LarodErrorCode::KILL_SESSION, + larodErrorCode_LAROD_ERROR_INVALID_CHIP_ID => LarodErrorCode::INVALID_CHIP_ID, + larodErrorCode_LAROD_ERROR_INVALID_ACCESS => LarodErrorCode::INVALID_ACCESS, + larodErrorCode_LAROD_ERROR_DELETE_MODEL => LarodErrorCode::DELETE_MODEL, + larodErrorCode_LAROD_ERROR_TENSOR_MISMATCH => LarodErrorCode::TENSOR_MISMATCH, + larodErrorCode_LAROD_ERROR_VERSION_MISMATCH => LarodErrorCode::VERSION_MISMATCH, + larodErrorCode_LAROD_ERROR_ALLOC => LarodErrorCode::ALLOC, + larodErrorCode_LAROD_ERROR_POWER_NOT_AVAILABLE => LarodErrorCode::POWER_NOT_AVAILABLE, + larodErrorCode_LAROD_ERROR_MAX_ERRNO => LarodErrorCode::MAX_ERRNO, + _ => unreachable!(), + } + } +} + +fn create_map() -> Result<*mut larodMap> { + let mut error: *mut larodError = ptr::null_mut(); + let map: *mut larodMap = unsafe { larodCreateMap(&mut error) }; + println!("map is_null? {:?}", map.is_null()); + println!("error is_null? {:?}", error.is_null()); + let e: LarodError = error.into(); + if !map.is_null() && matches!(e.code, LarodErrorCode::NONE) { + Ok(map) + } else { + Err(e) + } } #[cfg(test)] mod tests { use super::*; + use std::ptr; #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + fn it_creates_larod_map() { + assert!(create_map().is_ok()); } } From e158693dfd171db6c14277d52e348aa4ef4b984e Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Sun, 27 Oct 2024 22:15:25 -0400 Subject: [PATCH 04/54] Swap back to x86_64 --- .devcontainer/Dockerfile | 22 ++++++++++++---------- .devhost/install-rust.sh | 4 ++-- .devhost/install-venv.sh | 10 +++++----- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f9193a62..3af97780 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -5,9 +5,9 @@ ARG UBUNTU_VERSION=22.04 ARG VERSION=1.3 ARG BASE_IMAGE=debian:bookworm-20240423 -FROM --platform=linux/arm64 ${REPO}/${SDK}:${VERSION}-aarch64-ubuntu${UBUNTU_VERSION} AS sdk-aarch64 -FROM --platform=linux/arm64 ${REPO}/${SDK}:${VERSION}-armv7hf-ubuntu${UBUNTU_VERSION} AS sdk-armv7hf -FROM --platform=linux/arm64 ${BASE_IMAGE} +FROM --platform=linux/amd64 ${REPO}/${SDK}:${VERSION}-aarch64-ubuntu${UBUNTU_VERSION} AS sdk-aarch64 +FROM --platform=linux/amd64 ${REPO}/${SDK}:${VERSION}-armv7hf-ubuntu${UBUNTU_VERSION} AS sdk-armv7hf +FROM --platform=linux/amd64 ${BASE_IMAGE} COPY --from=sdk-aarch64 /opt/axis/acapsdk/axis-acap-manifest-tools /opt/axis/acapsdk/axis-acap-manifest-tools COPY --from=sdk-aarch64 /opt/axis/acapsdk/environment-setup-cortexa53-crypto-poky-linux /opt/axis/acapsdk/environment-setup-cortexa53-crypto-poky-linux @@ -25,16 +25,10 @@ ENV \ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-args=--sysroot=${SYSROOT_AARCH64}" \ CC_aarch64_unknown_linux_gnu="aarch64-linux-gnu-gcc" \ CXX_aarch64_unknown_linux_gnu="aarch64-linux-gnu-g++" \ - PKG_CONFIG_LIBDIR_aarch64_unknown_linux_gnu="${SYSROOT_AARCH64}/usr/lib/pkgconfig:${SYSROOT_AARCH64}/usr/share/pkgconfig" \ - PKG_CONFIG_PATH_aarch64_unknown_linux_gnu="${SYSROOT_AARCH64}/usr/lib/pkgconfig:${SYSROOT_AARCH64}/usr/share/pkgconfig" \ - PKG_CONFIG_SYSROOT_DIR_aarch64_unknown_linux_gnu="${SYSROOT_AARCH64}" \ CARGO_TARGET_THUMBV7NEON_UNKNOWN_LINUX_GNUEABIHF_LINKER="arm-linux-gnueabihf-gcc" \ CARGO_TARGET_THUMBV7NEON_UNKNOWN_LINUX_GNUEABIHF_RUSTFLAGS="-C link-args=--sysroot=${SYSROOT_ARMV7HF}" \ CC_thumbv7neon_unknown_linux_gnueabihf="arm-linux-gnueabihf-gcc" \ - CXX_thumbv7neon_unknown_linux_gnueabihf="arm-linux-gnueabihf-g++" \ - PKG_CONFIG_LIBDIR_thumbv7neon_unknown_linux_gnueabihf="${SYSROOT_ARMV7HF}/usr/lib/pkgconfig:${SYSROOT_ARMV7HF}/usr/share/pkgconfig" \ - PKG_CONFIG_PATH_thumbv7neon_unknown_linux_gnueabihf="${SYSROOT_ARMV7HF}/usr/lib/pkgconfig:${SYSROOT_ARMV7HF}/usr/share/pkgconfig" \ - PKG_CONFIG_SYSROOT_DIR_thumbv7neon_unknown_linux_gnueabihf="${SYSROOT_ARMV7HF}" + CXX_thumbv7neon_unknown_linux_gnueabihf="arm-linux-gnueabihf-g++" COPY .devhost/install-system-packages.sh ./ RUN ./install-system-packages.sh \ @@ -61,6 +55,14 @@ RUN --mount=type=bind,target=/context \ && ./install-venv.sh $VIRTUAL_ENV \ && chmod a+w -R $VIRTUAL_ENV +ENV \ + PKG_CONFIG_LIBDIR_aarch64_unknown_linux_gnu="${SYSROOT_AARCH64}/usr/lib/pkgconfig:${SYSROOT_AARCH64}/usr/share/pkgconfig" \ + PKG_CONFIG_PATH_aarch64_unknown_linux_gnu="${SYSROOT_AARCH64}/usr/lib/pkgconfig:${SYSROOT_AARCH64}/usr/share/pkgconfig" \ + PKG_CONFIG_SYSROOT_DIR_aarch64_unknown_linux_gnu="${SYSROOT_AARCH64}" \ + PKG_CONFIG_LIBDIR_thumbv7neon_unknown_linux_gnueabihf="${SYSROOT_ARMV7HF}/usr/lib/pkgconfig:${SYSROOT_ARMV7HF}/usr/share/pkgconfig" \ + PKG_CONFIG_PATH_thumbv7neon_unknown_linux_gnueabihf="${SYSROOT_ARMV7HF}/usr/lib/pkgconfig:${SYSROOT_ARMV7HF}/usr/share/pkgconfig" \ + PKG_CONFIG_SYSROOT_DIR_thumbv7neon_unknown_linux_gnueabihf="${SYSROOT_ARMV7HF}" + # If neither `CARGO_HOME` nor `HOME` is set when launching a container, then cargo will try to # download crates to this directory. If launched with the `--user` option then this will fail. # TODO: Replace the example in the README with something that does not mount any volumes. diff --git a/.devhost/install-rust.sh b/.devhost/install-rust.sh index 0f2ebf8c..c0b7baf8 100755 --- a/.devhost/install-rust.sh +++ b/.devhost/install-rust.sh @@ -3,9 +3,9 @@ set -eux curl \ --output /tmp/rustup-init \ - "https://static.rust-lang.org/rustup/archive/1.26.0/aarch64-unknown-linux-gnu/rustup-init" + "https://static.rust-lang.org/rustup/archive/1.26.0/x86_64-unknown-linux-gnu/rustup-init" -echo "673e336c81c65e6b16dcdede33f4cc9ed0f08bde1dbe7a935f113605292dc800 /tmp/rustup-init" \ +echo "0b2f6c8f85a3d02fde2efc0ced4657869d73fccfce59defb4e8d29233116e6db /tmp/rustup-init" \ | sha256sum -c - chmod +x /tmp/rustup-init diff --git a/.devhost/install-venv.sh b/.devhost/install-venv.sh index 40374e42..6c079cd8 100755 --- a/.devhost/install-venv.sh +++ b/.devhost/install-venv.sh @@ -21,15 +21,15 @@ PIP_CONSTRAINT=constraints.txt pip install --requirement requirements.txt # automatically. curl \ --location \ - --output /tmp/node-v18.16.1-linux-arm64.tar.gz \ - "https://nodejs.org/dist/v18.16.1/node-v18.16.1-linux-arm64.tar.gz" + --output /tmp/node-v18.16.1-linux-x64.tar.gz \ + "https://nodejs.org/dist/v18.16.1/node-v18.16.1-linux-x64.tar.gz" -echo "555b5c521e068acc976e672978ba0f5b1a0c030192b50639384c88143f4460bc /tmp/node-v18.16.1-linux-arm64.tar.gz" \ +echo "59582f51570d0857de6333620323bdeee5ae36107318f86ce5eca24747cabf5b /tmp/node-v18.16.1-linux-x64.tar.gz" \ | sha256sum -c - -tar -xf "/tmp/node-v18.16.1-linux-arm64.tar.gz" --strip-components 1 -C "${VIRTUAL_ENV}" +tar -xf "/tmp/node-v18.16.1-linux-x64.tar.gz" --strip-components 1 -C "${VIRTUAL_ENV}" -rm /tmp/node-v18.16.1-linux-arm64.tar.gz +rm /tmp/node-v18.16.1-linux-x64.tar.gz # Install `devcontainer` into venv npm install -g @devcontainers/cli@0.65.0 From 024e491c855ecbf94e202d95d96716da9b19fd88 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Sun, 27 Oct 2024 22:15:41 -0400 Subject: [PATCH 05/54] Start implementing LarodMap --- crates/larod/src/lib.rs | 142 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 12 deletions(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 21864083..56367655 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -1,5 +1,8 @@ use larod_sys::*; -use std::{ffi::CStr, ptr}; +use std::{ + ffi::{CStr, CString}, + ptr, +}; type Result = std::result::Result; @@ -68,16 +71,101 @@ impl From for LarodErrorCode { } } -fn create_map() -> Result<*mut larodMap> { - let mut error: *mut larodError = ptr::null_mut(); - let map: *mut larodMap = unsafe { larodCreateMap(&mut error) }; - println!("map is_null? {:?}", map.is_null()); - println!("error is_null? {:?}", error.is_null()); - let e: LarodError = error.into(); - if !map.is_null() && matches!(e.code, LarodErrorCode::NONE) { - Ok(map) - } else { - Err(e) +pub struct LarodMap { + raw: *mut larodMap, +} + +impl LarodMap { + fn new() -> Result { + let mut error: *mut larodError = ptr::null_mut(); + let map: *mut larodMap = unsafe { larodCreateMap(&mut error) }; + println!("map is_null? {:?}", map.is_null()); + println!("error is_null? {:?}", error.is_null()); + let e: LarodError = error.into(); + if !map.is_null() && matches!(e.code, LarodErrorCode::NONE) { + Ok(Self { raw: map }) + } else { + Err(e) + } + } + + fn set_string(&mut self, k: &str, v: &str) -> Result<()> { + let Ok(key_cstr) = CString::new(k.as_bytes()) else { + return Err(LarodError { + msg: String::from("Could not allocate set_string key CString"), + code: LarodErrorCode::ALLOC, + }); + }; + let Ok(value_cstr) = CString::new(v.as_bytes()) else { + return Err(LarodError { + msg: String::from("Could not allocate set_string value CString"), + code: LarodErrorCode::ALLOC, + }); + }; + let mut error: *mut larodError = ptr::null_mut(); + let success = + unsafe { larodMapSetStr(self.raw, key_cstr.as_ptr(), value_cstr.as_ptr(), &mut error) }; + if success { + Ok(()) + } else { + Err(error.into()) + } + } + fn set_int(&mut self, k: &str, v: i64) -> Result<()> { + let Ok(key_cstr) = CString::new(k.as_bytes()) else { + return Err(LarodError { + msg: String::from("Could not allocate set_string key CString"), + code: LarodErrorCode::ALLOC, + }); + }; + let mut error: *mut larodError = ptr::null_mut(); + let success = unsafe { larodMapSetInt(self.raw, key_cstr.as_ptr(), v, &mut error) }; + if success { + Ok(()) + } else { + Err(error.into()) + } + } + fn set_int_arr2(&mut self, k: &str, v: (i64, i64)) -> Result<()> { + let Ok(key_cstr) = CString::new(k.as_bytes()) else { + return Err(LarodError { + msg: String::from("Could not allocate set_string key CString"), + code: LarodErrorCode::ALLOC, + }); + }; + let mut error: *mut larodError = ptr::null_mut(); + let success = + unsafe { larodMapSetIntArr2(self.raw, key_cstr.as_ptr(), v.0, v.1, &mut error) }; + if success { + Ok(()) + } else { + Err(error.into()) + } + } + fn set_int_arr4(&mut self, k: &str, v: (i64, i64, i64, i64)) -> Result<()> { + let Ok(key_cstr) = CString::new(k.as_bytes()) else { + return Err(LarodError { + msg: String::from("Could not allocate set_string key CString"), + code: LarodErrorCode::ALLOC, + }); + }; + let mut error: *mut larodError = ptr::null_mut(); + let success = unsafe { + larodMapSetIntArr4(self.raw, key_cstr.as_ptr(), v.0, v.1, v.2, v.3, &mut error) + }; + if success { + Ok(()) + } else { + Err(error.into()) + } + } +} + +impl std::ops::Drop for LarodMap { + fn drop(&mut self) { + unsafe { + larodDestroyMap(&mut self.raw); + } } } @@ -88,6 +176,36 @@ mod tests { #[test] fn it_creates_larod_map() { - assert!(create_map().is_ok()); + assert!(LarodMap::new().is_ok()); + } + + #[test] + fn it_drops_map() { + let map = LarodMap::new().unwrap(); + std::mem::drop(map); + } + + #[test] + fn larod_map_can_set_str() { + let mut map = LarodMap::new().unwrap(); + map.set_string("test_key", "test_value").unwrap(); + } + + #[test] + fn larod_map_can_set_int() { + let mut map = LarodMap::new().unwrap(); + map.set_int("test_key", 10).unwrap(); + } + + #[test] + fn larod_map_can_set_2_tuple() { + let mut map = LarodMap::new().unwrap(); + map.set_int_arr2("test_key", (1, 2)).unwrap(); + } + + #[test] + fn larod_map_can_set_4_tuple() { + let mut map = LarodMap::new().unwrap(); + map.set_int_arr4("test_key", (1, 2, 3, 4)).unwrap(); } } From 56d372043b5c48f2e9af3dac54d03d7b72d137d7 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Mon, 28 Oct 2024 22:21:01 -0400 Subject: [PATCH 06/54] Implement get_string, get_int_arr2, get_int_arr4. --- crates/larod/src/lib.rs | 93 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 56367655..25fdb502 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -1,7 +1,8 @@ +use core::slice; use larod_sys::*; use std::{ ffi::{CStr, CString}, - ptr, + ptr::{self, slice_from_raw_parts}, }; type Result = std::result::Result; @@ -43,6 +44,7 @@ enum LarodErrorCode { VERSION_MISMATCH, ALLOC, POWER_NOT_AVAILABLE, + INVALID_TYPE, MAX_ERRNO, } @@ -159,6 +161,95 @@ impl LarodMap { Err(error.into()) } } + + fn get_string(&self, k: &str) -> Result { + let Ok(key_cstr) = CString::new(k) else { + return Err(LarodError { + msg: String::from("Could not allocate set_string key CString"), + code: LarodErrorCode::ALLOC, + }); + }; + let mut error: *mut larodError = ptr::null_mut(); + let s = unsafe { CStr::from_ptr(larodMapGetStr(self.raw, key_cstr.as_ptr(), &mut error)) }; + let Ok(rs) = s.to_str() else { + return Err(LarodError { + msg: String::from("Returned string is not valid UTF-8"), + code: LarodErrorCode::INVALID_TYPE, + }); + }; + Ok(String::from(rs)) + } + fn get_int(&self, k: &str) -> Result { + let Ok(key_cstr) = CString::new(k) else { + return Err(LarodError { + msg: String::from("Could not allocate set_string key CString"), + code: LarodErrorCode::ALLOC, + }); + }; + let mut error: *mut larodError = ptr::null_mut(); + let mut v: i64 = 0; + let success = unsafe { larodMapGetInt(self.raw, key_cstr.as_ptr(), &mut v, &mut error) }; + if success { + Ok(v) + } else { + Err(error.into()) + } + } + fn get_int_arr2(&self, k: &str) -> Result<&[i64; 2]> { + let Ok(key_cstr) = CString::new(k) else { + return Err(LarodError { + msg: String::from("Could not allocate set_string key CString"), + code: LarodErrorCode::ALLOC, + }); + }; + let mut error: *mut larodError = ptr::null_mut(); + let maybe_int_arr = unsafe { + let ip = larodMapGetIntArr2(self.raw, key_cstr.as_ptr(), &mut error); + if ip.is_null() { + return Err(LarodError { + msg: String::from("Could not get integer array from LarodMap"), + code: LarodErrorCode::INVALID_TYPE, + }); + } else { + slice::from_raw_parts(ip, 2).try_into() + } + }; + let Ok(int_arr) = maybe_int_arr else { + return Err(LarodError { + msg: String::from("&[i64; 2] data stored in LarodMap is invalid."), + code: LarodErrorCode::INVALID_TYPE, + }); + }; + Ok(int_arr) + } + + fn get_int_arr4(&self, k: &str) -> Result<&[i64; 4]> { + let Ok(key_cstr) = CString::new(k) else { + return Err(LarodError { + msg: String::from("Could not allocate set_string key CString"), + code: LarodErrorCode::ALLOC, + }); + }; + let mut error: *mut larodError = ptr::null_mut(); + let maybe_int_arr = unsafe { + let ip = larodMapGetIntArr4(self.raw, key_cstr.as_ptr(), &mut error); + if ip.is_null() { + return Err(LarodError { + msg: String::from("Could not get integer array from LarodMap"), + code: LarodErrorCode::INVALID_TYPE, + }); + } else { + slice::from_raw_parts(ip, 4).try_into() + } + }; + let Ok(int_arr) = maybe_int_arr else { + return Err(LarodError { + msg: String::from("&[i64; 2] data stored in LarodMap is invalid."), + code: LarodErrorCode::INVALID_TYPE, + }); + }; + Ok(int_arr) + } } impl std::ops::Drop for LarodMap { From 3d510b6045f97d7c42b2da0fc49d7d2f901ec82d Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Tue, 29 Oct 2024 23:44:24 -0400 Subject: [PATCH 07/54] - Improved error handling to always deallocate a larodError object if the pointer to it is non-NULL. - Added some documentation. - Mimicked the try_from macro from the axevent crate to DRY the code. --- crates/larod/src/lib.rs | 231 +++++++++++++++++++++++++++------------- 1 file changed, 157 insertions(+), 74 deletions(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 25fdb502..c6ea5073 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -1,27 +1,69 @@ +//! A safe warpper around the larod-sys bindings to the larod C library. +//! +//! # Gotchas +//! Many of the C functions return either a bool or a pointer to some object. +//! Additionally, one of the out arguments is the pointer to the larodError +//! struct. If the normal return type is true, or not NULL in the case of a +//! pointer, the pointer to the larodError struct is expected to be NULL. This +//! represents two potentially conflicting indicators of whether the function +//! succeeded. +//! +//! Crucially, objects pointed to by returned pointers *AND* a non-NULL pointer +//! to a larodError struct need to be dealocated. That is handled appropriately +//! by copying the larodError data into a Rust LarodError struct and +//! dealocating the larodError object if it is non-NULL. + use core::slice; use larod_sys::*; use std::{ - ffi::{CStr, CString}, + ffi::{c_char, CStr, CString}, ptr::{self, slice_from_raw_parts}, }; type Result = std::result::Result; +macro_rules! try_func { + ($func:ident $(,)?) => {{ + let mut error: *mut larodError = ptr::null_mut(); + let success = $func(&mut error); + (success, LarodError::from(error)) + }}; + ($func:ident, $($arg:expr),+ $(,)?) => {{ + let mut error: *mut larodError = ptr::null_mut(); + let success = $func($( $arg ),+, &mut error); + (success, LarodError::from(error)) + }} +} + // Define our error types. These may be customized for our error handling cases. // Now we will be able to write our own errors, defer to an underlying error // implementation, or do something in between. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] struct LarodError { msg: String, code: LarodErrorCode, } +/// Convert from liblarod larodError to LarodError +/// If larodError is not NULL, it must be dealocated by calling larodClearError impl From<*mut larodError> for LarodError { - fn from(e: *mut larodError) -> Self { - let le = unsafe { *e }; - let msg: String = unsafe { CStr::from_ptr(le.msg).to_str().unwrap().into() }; - let code: LarodErrorCode = le.code.into(); - Self { msg, code } + fn from(mut e: *mut larodError) -> Self { + if e.is_null() { + Self::default() + } else { + let le = unsafe { *e }; + let msg: String = unsafe { + CStr::from_ptr(le.msg) + .to_str() + .unwrap_or("Error message invalid") + .into() + }; + let code: LarodErrorCode = le.code.into(); + unsafe { + larodClearError(&mut e); + } + Self { msg, code } + } } } @@ -48,6 +90,12 @@ enum LarodErrorCode { MAX_ERRNO, } +impl Default for LarodErrorCode { + fn default() -> Self { + LarodErrorCode::NONE + } +} + impl From for LarodErrorCode { fn from(code: larodErrorCode) -> LarodErrorCode { match code { @@ -79,15 +127,15 @@ pub struct LarodMap { impl LarodMap { fn new() -> Result { - let mut error: *mut larodError = ptr::null_mut(); - let map: *mut larodMap = unsafe { larodCreateMap(&mut error) }; - println!("map is_null? {:?}", map.is_null()); - println!("error is_null? {:?}", error.is_null()); - let e: LarodError = error.into(); - if !map.is_null() && matches!(e.code, LarodErrorCode::NONE) { - Ok(Self { raw: map }) - } else { + let (map, e): (*mut larodMap, LarodError) = unsafe { try_func!(larodCreateMap) }; + if map.is_null() { Err(e) + } else { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodCreateMap allocated a map AND returned an error!" + ); + Ok(Self { raw: map }) } } @@ -104,13 +152,22 @@ impl LarodMap { code: LarodErrorCode::ALLOC, }); }; - let mut error: *mut larodError = ptr::null_mut(); - let success = - unsafe { larodMapSetStr(self.raw, key_cstr.as_ptr(), value_cstr.as_ptr(), &mut error) }; + let (success, e): (bool, LarodError) = unsafe { + try_func!( + larodMapSetStr, + self.raw, + key_cstr.as_ptr(), + value_cstr.as_ptr(), + ) + }; if success { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodMapSetStr indicated success AND returned an error!" + ); Ok(()) } else { - Err(error.into()) + Err(e) } } fn set_int(&mut self, k: &str, v: i64) -> Result<()> { @@ -120,12 +177,16 @@ impl LarodMap { code: LarodErrorCode::ALLOC, }); }; - let mut error: *mut larodError = ptr::null_mut(); - let success = unsafe { larodMapSetInt(self.raw, key_cstr.as_ptr(), v, &mut error) }; + let (success, e): (bool, LarodError) = + unsafe { try_func!(larodMapSetInt, self.raw, key_cstr.as_ptr(), v) }; if success { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodMapSetInt indicated success AND returned an error!" + ); Ok(()) } else { - Err(error.into()) + Err(e) } } fn set_int_arr2(&mut self, k: &str, v: (i64, i64)) -> Result<()> { @@ -135,13 +196,17 @@ impl LarodMap { code: LarodErrorCode::ALLOC, }); }; - let mut error: *mut larodError = ptr::null_mut(); - let success = - unsafe { larodMapSetIntArr2(self.raw, key_cstr.as_ptr(), v.0, v.1, &mut error) }; + let (success, e): (bool, LarodError) = + unsafe { try_func!(larodMapSetIntArr2, self.raw, key_cstr.as_ptr(), v.0, v.1) }; + if success { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodMapSetIntArr2 indicated success AND returned an error!" + ); Ok(()) } else { - Err(error.into()) + Err(e) } } fn set_int_arr4(&mut self, k: &str, v: (i64, i64, i64, i64)) -> Result<()> { @@ -151,14 +216,26 @@ impl LarodMap { code: LarodErrorCode::ALLOC, }); }; - let mut error: *mut larodError = ptr::null_mut(); - let success = unsafe { - larodMapSetIntArr4(self.raw, key_cstr.as_ptr(), v.0, v.1, v.2, v.3, &mut error) + let (success, e): (bool, LarodError) = unsafe { + try_func!( + larodMapSetIntArr4, + self.raw, + key_cstr.as_ptr(), + v.0, + v.1, + v.2, + v.3 + ) }; + if success { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodMapSetIntArr4 indicated success AND returned an error!" + ); Ok(()) } else { - Err(error.into()) + Err(e) } } @@ -169,15 +246,21 @@ impl LarodMap { code: LarodErrorCode::ALLOC, }); }; - let mut error: *mut larodError = ptr::null_mut(); - let s = unsafe { CStr::from_ptr(larodMapGetStr(self.raw, key_cstr.as_ptr(), &mut error)) }; - let Ok(rs) = s.to_str() else { + let (c_str_ptr, e): (*const c_char, LarodError) = + unsafe { try_func!(larodMapGetStr, self.raw, key_cstr.as_ptr()) }; + let c_str = unsafe { CStr::from_ptr(c_str_ptr) }; + if let Ok(rs) = c_str.to_str() { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodMapGetStr returned a string AND returned an error!" + ); + Ok(String::from(rs)) + } else { return Err(LarodError { msg: String::from("Returned string is not valid UTF-8"), code: LarodErrorCode::INVALID_TYPE, }); - }; - Ok(String::from(rs)) + } } fn get_int(&self, k: &str) -> Result { let Ok(key_cstr) = CString::new(k) else { @@ -186,13 +269,17 @@ impl LarodMap { code: LarodErrorCode::ALLOC, }); }; - let mut error: *mut larodError = ptr::null_mut(); let mut v: i64 = 0; - let success = unsafe { larodMapGetInt(self.raw, key_cstr.as_ptr(), &mut v, &mut error) }; + let (success, e): (bool, LarodError) = + unsafe { try_func!(larodMapGetInt, self.raw, key_cstr.as_ptr(), &mut v) }; if success { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodMapGetInt indicated success AND returned an error!" + ); Ok(v) } else { - Err(error.into()) + Err(e) } } fn get_int_arr2(&self, k: &str) -> Result<&[i64; 2]> { @@ -202,25 +289,23 @@ impl LarodMap { code: LarodErrorCode::ALLOC, }); }; - let mut error: *mut larodError = ptr::null_mut(); - let maybe_int_arr = unsafe { - let ip = larodMapGetIntArr2(self.raw, key_cstr.as_ptr(), &mut error); - if ip.is_null() { - return Err(LarodError { - msg: String::from("Could not get integer array from LarodMap"), - code: LarodErrorCode::INVALID_TYPE, - }); - } else { - slice::from_raw_parts(ip, 2).try_into() + let (out_arr, e) = unsafe { try_func!(larodMapGetIntArr2, self.raw, key_cstr.as_ptr()) }; + if out_arr.is_null() { + Err(e) + } else { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodMapGetInt indicated success AND returned an error!" + ); + unsafe { + slice::from_raw_parts(out_arr, 2) + .try_into() + .or(Err(LarodError { + msg: String::from("&[i64; 2] data stored in LarodMap is invalid."), + code: LarodErrorCode::INVALID_TYPE, + })) } - }; - let Ok(int_arr) = maybe_int_arr else { - return Err(LarodError { - msg: String::from("&[i64; 2] data stored in LarodMap is invalid."), - code: LarodErrorCode::INVALID_TYPE, - }); - }; - Ok(int_arr) + } } fn get_int_arr4(&self, k: &str) -> Result<&[i64; 4]> { @@ -230,25 +315,23 @@ impl LarodMap { code: LarodErrorCode::ALLOC, }); }; - let mut error: *mut larodError = ptr::null_mut(); - let maybe_int_arr = unsafe { - let ip = larodMapGetIntArr4(self.raw, key_cstr.as_ptr(), &mut error); - if ip.is_null() { - return Err(LarodError { - msg: String::from("Could not get integer array from LarodMap"), - code: LarodErrorCode::INVALID_TYPE, - }); - } else { - slice::from_raw_parts(ip, 4).try_into() + let (out_arr, e) = unsafe { try_func!(larodMapGetIntArr4, self.raw, key_cstr.as_ptr()) }; + if out_arr.is_null() { + Err(e) + } else { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodMapGetIntArr4 indicated success AND returned an error!" + ); + unsafe { + slice::from_raw_parts(out_arr, 4) + .try_into() + .or(Err(LarodError { + msg: String::from("&[i64; 2] data stored in LarodMap is invalid."), + code: LarodErrorCode::INVALID_TYPE, + })) } - }; - let Ok(int_arr) = maybe_int_arr else { - return Err(LarodError { - msg: String::from("&[i64; 2] data stored in LarodMap is invalid."), - code: LarodErrorCode::INVALID_TYPE, - }); - }; - Ok(int_arr) + } } } From 3e004a0f8bb6e1e6369dccd65e38ebdd3799af16 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 30 Oct 2024 00:03:24 -0400 Subject: [PATCH 08/54] Added builder pattern for LarodClient. --- crates/larod/src/lib.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index c6ea5073..0372342e 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -12,6 +12,10 @@ //! to a larodError struct need to be dealocated. That is handled appropriately //! by copying the larodError data into a Rust LarodError struct and //! dealocating the larodError object if it is non-NULL. +//! +//! # TODOs: +//! - [ ] [larodDisconnect](https://axiscommunications.github.io/acap-documentation/docs/api/src/api/larod/html/larod_8h.html#ab8f97b4b4d15798384ca25f32ca77bba) +//! indicates it may fail to "kill a session." What are the implications if it fails to kill a session? Can we clear the sessions? use core::slice; use larod_sys::*; @@ -343,6 +347,36 @@ impl std::ops::Drop for LarodMap { } } +pub struct LarodClientBuilder {} + +impl LarodClientBuilder { + pub fn build() -> Result { + let mut connection: *mut larodConnection = ptr::null_mut(); + let (success, e): (bool, LarodError) = unsafe { try_func!(larodConnect, &mut connection) }; + if success { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodConnect indicated success AND returned an error!" + ); + Ok(LarodClient { connection }) + } else { + Err(e) + } + } +} + +pub struct LarodClient { + connection: *mut larodConnection, +} + +impl std::ops::Drop for LarodClient { + fn drop(&mut self) { + unsafe { + try_func!(larodDisconnect, &mut self.connection); + } + } +} + #[cfg(test)] mod tests { use super::*; From 021ea65984488c81ccbcccccf3584a0f530b8c6b Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 31 Oct 2024 07:37:42 -0400 Subject: [PATCH 09/54] Add log. --- Cargo.lock | 1 + crates/larod/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index d3848bc3..350e5f60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1288,6 +1288,7 @@ name = "larod" version = "0.1.0" dependencies = [ "larod-sys", + "log", ] [[package]] diff --git a/crates/larod/Cargo.toml b/crates/larod/Cargo.toml index f00f6840..021942a9 100644 --- a/crates/larod/Cargo.toml +++ b/crates/larod/Cargo.toml @@ -6,3 +6,4 @@ edition.workspace = true [dependencies] larod-sys = { workspace = true } +log.workspace = true From f9e05066df35477f115a4636c44c1493baca5c57 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 31 Oct 2024 07:38:56 -0400 Subject: [PATCH 10/54] Start adding LarodDevice and Session - Stubbed out functions for Session (larodConnection) - Start implementing a few functions to list hand handle larodDevices --- crates/larod/src/lib.rs | 221 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 199 insertions(+), 22 deletions(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 0372342e..562b4170 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -17,11 +17,13 @@ //! - [ ] [larodDisconnect](https://axiscommunications.github.io/acap-documentation/docs/api/src/api/larod/html/larod_8h.html#ab8f97b4b4d15798384ca25f32ca77bba) //! indicates it may fail to "kill a session." What are the implications if it fails to kill a session? Can we clear the sessions? -use core::slice; +use core::{num, slice}; use larod_sys::*; use std::{ - ffi::{c_char, CStr, CString}, + collections::HashMap, + ffi::{c_char, c_int, CStr, CString}, ptr::{self, slice_from_raw_parts}, + slice::from_raw_parts, }; type Result = std::result::Result; @@ -43,15 +45,15 @@ macro_rules! try_func { // Now we will be able to write our own errors, defer to an underlying error // implementation, or do something in between. #[derive(Debug, Clone, Default)] -struct LarodError { - msg: String, - code: LarodErrorCode, +pub struct LarodError { + pub msg: String, + pub code: LarodErrorCode, } -/// Convert from liblarod larodError to LarodError -/// If larodError is not NULL, it must be dealocated by calling larodClearError -impl From<*mut larodError> for LarodError { - fn from(mut e: *mut larodError) -> Self { +impl LarodError { + /// Convert from liblarod larodError to LarodError + /// If larodError is not NULL, it must be dealocated by calling larodClearError + fn from(e: *mut larodError) -> Self { if e.is_null() { Self::default() } else { @@ -63,9 +65,9 @@ impl From<*mut larodError> for LarodError { .into() }; let code: LarodErrorCode = le.code.into(); - unsafe { - larodClearError(&mut e); - } + // unsafe { + // larodClearError(&mut e); + // } Self { msg, code } } } @@ -347,32 +349,207 @@ impl std::ops::Drop for LarodMap { } } -pub struct LarodClientBuilder {} +#[derive(Debug)] +pub struct LarodDevice { + ptr: *const larodDevice, + name: String, + id: u32, +} + +impl LarodDevice { + pub fn get_name(&self) -> &str { + &self.name + } -impl LarodClientBuilder { - pub fn build() -> Result { - let mut connection: *mut larodConnection = ptr::null_mut(); - let (success, e): (bool, LarodError) = unsafe { try_func!(larodConnect, &mut connection) }; + fn larod_get_name(pointer: *const larodDevice) -> Result { + unsafe { + let (c_char_ptr, error) = try_func!(larodGetDeviceName, pointer); + if c_char_ptr.is_null() { + Err(error) + } else { + debug_assert!( + matches!(error.code, LarodErrorCode::NONE), + "larodGetDeviceName returned an object pointer AND returned an error!" + ); + let c_name = CStr::from_ptr(c_char_ptr); + c_name + .to_str() + .map(|n| String::from(n)) + .map_err(|e| LarodError { + msg: String::from("Returned string is not valid UTF-8"), + code: LarodErrorCode::INVALID_TYPE, + }) + } + } + } + + fn larod_get_instance(pointer: *const larodDevice) -> Result { + unsafe { + let mut instance: u32 = 0; + let (success, error) = try_func!(larodGetDeviceInstance, pointer, &mut instance); + if success { + debug_assert!( + matches!(error.code, LarodErrorCode::NONE), + "larodGetDeviceInstance returned success AND returned an error!" + ); + Ok(instance) + } else { + Err(error) + } + } + } +} + +impl TryFrom<*const larodDevice> for LarodDevice { + type Error = LarodError; + fn try_from(value: *const larodDevice) -> Result { + let name = LarodDevice::larod_get_name(value)?; + let id = LarodDevice::larod_get_instance(value)?; + Ok(Self { + ptr: value, + name, + id, + }) + } +} + +pub struct SessionBuilder {} + +impl SessionBuilder { + pub fn new() -> SessionBuilder { + SessionBuilder {} + } + pub fn build(&self) -> Result { + let mut conn: *mut larodConnection = ptr::null_mut(); + let (success, e): (bool, LarodError) = unsafe { try_func!(larodConnect, &mut conn) }; if success { debug_assert!( matches!(e.code, LarodErrorCode::NONE), "larodConnect indicated success AND returned an error!" ); - Ok(LarodClient { connection }) + Ok(Session { + conn, + model_map: HashMap::new(), + devices: Vec::new(), + }) } else { Err(e) } } } -pub struct LarodClient { - connection: *mut larodConnection, +pub struct Session { + conn: *mut larodConnection, + model_map: HashMap, + devices: Vec, +} + +// Using a session builder might not be necessary. +// There's little to configure when starting a session. +impl Session { + /// Constructs a new `Session`. + /// + /// # Panics + /// + /// Use `Session::builder()` if you wish to handle the failure as an `Error` + /// instead of panicking. + pub fn new() -> Session { + SessionBuilder::new().build().expect("Session::new()") + } + pub fn builder() -> SessionBuilder { + SessionBuilder::new() + } + pub fn disconnect(&mut self) -> Result<()> { + let (success, e): (bool, LarodError) = + unsafe { try_func!(larodDisconnect, &mut self.conn) }; + if success { + debug_assert!( + matches!(e.code, LarodErrorCode::NONE), + "larodDisconnect indicated success AND returned an error!" + ); + Ok(()) + } else { + Err(e) + } + } + pub fn num_sessions() -> Result<()> { + Ok(()) + } + pub fn device() -> Result<()> { + Ok(()) + } + pub fn list_chips() -> Result<()> { + Ok(()) + } + pub fn list_devices(&mut self) -> Result> { + let mut num_devices: usize = 0; + let (d, e) = unsafe { + let (dev_ptr, e) = try_func!(larodListDevices, self.conn, &mut num_devices); + if dev_ptr.is_null() { + return Err(e); + } + let raw_devices = unsafe { slice::from_raw_parts(dev_ptr, num_devices) }; + let mut devices: Vec = Vec::with_capacity(num_devices); + for (idx, raw_device) in raw_devices.into_iter().enumerate() { + let device = LarodDevice::try_from(*raw_device); + match device { + Ok(d) => devices.push(d), + Err(conv_error) => + log::error!("Could not identify larodDevice {} of {} returned from larodListDevices.\n{}", idx, num_devices, conv_error.msg), + } + } + (devices, e) + }; + Ok(d) + } + + // Overloaded need to check that. + pub fn load_model(&mut self) -> Result<()> { + // let model_fd: c_int = 0; + // let (m, e) = unsafe { + // try_func!(larodLoadModel, &mut self.conn, model_fd, ) + // } + Ok(()) + } + pub fn get_model() -> Result<()> { + Ok(()) + } + pub fn get_models() -> Result<()> { + Ok(()) + } + pub fn delete_model() -> Result<()> { + Ok(()) + } + pub fn alloc_model_inputs() -> Result<()> { + Ok(()) + } + pub fn alloc_model_outputs() -> Result<()> { + Ok(()) + } + pub fn destroy_tensors() -> Result<()> { + Ok(()) + } + pub fn track_tensor() -> Result<()> { + Ok(()) + } + pub fn run_job() -> Result<()> { + Ok(()) + } + pub fn run_inference() -> Result<()> { + Ok(()) + } + pub fn chip_id() -> Result<()> { + Ok(()) + } + pub fn chip_type() -> Result<()> { + Ok(()) + } } -impl std::ops::Drop for LarodClient { +impl std::ops::Drop for Session { fn drop(&mut self) { unsafe { - try_func!(larodDisconnect, &mut self.connection); + try_func!(larodDisconnect, &mut self.conn); } } } From c05a0a1625c7669a5060ea5712dc5dbfe588083f Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 1 Nov 2024 20:39:46 -0400 Subject: [PATCH 11/54] Add remote testing - Add testing documentation to README.md - Add runner specification to .cargo/config.toml (new) - Set `aarch64-unknown-linux-gnu` to default build target. - Add remote-test.sh script to run tests on remote target. --- .cargo/config.toml | 5 +++++ README.md | 14 ++++++++++++++ remote-test.sh | 10 ++++++++++ 3 files changed, 29 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 remote-test.sh diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..d5eccbc8 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[build] +target = "aarch64-unknown-linux-gnu" + +[target.aarch64-unknown-linux-gnu] +runner = ["/workspaces/acap-rs/remote-test.sh"] \ No newline at end of file diff --git a/README.md b/README.md index 41022f42..e09dd43e 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,20 @@ Important workflows are documented in the [Makefile](./Makefile) and can now be Development environments outside containers are more difficult to reproduce and maintain. Should it nonetheless be of interest, one procedure is documented in [this workflow](.github/workflows/on-host-workflow.yml). +## Testing +Some items in this workspace rely on libraries or hardware on Axis cameras. This makes testing difficult since these tests cannot run on an arbitrary x86_64 host. Below are some steps to enable running unit test on device. + +1. Connect an Axis camera to your network and ensure it is accessible. +2. The user will likely need to be `root`, such that the Axis camera file system is writable. +3. Set the `CARGO_TEST_CAMERA` environment variable to the user and IP of the camera with the format `user@ip` +4. Set up an identity based SSH connection to the camera. + 1. Create an ID via `ssh-keygen` + 2. Copy the id to the device via `ssh-copy-id` + +Now, via the [remote-test.sh](remote-test.sh) script, and the `runner = ["/workspaces/acap-rs/remote-test.sh"]` line in the .cargo/config.toml, tests with the `aarch64-unknown-linux-gnu` target triplet will automatically be copied to the remote camera and executed there. Run these tests via `cargo test --target aarch64-unknown-linux-gnu`. + +If you want to run tests locally, just make sure you clear the `CARGO_TEST_CAMERA` environment variable via `unset CARGO_TEST_CAMERA`. + ## Example applications Below is the list of examples available in the repository. diff --git a/remote-test.sh b/remote-test.sh new file mode 100644 index 00000000..02a1c7f7 --- /dev/null +++ b/remote-test.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -e +if [[ -z "${CARGO_TEST_CAMERA}" ]]; then + f=`basename $1` + scp "$1" $CARGO_TEST_CAMERA:. + # echo $f + ssh $CARGO_TEST_CAMERA "chmod +x /root/$f" + ssh $CARGO_TEST_CAMERA "/root/$f" + # ^ note: may need to change this line, see https://stackoverflow.com/q/9379400 +fi From e0fa1403aa0dc095ed9cc6be152f19525cbe3708 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 1 Nov 2024 22:54:03 -0400 Subject: [PATCH 12/54] Hide on-device tests behind a device-tests feature --- crates/larod/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/larod/Cargo.toml b/crates/larod/Cargo.toml index 021942a9..dad74c68 100644 --- a/crates/larod/Cargo.toml +++ b/crates/larod/Cargo.toml @@ -7,3 +7,6 @@ edition.workspace = true larod-sys = { workspace = true } log.workspace = true + +[features] +device-tests = [] \ No newline at end of file From 147e438d6789330c94067ebafcc25f65ee428279 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 1 Nov 2024 22:54:39 -0400 Subject: [PATCH 13/54] Inverted remote-test.sh check for CARGO_TEST_CAMERA definition --- remote-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remote-test.sh b/remote-test.sh index 02a1c7f7..78b8fadf 100644 --- a/remote-test.sh +++ b/remote-test.sh @@ -1,6 +1,6 @@ #!/bin/sh set -e -if [[ -z "${CARGO_TEST_CAMERA}" ]]; then +if [ -n "${CARGO_TEST_CAMERA}" ]; then f=`basename $1` scp "$1" $CARGO_TEST_CAMERA:. # echo $f From 32db020e821b9695d1e071e081c6b6d51d69163f Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Mon, 4 Nov 2024 11:16:01 -0500 Subject: [PATCH 14/54] Update larod_sys/src/bindings.rs to match new enum style. --- crates/larod-sys/src/bindings.rs | 105 ++++++++++++++++++------------- 1 file changed, 60 insertions(+), 45 deletions(-) diff --git a/crates/larod-sys/src/bindings.rs b/crates/larod-sys/src/bindings.rs index 6ea38066..6e5497ab 100644 --- a/crates/larod-sys/src/bindings.rs +++ b/crates/larod-sys/src/bindings.rs @@ -5,51 +5,66 @@ pub struct larodDevice { _unused: [u8; 0], } -pub const larodAccess_LAROD_ACCESS_INVALID: larodAccess = 0; -pub const larodAccess_LAROD_ACCESS_PRIVATE: larodAccess = 1; -pub const larodAccess_LAROD_ACCESS_PUBLIC: larodAccess = 2; -pub type larodAccess = ::std::os::raw::c_uint; -pub const larodErrorCode_LAROD_ERROR_NONE: larodErrorCode = 0; -pub const larodErrorCode_LAROD_ERROR_JOB: larodErrorCode = -1; -pub const larodErrorCode_LAROD_ERROR_LOAD_MODEL: larodErrorCode = -2; -pub const larodErrorCode_LAROD_ERROR_FD: larodErrorCode = -3; -pub const larodErrorCode_LAROD_ERROR_MODEL_NOT_FOUND: larodErrorCode = -4; -pub const larodErrorCode_LAROD_ERROR_PERMISSION: larodErrorCode = -5; -pub const larodErrorCode_LAROD_ERROR_CONNECTION: larodErrorCode = -6; -pub const larodErrorCode_LAROD_ERROR_CREATE_SESSION: larodErrorCode = -7; -pub const larodErrorCode_LAROD_ERROR_KILL_SESSION: larodErrorCode = -8; -pub const larodErrorCode_LAROD_ERROR_INVALID_CHIP_ID: larodErrorCode = -9; -pub const larodErrorCode_LAROD_ERROR_INVALID_ACCESS: larodErrorCode = -10; -pub const larodErrorCode_LAROD_ERROR_DELETE_MODEL: larodErrorCode = -11; -pub const larodErrorCode_LAROD_ERROR_TENSOR_MISMATCH: larodErrorCode = -12; -pub const larodErrorCode_LAROD_ERROR_VERSION_MISMATCH: larodErrorCode = -13; -pub const larodErrorCode_LAROD_ERROR_ALLOC: larodErrorCode = -14; -pub const larodErrorCode_LAROD_ERROR_POWER_NOT_AVAILABLE: larodErrorCode = -15; -pub const larodErrorCode_LAROD_ERROR_MAX_ERRNO: larodErrorCode = 1024; -pub type larodErrorCode = ::std::os::raw::c_int; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_INVALID: larodTensorDataType = 0; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_UNSPECIFIED: larodTensorDataType = 1; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_BOOL: larodTensorDataType = 2; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_UINT8: larodTensorDataType = 3; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_INT8: larodTensorDataType = 4; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_UINT16: larodTensorDataType = 5; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_INT16: larodTensorDataType = 6; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_UINT32: larodTensorDataType = 7; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_INT32: larodTensorDataType = 8; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_UINT64: larodTensorDataType = 9; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_INT64: larodTensorDataType = 10; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_FLOAT16: larodTensorDataType = 11; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_FLOAT32: larodTensorDataType = 12; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_FLOAT64: larodTensorDataType = 13; -pub const larodTensorDataType_LAROD_TENSOR_DATA_TYPE_MAX: larodTensorDataType = 14; -pub type larodTensorDataType = ::std::os::raw::c_uint; -pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_INVALID: larodTensorLayout = 0; -pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_UNSPECIFIED: larodTensorLayout = 1; -pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_NHWC: larodTensorLayout = 2; -pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_NCHW: larodTensorLayout = 3; -pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_420SP: larodTensorLayout = 4; -pub const larodTensorLayout_LAROD_TENSOR_LAYOUT_MAX: larodTensorLayout = 5; -pub type larodTensorLayout = ::std::os::raw::c_uint; +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum larodAccess { + LAROD_ACCESS_INVALID = 0, + LAROD_ACCESS_PRIVATE = 1, + LAROD_ACCESS_PUBLIC = 2, +} +#[repr(i32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum larodErrorCode { + LAROD_ERROR_NONE = 0, + LAROD_ERROR_JOB = -1, + LAROD_ERROR_LOAD_MODEL = -2, + LAROD_ERROR_FD = -3, + LAROD_ERROR_MODEL_NOT_FOUND = -4, + LAROD_ERROR_PERMISSION = -5, + LAROD_ERROR_CONNECTION = -6, + LAROD_ERROR_CREATE_SESSION = -7, + LAROD_ERROR_KILL_SESSION = -8, + LAROD_ERROR_INVALID_CHIP_ID = -9, + LAROD_ERROR_INVALID_ACCESS = -10, + LAROD_ERROR_DELETE_MODEL = -11, + LAROD_ERROR_TENSOR_MISMATCH = -12, + LAROD_ERROR_VERSION_MISMATCH = -13, + LAROD_ERROR_ALLOC = -14, + LAROD_ERROR_MAX_ERRNO = 1024, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum larodTensorDataType { + LAROD_TENSOR_DATA_TYPE_INVALID = 0, + LAROD_TENSOR_DATA_TYPE_UNSPECIFIED = 1, + LAROD_TENSOR_DATA_TYPE_BOOL = 2, + LAROD_TENSOR_DATA_TYPE_UINT8 = 3, + LAROD_TENSOR_DATA_TYPE_INT8 = 4, + LAROD_TENSOR_DATA_TYPE_UINT16 = 5, + LAROD_TENSOR_DATA_TYPE_INT16 = 6, + LAROD_TENSOR_DATA_TYPE_UINT32 = 7, + LAROD_TENSOR_DATA_TYPE_INT32 = 8, + LAROD_TENSOR_DATA_TYPE_UINT64 = 9, + LAROD_TENSOR_DATA_TYPE_INT64 = 10, + LAROD_TENSOR_DATA_TYPE_FLOAT16 = 11, + LAROD_TENSOR_DATA_TYPE_FLOAT32 = 12, + LAROD_TENSOR_DATA_TYPE_FLOAT64 = 13, + LAROD_TENSOR_DATA_TYPE_MAX = 14, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum larodTensorLayout { + LAROD_TENSOR_LAYOUT_INVALID = 0, + LAROD_TENSOR_LAYOUT_UNSPECIFIED = 1, + LAROD_TENSOR_LAYOUT_NHWC = 2, + LAROD_TENSOR_LAYOUT_NCHW = 3, + LAROD_TENSOR_LAYOUT_420SP = 4, + LAROD_TENSOR_LAYOUT_MAX = 5, +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct larodError { From b3607bf4e32f6be87b9e08b4bb0ea65e7d5d2947 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Tue, 5 Nov 2024 20:57:35 -0500 Subject: [PATCH 15/54] Change to EnumVariation::Rust as default bindgen enum style. --- crates/larod-sys/build.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/larod-sys/build.rs b/crates/larod-sys/build.rs index 4485f5ec..771449c1 100644 --- a/crates/larod-sys/build.rs +++ b/crates/larod-sys/build.rs @@ -7,6 +7,9 @@ fn populated_bindings(dst: &path::PathBuf) { .allowlist_recursively(false) .allowlist_function("^(larod.*)$") .allowlist_type("^(_?larod.*)$") + .default_enum_style(bindgen::EnumVariation::Rust { + non_exhaustive: true, + }) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .layout_tests(false); for path in library.include_paths { From a5d62d58c1ea5f4a91bc014d68e79ad2e938dbd2 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Tue, 5 Nov 2024 21:34:58 -0500 Subject: [PATCH 16/54] Improve error handling and add documentation - Change error handling to better check for a non-NULL larodError pointer. - Add some documentation for LarodMap functions. - Set LarodMap functions to public. - Add some additional tests. - Hide on-device tests behind a device-tests feature. - Add a basic example. --- crates/larod/Cargo.toml | 6 +- crates/larod/examples/basic.rs | 25 ++ crates/larod/src/lib.rs | 553 ++++++++++++++++++++------------- 3 files changed, 373 insertions(+), 211 deletions(-) create mode 100644 crates/larod/examples/basic.rs diff --git a/crates/larod/Cargo.toml b/crates/larod/Cargo.toml index dad74c68..1cf2aa44 100644 --- a/crates/larod/Cargo.toml +++ b/crates/larod/Cargo.toml @@ -9,4 +9,8 @@ larod-sys = { workspace = true } log.workspace = true [features] -device-tests = [] \ No newline at end of file +device-tests = [] + +[[example]] +name = "basic" +crate-type = ["bin"] \ No newline at end of file diff --git a/crates/larod/examples/basic.rs b/crates/larod/examples/basic.rs new file mode 100644 index 00000000..9febfc69 --- /dev/null +++ b/crates/larod/examples/basic.rs @@ -0,0 +1,25 @@ +use larod::{Error, Session}; + +fn main() -> Result<(), Error> { + let session = Session::new(); + let devices = match session.get_devices() { + Ok(d) => d, + Err(Error::LarodError(e)) => { + if let Ok(msg) = e.msg() { + eprintln!("Error while listing available devices! {}", msg); + } else { + eprintln!("Error while listing available devices. Error returned ") + } + return Err(Error::LarodError(e)); + } + Err(e) => { + eprintln!("Unknown error while listing devices: {:?}", e); + return Err(e); + } + }; + println!("Devices:"); + for d in devices { + println!("{} ({})", d.get_name(), d.get_instance()); + } + Ok(()) +} diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 562b4170..4519ce7d 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -2,16 +2,22 @@ //! //! # Gotchas //! Many of the C functions return either a bool or a pointer to some object. -//! Additionally, one of the out arguments is the pointer to the larodError -//! struct. If the normal return type is true, or not NULL in the case of a +//! Additionally, one of the out arguments is a pointer to a larodError +//! object. If the normal return type is true, or not NULL in the case of a //! pointer, the pointer to the larodError struct is expected to be NULL. This //! represents two potentially conflicting indicators of whether the function //! succeeded. //! //! Crucially, objects pointed to by returned pointers *AND* a non-NULL pointer //! to a larodError struct need to be dealocated. That is handled appropriately -//! by copying the larodError data into a Rust LarodError struct and -//! dealocating the larodError object if it is non-NULL. +//! by constructing the LarodError struct if the larodError pointer is non-NULL +//! and the impl Drop for LarodError will dealocate the object appropriately. +//! +//! Example +//! ```rust +//! let session = Session::new(); +//! let devices = session.devices(); +//! ``` //! //! # TODOs: //! - [ ] [larodDisconnect](https://axiscommunications.github.io/acap-documentation/docs/api/src/api/larod/html/larod_8h.html#ab8f97b4b4d15798384ca25f32ca77bba) @@ -26,139 +32,143 @@ use std::{ slice::from_raw_parts, }; -type Result = std::result::Result; +type Result = std::result::Result; macro_rules! try_func { ($func:ident $(,)?) => {{ let mut error: *mut larodError = ptr::null_mut(); let success = $func(&mut error); - (success, LarodError::from(error)) + if error.is_null() { + (success, None) + } else { + (success, Some(Error::LarodError(LarodError{inner: error}))) + } }}; ($func:ident, $($arg:expr),+ $(,)?) => {{ let mut error: *mut larodError = ptr::null_mut(); let success = $func($( $arg ),+, &mut error); - (success, LarodError::from(error)) + if error.is_null() { + (success, None) + } else { + (success, Some(Error::LarodError(LarodError{inner: error}))) + } + }} } -// Define our error types. These may be customized for our error handling cases. -// Now we will be able to write our own errors, defer to an underlying error -// implementation, or do something in between. -#[derive(Debug, Clone, Default)] +// Most larod functions require a `NULL`` pointer to a larodError AND may +// produce either a `NULL`` output pointer or `false` if an error occurs. This +// results in two potential indicators of whether the function succeeded. If we +// get a`true` output, we expect the larodError to be a pointer to `NULL` still. +// In the possibly rare event that a function succeeds but the larodError +// pointer is not `NULL`, we need to deallocate that memory by calling +// `larodClearError`. The `try_func` macro always checks to see if the +// larodError pointer is `NULL` and return a `LarodError` if not. Doing so will +// call `larodClearError` when it is ultimately dropped. +#[derive(Debug)] pub struct LarodError { - pub msg: String, - pub code: LarodErrorCode, + inner: *mut larodError, } impl LarodError { - /// Convert from liblarod larodError to LarodError - /// If larodError is not NULL, it must be dealocated by calling larodClearError - fn from(e: *mut larodError) -> Self { - if e.is_null() { - Self::default() + pub fn msg(&self) -> Result { + if self.inner.is_null() { + Err(Error::NullLarodPointer) } else { - let le = unsafe { *e }; - let msg: String = unsafe { - CStr::from_ptr(le.msg) - .to_str() - .unwrap_or("Error message invalid") - .into() - }; - let code: LarodErrorCode = le.code.into(); - // unsafe { - // larodClearError(&mut e); - // } - Self { msg, code } + let msg_slice = unsafe { CStr::from_ptr((*self.inner).msg).to_str() }; + match msg_slice { + Ok(m) => Ok(m.into()), + Err(e) => { + log::error!("larodError.msg contained invalid UTF-8: {:?}", e); + Err(Error::InvalidLarodMessage) + } + } } } -} -#[allow(non_camel_case_types)] -#[derive(Debug, Clone)] -enum LarodErrorCode { - NONE, - JOB, - LOAD_MODEL, - FD, - MODEL_NOT_FOUND, - PERMISSION, - CONNECTION, - CREATE_SESSION, - KILL_SESSION, - INVALID_CHIP_ID, - INVALID_ACCESS, - DELETE_MODEL, - TENSOR_MISMATCH, - VERSION_MISMATCH, - ALLOC, - POWER_NOT_AVAILABLE, - INVALID_TYPE, - MAX_ERRNO, -} - -impl Default for LarodErrorCode { - fn default() -> Self { - LarodErrorCode::NONE + pub fn code(&self) -> larodErrorCode { + unsafe { (*self.inner).code } } } -impl From for LarodErrorCode { - fn from(code: larodErrorCode) -> LarodErrorCode { - match code { - larodErrorCode_LAROD_ERROR_NONE => LarodErrorCode::NONE, - larodErrorCode_LAROD_ERROR_JOB => LarodErrorCode::JOB, - larodErrorCode_LAROD_ERROR_LOAD_MODEL => LarodErrorCode::LOAD_MODEL, - larodErrorCode_LAROD_ERROR_FD => LarodErrorCode::FD, - larodErrorCode_LAROD_ERROR_MODEL_NOT_FOUND => LarodErrorCode::MODEL_NOT_FOUND, - larodErrorCode_LAROD_ERROR_PERMISSION => LarodErrorCode::PERMISSION, - larodErrorCode_LAROD_ERROR_CONNECTION => LarodErrorCode::CONNECTION, - larodErrorCode_LAROD_ERROR_CREATE_SESSION => LarodErrorCode::CREATE_SESSION, - larodErrorCode_LAROD_ERROR_KILL_SESSION => LarodErrorCode::KILL_SESSION, - larodErrorCode_LAROD_ERROR_INVALID_CHIP_ID => LarodErrorCode::INVALID_CHIP_ID, - larodErrorCode_LAROD_ERROR_INVALID_ACCESS => LarodErrorCode::INVALID_ACCESS, - larodErrorCode_LAROD_ERROR_DELETE_MODEL => LarodErrorCode::DELETE_MODEL, - larodErrorCode_LAROD_ERROR_TENSOR_MISMATCH => LarodErrorCode::TENSOR_MISMATCH, - larodErrorCode_LAROD_ERROR_VERSION_MISMATCH => LarodErrorCode::VERSION_MISMATCH, - larodErrorCode_LAROD_ERROR_ALLOC => LarodErrorCode::ALLOC, - larodErrorCode_LAROD_ERROR_POWER_NOT_AVAILABLE => LarodErrorCode::POWER_NOT_AVAILABLE, - larodErrorCode_LAROD_ERROR_MAX_ERRNO => LarodErrorCode::MAX_ERRNO, - _ => unreachable!(), +impl Drop for LarodError { + fn drop(&mut self) { + if !self.inner.is_null() { + unsafe { larodClearError(&mut self.inner) } } } } +#[derive(Debug)] +pub enum Error { + LarodError(LarodError), + NullLarodPointer, + InvalidLarodMessage, + PointerToInvalidData, + CStringAllocation, + MissingLarodError, +} + +// impl LarodError { +// /// Convert from liblarod larodError to LarodError +// /// If larodError is not NULL, it must be dealocated by calling larodClearError +// fn from(e: *mut larodError) -> Self { +// if e.is_null() { +// Self::default() +// } else { +// let le = unsafe { *e }; +// let msg: String = unsafe { +// CStr::from_ptr(le.msg) +// .to_str() +// .unwrap_or("Error message invalid") +// .into() +// }; +// let code: LarodErrorCode = le.code.into(); +// // unsafe { +// // larodClearError(&mut e); +// // } +// Self { msg, code } +// } +// } +// } + +/// A type representing a larodMap. pub struct LarodMap { raw: *mut larodMap, } impl LarodMap { - fn new() -> Result { - let (map, e): (*mut larodMap, LarodError) = unsafe { try_func!(larodCreateMap) }; - if map.is_null() { - Err(e) - } else { + /// Create a new larodMap object + pub fn new() -> Result { + let (map, maybe_error): (*mut larodMap, Option) = + unsafe { try_func!(larodCreateMap) }; + if !map.is_null() { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodCreateMap allocated a map AND returned an error!" ); Ok(Self { raw: map }) + } else { + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } - fn set_string(&mut self, k: &str, v: &str) -> Result<()> { + /// Add a string to a larodMap object. + /// Example + /// ```rust + /// use larod::LarodMap; + /// + /// let map = LarodMap::new().expect("Error creating map"); + /// map.set_string("key", "value").expect("Error setting string value for larodMap"); + /// ``` + pub fn set_string(&mut self, k: &str, v: &str) -> Result<()> { let Ok(key_cstr) = CString::new(k.as_bytes()) else { - return Err(LarodError { - msg: String::from("Could not allocate set_string key CString"), - code: LarodErrorCode::ALLOC, - }); + return Err(Error::CStringAllocation); }; let Ok(value_cstr) = CString::new(v.as_bytes()) else { - return Err(LarodError { - msg: String::from("Could not allocate set_string value CString"), - code: LarodErrorCode::ALLOC, - }); + return Err(Error::CStringAllocation); }; - let (success, e): (bool, LarodError) = unsafe { + let (success, maybe_error): (bool, Option) = unsafe { try_func!( larodMapSetStr, self.raw, @@ -168,61 +178,79 @@ impl LarodMap { }; if success { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodMapSetStr indicated success AND returned an error!" ); Ok(()) } else { - Err(e) + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } - fn set_int(&mut self, k: &str, v: i64) -> Result<()> { + + /// Add an integer to a larodMap object. + /// Example + /// ```rust + /// use larod::LarodMap; + /// + /// let map = LarodMap::new().expect("Error creating map"); + /// map.set_int("key", 45).expect("Error setting integer value for larodMap"); + /// ``` + pub fn set_int(&mut self, k: &str, v: i64) -> Result<()> { let Ok(key_cstr) = CString::new(k.as_bytes()) else { - return Err(LarodError { - msg: String::from("Could not allocate set_string key CString"), - code: LarodErrorCode::ALLOC, - }); + return Err(Error::CStringAllocation); }; - let (success, e): (bool, LarodError) = + let (success, maybe_error): (bool, Option) = unsafe { try_func!(larodMapSetInt, self.raw, key_cstr.as_ptr(), v) }; if success { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodMapSetInt indicated success AND returned an error!" ); Ok(()) } else { - Err(e) + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } - fn set_int_arr2(&mut self, k: &str, v: (i64, i64)) -> Result<()> { + + /// Add an integer array of two items to a larodMap object. + /// Example + /// ```rust + /// use larod::LarodMap; + /// + /// let map = LarodMap::new().expect("Error creating map"); + /// map.set_int_arr2("key", (45, 64)).expect("Error setting integer array for larodMap"); + /// ``` + pub fn set_int_arr2(&mut self, k: &str, v: (i64, i64)) -> Result<()> { let Ok(key_cstr) = CString::new(k.as_bytes()) else { - return Err(LarodError { - msg: String::from("Could not allocate set_string key CString"), - code: LarodErrorCode::ALLOC, - }); + return Err(Error::CStringAllocation); }; - let (success, e): (bool, LarodError) = + let (success, maybe_error): (bool, Option) = unsafe { try_func!(larodMapSetIntArr2, self.raw, key_cstr.as_ptr(), v.0, v.1) }; if success { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodMapSetIntArr2 indicated success AND returned an error!" ); Ok(()) } else { - Err(e) + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } - fn set_int_arr4(&mut self, k: &str, v: (i64, i64, i64, i64)) -> Result<()> { + + /// Add an integer array of 4 items to a larodMap object. + /// Example + /// ```rust + /// use larod::LarodMap; + /// + /// let map = LarodMap::new().expect("Error creating map"); + /// map.set_int_arr4("key", (45, 64, 36, 23)).expect("Error setting integer array for larodMap"); + /// ``` + pub fn set_int_arr4(&mut self, k: &str, v: (i64, i64, i64, i64)) -> Result<()> { let Ok(key_cstr) = CString::new(k.as_bytes()) else { - return Err(LarodError { - msg: String::from("Could not allocate set_string key CString"), - code: LarodErrorCode::ALLOC, - }); + return Err(Error::CStringAllocation); }; - let (success, e): (bool, LarodError) = unsafe { + let (success, maybe_error): (bool, Option) = unsafe { try_func!( larodMapSetIntArr4, self.raw, @@ -236,107 +264,126 @@ impl LarodMap { if success { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodMapSetIntArr4 indicated success AND returned an error!" ); Ok(()) } else { - Err(e) + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } - fn get_string(&self, k: &str) -> Result { + /// Get a string from a larodMap object. + /// Example + /// ```rust + /// use larod::LarodMap; + /// + /// let map = LarodMap::new().expect("Error creating map"); + /// map.set_string("key", "value").expect("Error setting string value for larodMap"); + /// let returned_string = map.get_string("key").expect("Unable to return value for \"key\""); + /// ``` + pub fn get_string(&self, k: &str) -> Result { let Ok(key_cstr) = CString::new(k) else { - return Err(LarodError { - msg: String::from("Could not allocate set_string key CString"), - code: LarodErrorCode::ALLOC, - }); + return Err(Error::CStringAllocation); }; - let (c_str_ptr, e): (*const c_char, LarodError) = + let (c_str_ptr, maybe_error): (*const c_char, Option) = unsafe { try_func!(larodMapGetStr, self.raw, key_cstr.as_ptr()) }; let c_str = unsafe { CStr::from_ptr(c_str_ptr) }; if let Ok(rs) = c_str.to_str() { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodMapGetStr returned a string AND returned an error!" ); Ok(String::from(rs)) } else { - return Err(LarodError { - msg: String::from("Returned string is not valid UTF-8"), - code: LarodErrorCode::INVALID_TYPE, - }); + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } - fn get_int(&self, k: &str) -> Result { + + /// Get an integer array of 4 items from a larodMap object. + /// Example + /// ```rust + /// use larod::LarodMap; + /// + /// let map = LarodMap::new().expect("Error creating map"); + /// map.set_int("key", 45).expect("Error setting integer array for larodMap"); + /// let value = map.get_int("key").expect("Unable to get array values for \"key\""); + /// ``` + pub fn get_int(&self, k: &str) -> Result { let Ok(key_cstr) = CString::new(k) else { - return Err(LarodError { - msg: String::from("Could not allocate set_string key CString"), - code: LarodErrorCode::ALLOC, - }); + return Err(Error::CStringAllocation); }; let mut v: i64 = 0; - let (success, e): (bool, LarodError) = + let (success, maybe_error): (bool, Option) = unsafe { try_func!(larodMapGetInt, self.raw, key_cstr.as_ptr(), &mut v) }; if success { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodMapGetInt indicated success AND returned an error!" ); Ok(v) } else { - Err(e) + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } - fn get_int_arr2(&self, k: &str) -> Result<&[i64; 2]> { + + /// Get an integer array of 4 items from a larodMap object. + /// Example + /// ```rust + /// use larod::LarodMap; + /// + /// let map = LarodMap::new().expect("Error creating map"); + /// map.set_int_arr2("key", (45, 64)).expect("Error setting integer array for larodMap"); + /// let returned_array = map.get_int_arr2("key").expect("Unable to get array values for \"key\""); + /// ``` + pub fn get_int_arr2(&self, k: &str) -> Result<&[i64; 2]> { let Ok(key_cstr) = CString::new(k) else { - return Err(LarodError { - msg: String::from("Could not allocate set_string key CString"), - code: LarodErrorCode::ALLOC, - }); + return Err(Error::CStringAllocation); }; - let (out_arr, e) = unsafe { try_func!(larodMapGetIntArr2, self.raw, key_cstr.as_ptr()) }; - if out_arr.is_null() { - Err(e) - } else { + let (out_arr, maybe_error) = + unsafe { try_func!(larodMapGetIntArr2, self.raw, key_cstr.as_ptr()) }; + if !out_arr.is_null() { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodMapGetInt indicated success AND returned an error!" ); unsafe { slice::from_raw_parts(out_arr, 2) .try_into() - .or(Err(LarodError { - msg: String::from("&[i64; 2] data stored in LarodMap is invalid."), - code: LarodErrorCode::INVALID_TYPE, - })) + .or(Err(Error::PointerToInvalidData)) } + } else { + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } - fn get_int_arr4(&self, k: &str) -> Result<&[i64; 4]> { + /// Get an integer array of 4 items from a larodMap object. + /// Example + /// ```rust + /// use larod::LarodMap; + /// + /// let map = LarodMap::new().expect("Error creating map"); + /// map.set_int_arr4("key", (45, 64, 36, 23)).expect("Error setting integer array for larodMap"); + /// let returned_array = map.get_int_arr4("key").expect("Unable to get array values for \"key\""); + /// ``` + pub fn get_int_arr4(&self, k: &str) -> Result<&[i64; 4]> { let Ok(key_cstr) = CString::new(k) else { - return Err(LarodError { - msg: String::from("Could not allocate set_string key CString"), - code: LarodErrorCode::ALLOC, - }); + return Err(Error::CStringAllocation); }; - let (out_arr, e) = unsafe { try_func!(larodMapGetIntArr4, self.raw, key_cstr.as_ptr()) }; - if out_arr.is_null() { - Err(e) - } else { + let (out_arr, maybe_error) = + unsafe { try_func!(larodMapGetIntArr4, self.raw, key_cstr.as_ptr()) }; + if !out_arr.is_null() { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodMapGetIntArr4 indicated success AND returned an error!" ); unsafe { slice::from_raw_parts(out_arr, 4) .try_into() - .or(Err(LarodError { - msg: String::from("&[i64; 2] data stored in LarodMap is invalid."), - code: LarodErrorCode::INVALID_TYPE, - })) + .or(Err(Error::PointerToInvalidData)) } + } else { + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } } @@ -349,36 +396,45 @@ impl std::ops::Drop for LarodMap { } } +/// A type representing a larodDevice. #[derive(Debug)] pub struct LarodDevice { + // The caller does not get ownership of the returned pointer and must not + // attempt to free it. The lifetime of the memory pointed to expires when + // conn closes. ptr: *const larodDevice, name: String, id: u32, } impl LarodDevice { + /// Get the name of a larodDevice. pub fn get_name(&self) -> &str { &self.name } + /// Get the instance of a larodDevice. + /// From the larod documentation + /// > *In case there are multiple identical devices that are available in the service, they are distinguished by an instance number, with the first instance starting from zero.* + pub fn get_instance(&self) -> u32 { + self.id + } + fn larod_get_name(pointer: *const larodDevice) -> Result { unsafe { - let (c_char_ptr, error) = try_func!(larodGetDeviceName, pointer); - if c_char_ptr.is_null() { - Err(error) - } else { + let (c_char_ptr, maybe_error) = try_func!(larodGetDeviceName, pointer); + if !c_char_ptr.is_null() { debug_assert!( - matches!(error.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodGetDeviceName returned an object pointer AND returned an error!" ); let c_name = CStr::from_ptr(c_char_ptr); c_name .to_str() .map(|n| String::from(n)) - .map_err(|e| LarodError { - msg: String::from("Returned string is not valid UTF-8"), - code: LarodErrorCode::INVALID_TYPE, - }) + .map_err(|e| Error::InvalidLarodMessage) + } else { + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } } @@ -386,22 +442,22 @@ impl LarodDevice { fn larod_get_instance(pointer: *const larodDevice) -> Result { unsafe { let mut instance: u32 = 0; - let (success, error) = try_func!(larodGetDeviceInstance, pointer, &mut instance); + let (success, maybe_error) = try_func!(larodGetDeviceInstance, pointer, &mut instance); if success { debug_assert!( - matches!(error.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodGetDeviceInstance returned success AND returned an error!" ); Ok(instance) } else { - Err(error) + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } } } impl TryFrom<*const larodDevice> for LarodDevice { - type Error = LarodError; + type Error = Error; fn try_from(value: *const larodDevice) -> Result { let name = LarodDevice::larod_get_name(value)?; let id = LarodDevice::larod_get_instance(value)?; @@ -421,27 +477,32 @@ impl SessionBuilder { } pub fn build(&self) -> Result { let mut conn: *mut larodConnection = ptr::null_mut(); - let (success, e): (bool, LarodError) = unsafe { try_func!(larodConnect, &mut conn) }; + let (success, maybe_error): (bool, Option) = + unsafe { try_func!(larodConnect, &mut conn) }; if success { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodConnect indicated success AND returned an error!" ); Ok(Session { conn, model_map: HashMap::new(), - devices: Vec::new(), }) } else { - Err(e) + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } } +impl Default for SessionBuilder { + fn default() -> Self { + SessionBuilder::new() + } +} + pub struct Session { conn: *mut larodConnection, model_map: HashMap, - devices: Vec, } // Using a session builder might not be necessary. @@ -460,47 +521,57 @@ impl Session { SessionBuilder::new() } pub fn disconnect(&mut self) -> Result<()> { - let (success, e): (bool, LarodError) = + let (success, maybe_error): (bool, Option) = unsafe { try_func!(larodDisconnect, &mut self.conn) }; if success { debug_assert!( - matches!(e.code, LarodErrorCode::NONE), + maybe_error.is_none(), "larodDisconnect indicated success AND returned an error!" ); Ok(()) } else { - Err(e) + Err(maybe_error.unwrap_or(Error::MissingLarodError)) } } pub fn num_sessions() -> Result<()> { Ok(()) } - pub fn device() -> Result<()> { - Ok(()) - } + + /// Returns a reference to an available device + // pub fn get_device(&self, name: &str) -> Option<&LarodDevice> { + // self.devices.get(name) + // } + pub fn list_chips() -> Result<()> { Ok(()) } - pub fn list_devices(&mut self) -> Result> { + + /// Get a reference to a HashMap of name LarodDevice pairs. + pub fn get_devices(&self) -> Result> { let mut num_devices: usize = 0; - let (d, e) = unsafe { - let (dev_ptr, e) = try_func!(larodListDevices, self.conn, &mut num_devices); - if dev_ptr.is_null() { - return Err(e); - } - let raw_devices = unsafe { slice::from_raw_parts(dev_ptr, num_devices) }; - let mut devices: Vec = Vec::with_capacity(num_devices); - for (idx, raw_device) in raw_devices.into_iter().enumerate() { - let device = LarodDevice::try_from(*raw_device); - match device { - Ok(d) => devices.push(d), - Err(conv_error) => - log::error!("Could not identify larodDevice {} of {} returned from larodListDevices.\n{}", idx, num_devices, conv_error.msg), - } + let (dev_ptr, maybe_error) = + unsafe { try_func!(larodListDevices, self.conn, &mut num_devices) }; + if dev_ptr.is_null() { + return Err(maybe_error.unwrap_or(Error::MissingLarodError)); + } + let raw_devices = unsafe { slice::from_raw_parts(dev_ptr, num_devices) }; + + let devices: Vec = raw_devices.iter().enumerate().filter_map(|(idx, raw_d)| { + match LarodDevice::try_from(*raw_d) { + Ok(d) => Some(d), + Err(Error::LarodError(e)) => { + let error_message = e.msg().unwrap_or(String::from("Error reading error message")); + log::error!("Could not identify larodDevice {} of {} returned from larodListDevices.\n{}", idx, num_devices, &error_message); + None + }, + Err(e) => { + log::error!("Could not identify larodDevice {} of {} returned from larodListDevices.\n{:?}", idx, num_devices, e); + None + }, } - (devices, e) - }; - Ok(d) + }).collect(); + + Ok(devices) } // Overloaded need to check that. @@ -546,6 +617,14 @@ impl Session { } } +impl Default for Session { + fn default() -> Self { + SessionBuilder::default() + .build() + .expect("Session::default()") + } +} + impl std::ops::Drop for Session { fn drop(&mut self) { unsafe { @@ -554,7 +633,7 @@ impl std::ops::Drop for Session { } } -#[cfg(test)] +#[cfg(all(test, target_arch = "aarch64", feature = "device-tests"))] mod tests { use super::*; use std::ptr; @@ -576,21 +655,75 @@ mod tests { map.set_string("test_key", "test_value").unwrap(); } + #[test] + fn larod_map_can_get_str() { + let mut map = LarodMap::new().unwrap(); + map.set_string("test_key", "this_value").unwrap(); + let s = map.get_string("test_key").unwrap(); + assert_eq!(s, String::from("this_value")); + } + #[test] fn larod_map_can_set_int() { let mut map = LarodMap::new().unwrap(); map.set_int("test_key", 10).unwrap(); } + #[test] + fn larod_map_can_get_int() { + let mut map = LarodMap::new().unwrap(); + map.set_int("test_key", 9).unwrap(); + let i = map.get_int("test_key").unwrap(); + assert_eq!(i, 9); + } + #[test] fn larod_map_can_set_2_tuple() { let mut map = LarodMap::new().unwrap(); map.set_int_arr2("test_key", (1, 2)).unwrap(); } + #[test] + fn larod_map_can_get_2_tuple() { + let mut map = LarodMap::new().unwrap(); + map.set_int_arr2("test_key", (5, 6)).unwrap(); + let arr = map.get_int_arr2("test_key").unwrap(); + assert_eq!(arr[0], 5); + assert_eq!(arr[1], 6); + } #[test] fn larod_map_can_set_4_tuple() { let mut map = LarodMap::new().unwrap(); map.set_int_arr4("test_key", (1, 2, 3, 4)).unwrap(); } + + #[test] + fn larod_map_can_get_4_tuple() { + let mut map = LarodMap::new().unwrap(); + map.set_int_arr4("test_key", (1, 2, 3, 4)).unwrap(); + let arr = map.get_int_arr4("test_key").unwrap(); + assert_eq!(arr[0], 1); + assert_eq!(arr[1], 2); + assert_eq!(arr[2], 3); + assert_eq!(arr[3], 4); + } + + #[test] + fn it_establishes_session() { + let sess = Session::new(); + } + + #[test] + fn it_lists_devices() { + let sess = Session::new(); + let devices = sess.devices().unwrap(); + for device in devices { + println!( + "device: {}, id: {}, addr: {:?}", + device.get_name(), + device.id, + unsafe { std::ptr::addr_of!(*device.ptr) }, + ); + } + } } From 4309ed684fa1daa2305fdf2193c1e9f99a74098f Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Tue, 5 Nov 2024 21:36:20 -0500 Subject: [PATCH 17/54] Fix test failure due to renamed method. --- crates/larod/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 4519ce7d..1b436df7 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -716,7 +716,7 @@ mod tests { #[test] fn it_lists_devices() { let sess = Session::new(); - let devices = sess.devices().unwrap(); + let devices = sess.get_devices().unwrap(); for device in devices { println!( "device: {}, id: {}, addr: {:?}", From de2367be7a379c1e6ecb308c4eb454ab7a6c1f83 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Tue, 5 Nov 2024 21:51:25 -0500 Subject: [PATCH 18/54] Remove note that is no longer needed. --- remote-test.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/remote-test.sh b/remote-test.sh index 78b8fadf..be6a1f82 100644 --- a/remote-test.sh +++ b/remote-test.sh @@ -6,5 +6,4 @@ if [ -n "${CARGO_TEST_CAMERA}" ]; then # echo $f ssh $CARGO_TEST_CAMERA "chmod +x /root/$f" ssh $CARGO_TEST_CAMERA "/root/$f" - # ^ note: may need to change this line, see https://stackoverflow.com/q/9379400 fi From 1dc68960cbd242023df0cb2f5ea0a12325e01253 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Tue, 5 Nov 2024 21:55:26 -0500 Subject: [PATCH 19/54] Run executable is no camera. --- remote-test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/remote-test.sh b/remote-test.sh index be6a1f82..652a9984 100644 --- a/remote-test.sh +++ b/remote-test.sh @@ -6,4 +6,6 @@ if [ -n "${CARGO_TEST_CAMERA}" ]; then # echo $f ssh $CARGO_TEST_CAMERA "chmod +x /root/$f" ssh $CARGO_TEST_CAMERA "/root/$f" +else + $1 fi From 55a71586464a9394b99b6d4e1fcfd122c0c68f78 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 7 Nov 2024 11:09:34 -0500 Subject: [PATCH 20/54] Explicitly tie the lifetime of LarodDevice to the lifetime of the Session from which it was acquired. --- crates/larod/src/lib.rs | 55 ++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 1b436df7..c03a6254 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -28,6 +28,7 @@ use larod_sys::*; use std::{ collections::HashMap, ffi::{c_char, c_int, CStr, CString}, + marker::PhantomData, ptr::{self, slice_from_raw_parts}, slice::from_raw_parts, }; @@ -397,17 +398,31 @@ impl std::ops::Drop for LarodMap { } /// A type representing a larodDevice. +/// The lifetime of LarodDevice is explicitly tied to the lifetime of a +/// [Session]. So using a LarodDevice after the Session it was acquired from +/// will cause compilation to fail. +/// ```compile_fail +/// let sess = Session::new(); +/// let first_device = sess +/// .get_devices() +/// .expect("unable to get devices") +/// .pop() +/// .expect("empty devices list!"); +/// drop(sess); +/// println!("{:?}", first_device.get_name()); +/// ``` #[derive(Debug)] -pub struct LarodDevice { +pub struct LarodDevice<'a> { // The caller does not get ownership of the returned pointer and must not // attempt to free it. The lifetime of the memory pointed to expires when // conn closes. ptr: *const larodDevice, name: String, id: u32, + phantom: PhantomData<&'a Session<'a>>, } -impl LarodDevice { +impl<'a> LarodDevice<'a> { /// Get the name of a larodDevice. pub fn get_name(&self) -> &str { &self.name @@ -456,7 +471,7 @@ impl LarodDevice { } } -impl TryFrom<*const larodDevice> for LarodDevice { +impl<'a> TryFrom<*const larodDevice> for LarodDevice<'a> { type Error = Error; fn try_from(value: *const larodDevice) -> Result { let name = LarodDevice::larod_get_name(value)?; @@ -465,6 +480,7 @@ impl TryFrom<*const larodDevice> for LarodDevice { ptr: value, name, id, + phantom: PhantomData, }) } } @@ -475,7 +491,7 @@ impl SessionBuilder { pub fn new() -> SessionBuilder { SessionBuilder {} } - pub fn build(&self) -> Result { + pub fn build(&self) -> Result> { let mut conn: *mut larodConnection = ptr::null_mut(); let (success, maybe_error): (bool, Option) = unsafe { try_func!(larodConnect, &mut conn) }; @@ -487,6 +503,7 @@ impl SessionBuilder { Ok(Session { conn, model_map: HashMap::new(), + phantom: PhantomData, }) } else { Err(maybe_error.unwrap_or(Error::MissingLarodError)) @@ -500,21 +517,22 @@ impl Default for SessionBuilder { } } -pub struct Session { +pub struct Session<'a> { conn: *mut larodConnection, model_map: HashMap, + phantom: PhantomData<&'a larodConnection>, } // Using a session builder might not be necessary. // There's little to configure when starting a session. -impl Session { +impl<'a> Session<'a> { /// Constructs a new `Session`. /// /// # Panics /// /// Use `Session::builder()` if you wish to handle the failure as an `Error` /// instead of panicking. - pub fn new() -> Session { + pub fn new() -> Session<'a> { SessionBuilder::new().build().expect("Session::new()") } pub fn builder() -> SessionBuilder { @@ -538,9 +556,22 @@ impl Session { } /// Returns a reference to an available device - // pub fn get_device(&self, name: &str) -> Option<&LarodDevice> { - // self.devices.get(name) - // } + pub fn get_device(&self, name: &str, instance: u32) -> Result { + let Ok(name_cstr) = CString::new(name) else { + return Err(Error::CStringAllocation); + }; + let (device_ptr, maybe_error) = + unsafe { try_func!(larodGetDevice, self.conn, name_cstr.as_ptr(), instance) }; + if !device_ptr.is_null() { + debug_assert!( + maybe_error.is_none(), + "larodGetDevice indicated success AND returned an error!" + ); + Ok(LarodDevice::try_from(device_ptr)?) + } else { + Err(maybe_error.unwrap_or(Error::MissingLarodError)) + } + } pub fn list_chips() -> Result<()> { Ok(()) @@ -617,7 +648,7 @@ impl Session { } } -impl Default for Session { +impl<'a> Default for Session<'a> { fn default() -> Self { SessionBuilder::default() .build() @@ -625,7 +656,7 @@ impl Default for Session { } } -impl std::ops::Drop for Session { +impl<'a> std::ops::Drop for Session<'a> { fn drop(&mut self) { unsafe { try_func!(larodDisconnect, &mut self.conn); From b294ca3938681e97db18b9bedc6e2970b81d0188 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 8 Nov 2024 21:01:09 -0500 Subject: [PATCH 21/54] Don't copy larodDevice data from C to Rust, just provide access via functions. --- crates/larod/src/lib.rs | 73 ++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 49 deletions(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index c03a6254..25dff682 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -15,6 +15,7 @@ //! //! Example //! ```rust +//! use larod::Session; //! let session = Session::new(); //! let devices = session.devices(); //! ``` @@ -402,6 +403,7 @@ impl std::ops::Drop for LarodMap { /// [Session]. So using a LarodDevice after the Session it was acquired from /// will cause compilation to fail. /// ```compile_fail +/// use larod::Session; /// let sess = Session::new(); /// let first_device = sess /// .get_devices() @@ -417,27 +419,14 @@ pub struct LarodDevice<'a> { // attempt to free it. The lifetime of the memory pointed to expires when // conn closes. ptr: *const larodDevice, - name: String, - id: u32, phantom: PhantomData<&'a Session<'a>>, } impl<'a> LarodDevice<'a> { /// Get the name of a larodDevice. - pub fn get_name(&self) -> &str { - &self.name - } - - /// Get the instance of a larodDevice. - /// From the larod documentation - /// > *In case there are multiple identical devices that are available in the service, they are distinguished by an instance number, with the first instance starting from zero.* - pub fn get_instance(&self) -> u32 { - self.id - } - - fn larod_get_name(pointer: *const larodDevice) -> Result { + fn get_name(&self) -> Result { unsafe { - let (c_char_ptr, maybe_error) = try_func!(larodGetDeviceName, pointer); + let (c_char_ptr, maybe_error) = try_func!(larodGetDeviceName, self.ptr); if !c_char_ptr.is_null() { debug_assert!( maybe_error.is_none(), @@ -454,10 +443,13 @@ impl<'a> LarodDevice<'a> { } } - fn larod_get_instance(pointer: *const larodDevice) -> Result { + /// Get the instance of a larodDevice. + /// From the larod documentation + /// > *In case there are multiple identical devices that are available in the service, they are distinguished by an instance number, with the first instance starting from zero.* + fn get_instance(&self) -> Result { unsafe { let mut instance: u32 = 0; - let (success, maybe_error) = try_func!(larodGetDeviceInstance, pointer, &mut instance); + let (success, maybe_error) = try_func!(larodGetDeviceInstance, self.ptr, &mut instance); if success { debug_assert!( maybe_error.is_none(), @@ -471,20 +463,6 @@ impl<'a> LarodDevice<'a> { } } -impl<'a> TryFrom<*const larodDevice> for LarodDevice<'a> { - type Error = Error; - fn try_from(value: *const larodDevice) -> Result { - let name = LarodDevice::larod_get_name(value)?; - let id = LarodDevice::larod_get_instance(value)?; - Ok(Self { - ptr: value, - name, - id, - phantom: PhantomData, - }) - } -} - pub struct SessionBuilder {} impl SessionBuilder { @@ -567,7 +545,10 @@ impl<'a> Session<'a> { maybe_error.is_none(), "larodGetDevice indicated success AND returned an error!" ); - Ok(LarodDevice::try_from(device_ptr)?) + Ok(LarodDevice { + ptr: device_ptr, + phantom: PhantomData, + }) } else { Err(maybe_error.unwrap_or(Error::MissingLarodError)) } @@ -585,22 +566,16 @@ impl<'a> Session<'a> { if dev_ptr.is_null() { return Err(maybe_error.unwrap_or(Error::MissingLarodError)); } - let raw_devices = unsafe { slice::from_raw_parts(dev_ptr, num_devices) }; - - let devices: Vec = raw_devices.iter().enumerate().filter_map(|(idx, raw_d)| { - match LarodDevice::try_from(*raw_d) { - Ok(d) => Some(d), - Err(Error::LarodError(e)) => { - let error_message = e.msg().unwrap_or(String::from("Error reading error message")); - log::error!("Could not identify larodDevice {} of {} returned from larodListDevices.\n{}", idx, num_devices, &error_message); - None - }, - Err(e) => { - log::error!("Could not identify larodDevice {} of {} returned from larodListDevices.\n{:?}", idx, num_devices, e); - None - }, - } - }).collect(); + let raw_devices = + unsafe { slice::from_raw_parts::<'a, *const larodDevice>(dev_ptr, num_devices) }; + + let devices: Vec = raw_devices + .iter() + .map(|ptr| LarodDevice { + ptr: *ptr, + phantom: PhantomData, + }) + .collect(); Ok(devices) } @@ -751,7 +726,7 @@ mod tests { for device in devices { println!( "device: {}, id: {}, addr: {:?}", - device.get_name(), + device.get_name().unwrap(), device.id, unsafe { std::ptr::addr_of!(*device.ptr) }, ); From bdeda8127c1499f07132bd3cb4c0496df3eb0445 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 8 Nov 2024 21:01:44 -0500 Subject: [PATCH 22/54] Unwrap result for test listing devices. --- crates/larod/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 25dff682..2b49ad98 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -727,7 +727,7 @@ mod tests { println!( "device: {}, id: {}, addr: {:?}", device.get_name().unwrap(), - device.id, + device.get_instance().unwrap(), unsafe { std::ptr::addr_of!(*device.ptr) }, ); } From 1eb3252c84a3a7f694d0f020b86b8e19f6a3929a Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 8 Nov 2024 21:04:04 -0500 Subject: [PATCH 23/54] Make LarodDevice.get_name() and LarodDevice.get_instance() public. Incorporate a few clippy lints. --- crates/larod/src/lib.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 2b49ad98..79c47e74 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -24,14 +24,13 @@ //! - [ ] [larodDisconnect](https://axiscommunications.github.io/acap-documentation/docs/api/src/api/larod/html/larod_8h.html#ab8f97b4b4d15798384ca25f32ca77bba) //! indicates it may fail to "kill a session." What are the implications if it fails to kill a session? Can we clear the sessions? -use core::{num, slice}; +use core::slice; use larod_sys::*; use std::{ collections::HashMap, - ffi::{c_char, c_int, CStr, CString}, + ffi::{c_char, CStr, CString}, marker::PhantomData, - ptr::{self, slice_from_raw_parts}, - slice::from_raw_parts, + ptr::{self}, }; type Result = std::result::Result; @@ -424,7 +423,7 @@ pub struct LarodDevice<'a> { impl<'a> LarodDevice<'a> { /// Get the name of a larodDevice. - fn get_name(&self) -> Result { + pub fn get_name(&self) -> Result { unsafe { let (c_char_ptr, maybe_error) = try_func!(larodGetDeviceName, self.ptr); if !c_char_ptr.is_null() { @@ -435,8 +434,8 @@ impl<'a> LarodDevice<'a> { let c_name = CStr::from_ptr(c_char_ptr); c_name .to_str() - .map(|n| String::from(n)) - .map_err(|e| Error::InvalidLarodMessage) + .map(String::from) + .map_err(|_e| Error::InvalidLarodMessage) } else { Err(maybe_error.unwrap_or(Error::MissingLarodError)) } @@ -446,7 +445,7 @@ impl<'a> LarodDevice<'a> { /// Get the instance of a larodDevice. /// From the larod documentation /// > *In case there are multiple identical devices that are available in the service, they are distinguished by an instance number, with the first instance starting from zero.* - fn get_instance(&self) -> Result { + pub fn get_instance(&self) -> Result { unsafe { let mut instance: u32 = 0; let (success, maybe_error) = try_func!(larodGetDeviceInstance, self.ptr, &mut instance); @@ -642,7 +641,6 @@ impl<'a> std::ops::Drop for Session<'a> { #[cfg(all(test, target_arch = "aarch64", feature = "device-tests"))] mod tests { use super::*; - use std::ptr; #[test] fn it_creates_larod_map() { From 7941c92a9079877a2d8c35017cb3b2bb8fb3df6e Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 8 Nov 2024 21:58:48 -0500 Subject: [PATCH 24/54] Align getters/setters with RFC344. https://github.com/rust-lang/rfcs/blob/master/text/0344-conventions-galore.md#gettersetter-apis --- crates/larod/examples/basic.rs | 8 ++++++-- crates/larod/src/lib.rs | 12 ++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/crates/larod/examples/basic.rs b/crates/larod/examples/basic.rs index 9febfc69..96316088 100644 --- a/crates/larod/examples/basic.rs +++ b/crates/larod/examples/basic.rs @@ -2,7 +2,7 @@ use larod::{Error, Session}; fn main() -> Result<(), Error> { let session = Session::new(); - let devices = match session.get_devices() { + let devices = match session.devices() { Ok(d) => d, Err(Error::LarodError(e)) => { if let Ok(msg) = e.msg() { @@ -19,7 +19,11 @@ fn main() -> Result<(), Error> { }; println!("Devices:"); for d in devices { - println!("{} ({})", d.get_name(), d.get_instance()); + println!( + "{} ({})", + d.name().expect("Couldn't get device name"), + d.instance().expect("Couldn't get device instance") + ); } Ok(()) } diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 79c47e74..8905fe09 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -405,12 +405,12 @@ impl std::ops::Drop for LarodMap { /// use larod::Session; /// let sess = Session::new(); /// let first_device = sess -/// .get_devices() +/// .devices() /// .expect("unable to get devices") /// .pop() /// .expect("empty devices list!"); /// drop(sess); -/// println!("{:?}", first_device.get_name()); +/// println!("{:?}", first_device.name()); /// ``` #[derive(Debug)] pub struct LarodDevice<'a> { @@ -423,7 +423,7 @@ pub struct LarodDevice<'a> { impl<'a> LarodDevice<'a> { /// Get the name of a larodDevice. - pub fn get_name(&self) -> Result { + pub fn name(&self) -> Result { unsafe { let (c_char_ptr, maybe_error) = try_func!(larodGetDeviceName, self.ptr); if !c_char_ptr.is_null() { @@ -445,7 +445,7 @@ impl<'a> LarodDevice<'a> { /// Get the instance of a larodDevice. /// From the larod documentation /// > *In case there are multiple identical devices that are available in the service, they are distinguished by an instance number, with the first instance starting from zero.* - pub fn get_instance(&self) -> Result { + pub fn instance(&self) -> Result { unsafe { let mut instance: u32 = 0; let (success, maybe_error) = try_func!(larodGetDeviceInstance, self.ptr, &mut instance); @@ -533,7 +533,7 @@ impl<'a> Session<'a> { } /// Returns a reference to an available device - pub fn get_device(&self, name: &str, instance: u32) -> Result { + pub fn device(&self, name: &str, instance: u32) -> Result { let Ok(name_cstr) = CString::new(name) else { return Err(Error::CStringAllocation); }; @@ -558,7 +558,7 @@ impl<'a> Session<'a> { } /// Get a reference to a HashMap of name LarodDevice pairs. - pub fn get_devices(&self) -> Result> { + pub fn devices(&self) -> Result> { let mut num_devices: usize = 0; let (dev_ptr, maybe_error) = unsafe { try_func!(larodListDevices, self.conn, &mut num_devices) }; From 59b9ac539af6a54afdf769b9219fcb045ed38415 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 8 Nov 2024 22:13:22 -0500 Subject: [PATCH 25/54] Fix getter names missed in previous commit. --- crates/larod/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 8905fe09..4cf2789b 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -720,12 +720,12 @@ mod tests { #[test] fn it_lists_devices() { let sess = Session::new(); - let devices = sess.get_devices().unwrap(); + let devices = sess.devices().unwrap(); for device in devices { println!( "device: {}, id: {}, addr: {:?}", - device.get_name().unwrap(), - device.get_instance().unwrap(), + device.name().unwrap(), + device.instance().unwrap(), unsafe { std::ptr::addr_of!(*device.ptr) }, ); } From 4373b188c8035421a9701618e78b269179242253 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 8 Nov 2024 22:13:41 -0500 Subject: [PATCH 26/54] Start stubbing LarodModel. --- crates/larod/src/lib.rs | 101 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 10 deletions(-) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index 4cf2789b..bd4aed67 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -25,11 +25,15 @@ //! indicates it may fail to "kill a session." What are the implications if it fails to kill a session? Can we clear the sessions? use core::slice; +pub use larod_sys::larodAccess as LarodAccess; use larod_sys::*; use std::{ collections::HashMap, ffi::{c_char, CStr, CString}, + fs::File, marker::PhantomData, + os::fd::AsRawFd, + path::Path, ptr::{self}, }; @@ -108,6 +112,7 @@ pub enum Error { PointerToInvalidData, CStringAllocation, MissingLarodError, + IOError(std::io::Error), } // impl LarodError { @@ -462,6 +467,49 @@ impl<'a> LarodDevice<'a> { } } +pub struct LarodModel { + ptr: *mut larodModel, +} + +impl LarodModel { + pub fn id() -> Result<()> { + Ok(()) + } + pub fn chip() -> Result<()> { + Ok(()) + } + pub fn device() -> Result<()> { + Ok(()) + } + pub fn size() -> Result<()> { + Ok(()) + } + pub fn name() -> Result<()> { + Ok(()) + } + pub fn access() -> Result<()> { + Ok(()) + } + pub fn num_inputs() -> Result<()> { + Ok(()) + } + pub fn num_outputs() -> Result<()> { + Ok(()) + } + pub fn create_model_inputs() -> Result<()> { + Ok(()) + } + pub fn create_model_outputs() -> Result<()> { + Ok(()) + } +} + +impl Drop for LarodModel { + fn drop(&mut self) { + unsafe { larodDestroyModel(&mut self.ptr) }; + } +} + pub struct SessionBuilder {} impl SessionBuilder { @@ -580,20 +628,53 @@ impl<'a> Session<'a> { } // Overloaded need to check that. - pub fn load_model(&mut self) -> Result<()> { - // let model_fd: c_int = 0; - // let (m, e) = unsafe { - // try_func!(larodLoadModel, &mut self.conn, model_fd, ) - // } - Ok(()) + pub fn load_model>( + &self, + path: T, + name: &str, + device: &LarodDevice, + access: &LarodAccess, + params: &LarodMap, + ) -> Result { + let file = File::open(path).map_err(Error::IOError)?; + let name_cstr = CString::new(name).map_err(|_e| Error::CStringAllocation)?; + let (model_ptr, maybe_error) = unsafe { + try_func!( + larodLoadModel, + self.conn, + file.as_raw_fd(), + device.ptr, + *access, + name_cstr.as_ptr(), + params.raw + ) + }; + if !model_ptr.is_null() { + debug_assert!( + maybe_error.is_none(), + "larodLoadModel indicated success AND returned an error!" + ); + Ok(LarodModel { ptr: model_ptr }) + } else { + Err(maybe_error.unwrap_or(Error::MissingLarodError)) + } } - pub fn get_model() -> Result<()> { - Ok(()) + pub fn get_model(&self, model_id: u64) -> Result { + let (model_ptr, maybe_error) = unsafe { try_func!(larodGetModel, self.conn, model_id) }; + if !model_ptr.is_null() { + debug_assert!( + maybe_error.is_none(), + "larodGetModel indicated success AND returned an error!" + ); + Ok(LarodModel { ptr: model_ptr }) + } else { + Err(maybe_error.unwrap_or(Error::MissingLarodError)) + } } - pub fn get_models() -> Result<()> { + pub fn models() -> Result<()> { Ok(()) } - pub fn delete_model() -> Result<()> { + pub fn delete_model(&self) -> Result<()> { Ok(()) } pub fn alloc_model_inputs() -> Result<()> { From cdaf42ba42af188898021f25ac0071f96bec482f Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 14 Nov 2024 21:30:25 -0500 Subject: [PATCH 27/54] Remove `bin` crate type from [[example]] --- crates/larod/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/larod/Cargo.toml b/crates/larod/Cargo.toml index 1cf2aa44..d6005897 100644 --- a/crates/larod/Cargo.toml +++ b/crates/larod/Cargo.toml @@ -12,5 +12,4 @@ log.workspace = true device-tests = [] [[example]] -name = "basic" -crate-type = ["bin"] \ No newline at end of file +name = "basic" \ No newline at end of file From ff868cbe5954123e461b2459c09af946571bc8ef Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 14 Nov 2024 21:31:32 -0500 Subject: [PATCH 28/54] Stub out Tensor struct. --- crates/larod/src/lib.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs index bd4aed67..fff2ee33 100644 --- a/crates/larod/src/lib.rs +++ b/crates/larod/src/lib.rs @@ -402,6 +402,37 @@ impl std::ops::Drop for LarodMap { } } +#[derive(Eq, PartialEq, Hash)] +pub struct Tensor<'a> { + ptr: *mut larodTensor, + phantom: PhantomData<&'a Session<'a>>, +} + +/// A structure representing a larodTensor. +impl<'a> Tensor<'a> { + fn as_ptr(&self) -> *const larodTensor { + self.ptr.cast_const() + } + pub fn name() {} + pub fn byte_size() {} + pub fn dims() {} + pub fn set_dims() {} + pub fn pitches() {} + pub fn set_pitches() {} + pub fn data_type() {} + pub fn set_data_type() {} + pub fn layout() {} + pub fn set_layout() {} + pub fn fd() {} + pub fn set_fd() {} + pub fn fd_size() {} + pub fn set_fd_size() {} + pub fn fd_offset() {} + pub fn set_fd_offset() {} + pub fn fd_props() {} + pub fn set_fd_props() {} +} + /// A type representing a larodDevice. /// The lifetime of LarodDevice is explicitly tied to the lifetime of a /// [Session]. So using a LarodDevice after the Session it was acquired from From c30c1a0305d48e2beeae5940f3f8fcb9c0a279ab Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 15 Nov 2024 23:51:45 -0500 Subject: [PATCH 29/54] Add vdo-sys --- Cargo.toml | 1 + crates/vdo-sys/build.rs | 26 + crates/vdo-sys/src/bindings.rs | 1019 ++++++++++++++++++++++++++++++++ crates/vdo-sys/src/lib.rs | 31 + crates/vdo-sys/wrapper.h | 4 + 5 files changed, 1081 insertions(+) create mode 100644 crates/vdo-sys/build.rs create mode 100644 crates/vdo-sys/src/bindings.rs create mode 100644 crates/vdo-sys/src/lib.rs create mode 100644 crates/vdo-sys/wrapper.h diff --git a/Cargo.toml b/Cargo.toml index 0bda46b3..f94f7bc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ licensekey = { path = "crates/licensekey" } licensekey-sys = { path = "crates/licensekey-sys" } mdb = { path = "crates/mdb" } mdb-sys = { path = "crates/mdb-sys" } +vdo-sys = { path = "crates/vdo-sys" } [workspace.package] edition = "2021" diff --git a/crates/vdo-sys/build.rs b/crates/vdo-sys/build.rs new file mode 100644 index 00000000..d950addc --- /dev/null +++ b/crates/vdo-sys/build.rs @@ -0,0 +1,26 @@ +use std::{env, path}; + +fn populated_bindings(dst: &path::PathBuf) { + let library = pkg_config::Config::new().probe("vdo").unwrap(); + let mut bindings = bindgen::Builder::default() + .header("wrapper.h") + .allowlist_recursively(false) + .allowlist_function("^(vdo.*)$") + .allowlist_type("^(_?Vdo.*)$") + .default_enum_style(bindgen::EnumVariation::Rust { + non_exhaustive: true, + }) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .layout_tests(false); + for path in library.include_paths { + bindings = bindings.clang_args(&["-F", path.to_str().unwrap()]); + } + bindings.generate().unwrap().write_to_file(dst).unwrap(); +} + +fn main() { + let dst = path::PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); + if env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default() != "x86_64" { + populated_bindings(&dst); + } +} diff --git a/crates/vdo-sys/src/bindings.rs b/crates/vdo-sys/src/bindings.rs new file mode 100644 index 00000000..e6ad9fbb --- /dev/null +++ b/crates/vdo-sys/src/bindings.rs @@ -0,0 +1,1019 @@ +/* automatically generated by rust-bindgen 0.69.4 */ + +#[repr(C)] +#[derive(Default)] +pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); +impl __IncompleteArrayField { + #[inline] + pub const fn new() -> Self { + __IncompleteArrayField(::std::marker::PhantomData, []) + } + #[inline] + pub fn as_ptr(&self) -> *const T { + self as *const _ as *const T + } + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self as *mut _ as *mut T + } + #[inline] + pub unsafe fn as_slice(&self, len: usize) -> &[T] { + ::std::slice::from_raw_parts(self.as_ptr(), len) + } + #[inline] + pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { + ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) + } +} +impl ::std::fmt::Debug for __IncompleteArrayField { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + fmt.write_str("__IncompleteArrayField") + } +} +#[repr(C)] +pub struct __BindgenUnionField(::std::marker::PhantomData); +impl __BindgenUnionField { + #[inline] + pub const fn new() -> Self { + __BindgenUnionField(::std::marker::PhantomData) + } + #[inline] + pub unsafe fn as_ref(&self) -> &T { + ::std::mem::transmute(self) + } + #[inline] + pub unsafe fn as_mut(&mut self) -> &mut T { + ::std::mem::transmute(self) + } +} +impl ::std::default::Default for __BindgenUnionField { + #[inline] + fn default() -> Self { + Self::new() + } +} +impl ::std::clone::Clone for __BindgenUnionField { + #[inline] + fn clone(&self) -> Self { + *self + } +} +impl ::std::marker::Copy for __BindgenUnionField {} +impl ::std::fmt::Debug for __BindgenUnionField { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + fmt.write_str("__BindgenUnionField") + } +} +impl ::std::hash::Hash for __BindgenUnionField { + fn hash(&self, _state: &mut H) {} +} +impl ::std::cmp::PartialEq for __BindgenUnionField { + fn eq(&self, _other: &__BindgenUnionField) -> bool { + true + } +} +impl ::std::cmp::Eq for __BindgenUnionField {} +#[repr(i32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoWdrMode { + VDO_WDR_MODE_NONE = -1, + VDO_WDR_MODE_LINEAR = 0, + VDO_WDR_MODE_2X = 1, + VDO_WDR_MODE_3X = 2, + VDO_WDR_MODE_4X = 3, + VDO_WDR_MODE_SENSOR = 4, +} +#[repr(i32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoFormat { + VDO_FORMAT_NONE = -1, + VDO_FORMAT_H264 = 0, + VDO_FORMAT_H265 = 1, + VDO_FORMAT_JPEG = 2, + VDO_FORMAT_YUV = 3, + VDO_FORMAT_BAYER = 4, + VDO_FORMAT_IVS = 5, + VDO_FORMAT_RAW = 6, + VDO_FORMAT_RGBA = 7, + VDO_FORMAT_RGB = 8, + VDO_FORMAT_PLANAR_RGB = 9, +} +#[repr(i32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoH264Profile { + VDO_H264_PROFILE_NONE = -1, + VDO_H264_PROFILE_BASELINE = 0, + VDO_H264_PROFILE_MAIN = 1, + VDO_H264_PROFILE_HIGH = 2, +} +#[repr(i32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoH265Profile { + VDO_H265_PROFILE_NONE = -1, + VDO_H265_PROFILE_MAIN = 0, + VDO_H265_PROFILE_MAIN_10 = 1, +} +#[repr(i32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoRateControlMode { + VDO_RATE_CONTROL_MODE_NONE = -1, + VDO_RATE_CONTROL_MODE_CBR = 0, + VDO_RATE_CONTROL_MODE_VBR = 1, + VDO_RATE_CONTROL_MODE_MBR = 2, + VDO_RATE_CONTROL_MODE_ABR = 3, +} +#[repr(i32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoRateControlPriority { + VDO_RATE_CONTROL_PRIORITY_NONE = -1, + VDO_RATE_CONTROL_PRIORITY_FRAMERATE = 0, + VDO_RATE_CONTROL_PRIORITY_QUALITY = 1, + VDO_RATE_CONTROL_PRIORITY_FULL_FRAMERATE = 2, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoFrameType { + VDO_FRAME_TYPE_NONE = 0, + VDO_FRAME_TYPE_H264_SPS = 1, + VDO_FRAME_TYPE_H264_PPS = 2, + VDO_FRAME_TYPE_H264_SEI = 3, + VDO_FRAME_TYPE_H264_IDR = 4, + VDO_FRAME_TYPE_H264_I = 5, + VDO_FRAME_TYPE_H264_P = 6, + VDO_FRAME_TYPE_H264_B = 7, + VDO_FRAME_TYPE_H265_SPS = 8, + VDO_FRAME_TYPE_H265_PPS = 9, + VDO_FRAME_TYPE_H265_VPS = 10, + VDO_FRAME_TYPE_H265_SEI = 11, + VDO_FRAME_TYPE_H265_IDR = 12, + VDO_FRAME_TYPE_H265_I = 13, + VDO_FRAME_TYPE_H265_P = 14, + VDO_FRAME_TYPE_H265_B = 15, + VDO_FRAME_TYPE_JPEG = 16, + VDO_FRAME_TYPE_YUV = 17, + VDO_FRAME_TYPE_RAW = 18, + VDO_FRAME_TYPE_RGBA = 19, + VDO_FRAME_TYPE_RGB = 20, + VDO_FRAME_TYPE_PLANAR_RGB = 21, +} +#[repr(i32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoZipStreamProfile { + VDO_ZIPSTREAM_PROFILE_NONE = -1, + VDO_ZIPSTREAM_PROFILE_CLASSIC = 0, + VDO_ZIPSTREAM_PROFILE_STORAGE = 1, + VDO_ZIPSTREAM_PROFILE_LIVE = 2, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoChunkType { + VDO_CHUNK_NONE = 0, + VDO_CHUNK_ERROR = 2147483648, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoChunkOption { + VDO_CHUNK_OPTION_NONE = 0, + VDO_CHUNK_OPTION_MMAP = 2147483648, +} +#[repr(C)] +pub struct VdoChunk { + pub data: gpointer, + pub size: gsize, + pub type_: VdoChunkType, + pub offset: gint64, +} +#[repr(i32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoOverlayAlign { + VDO_OVERLAY_ALIGN_NONE = -1, + VDO_OVERLAY_ALIGN_TOP = 0, + VDO_OVERLAY_ALIGN_BOTTOM = 1, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoOverlayColor { + VDO_OVERLAY_COLOR_TRANSPARENT = 0, + VDO_OVERLAY_COLOR_BLACK = 61440, + VDO_OVERLAY_COLOR_WHITE = 65535, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoOverlayTextSize { + VDO_OVERLAY_TEXT_SIZE_SMALL = 16, + VDO_OVERLAY_TEXT_SIZE_MEDIUM = 32, + VDO_OVERLAY_TEXT_SIZE_LARGE = 48, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoStreamTimestamp { + VDO_TIMESTAMP_NONE = 0, + VDO_TIMESTAMP_UTC = 1, + VDO_TIMESTAMP_ZIPSTREAM = 2, + VDO_TIMESTAMP_DIFF = 4, + VDO_TIMESTAMP_MONO_CAPTURE = 8, + VDO_TIMESTAMP_MONO_SERVER = 16, + VDO_TIMESTAMP_MONO_CLIENT = 32, + VDO_TIMESTAMP_MONO_CLIENT_SERVER_DIFF = 52, + VDO_TIMESTAMP_MONO_CLIENT_CAPTURE_DIFF = 44, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoIntent { + VDO_INTENT_NONE = 0, + VDO_INTENT_CONTROL = 1, + VDO_INTENT_MONITOR = 2, + VDO_INTENT_CONSUME = 4, + VDO_INTENT_PRODUCE = 8, + VDO_INTENT_DEFAULT = 5, + VDO_INTENT_EVENTFD = 16, + VDO_INTENT_UNIVERSE = 4294967295, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoStreamEvent { + VDO_STREAM_EVENT_NONE = 0, + VDO_STREAM_EVENT_STARTED = 1, + VDO_STREAM_EVENT_STOPPED = 2, + VDO_STREAM_EVENT_RESOURCE = 16, + VDO_STREAM_EVENT_QUOTA_SOFT = 17, + VDO_STREAM_EVENT_QUOTA_HARD = 18, + VDO_STREAM_EVENT_ZIPSTREAM = 32, + VDO_STREAM_EVENT_BUFFERING = 64, + VDO_STREAM_EVENT_BUFFERING_WARN = 65, + VDO_STREAM_EVENT_BUFFERING_FAIL = 66, + VDO_STREAM_EVENT_INVALID = 4294967295, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoBufferAccess { + VDO_BUFFER_ACCESS_NONE = 0, + VDO_BUFFER_ACCESS_CPU_RD = 1, + VDO_BUFFER_ACCESS_DEV_RD = 2, + VDO_BUFFER_ACCESS_ANY_RD = 3, + VDO_BUFFER_ACCESS_CPU_WR = 256, + VDO_BUFFER_ACCESS_DEV_WR = 512, + VDO_BUFFER_ACCESS_ANY_WR = 768, + VDO_BUFFER_ACCESS_CPU_RW = 257, + VDO_BUFFER_ACCESS_DEV_RW = 514, + VDO_BUFFER_ACCESS_ANY_RW = 771, +} +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum VdoBufferStrategy { + VDO_BUFFER_STRATEGY_NONE = 0, + VDO_BUFFER_STRATEGY_INPUT = 1, + VDO_BUFFER_STRATEGY_EXTERNAL = 2, + VDO_BUFFER_STRATEGY_EXPLICIT = 3, + VDO_BUFFER_STRATEGY_INFINITE = 4, +} +#[repr(C)] +pub struct VdoMemChunk { + pub data: gpointer, + pub data_size: gsize, +} +#[repr(C)] +pub struct VdoResolution { + pub width: guint32, + pub height: guint32, +} +#[repr(C)] +pub struct VdoResolutionSet { + pub count: gsize, + pub resolutions: __IncompleteArrayField, +} +#[repr(C)] +pub struct VdoRect { + pub width: guint, + pub height: guint, + pub x: guint, + pub y: guint, +} +#[repr(C)] +pub struct VdoPair32i { + pub __bindgen_anon_1: __BindgenUnionField, + pub __bindgen_anon_2: __BindgenUnionField, + pub __bindgen_anon_3: __BindgenUnionField, + pub val: __BindgenUnionField<[gint32; 2usize]>, + pub bindgen_union_field: [u32; 2usize], +} +#[repr(C)] +pub struct VdoPair32i__bindgen_ty_1 { + pub x: gint32, + pub y: gint32, +} +#[repr(C)] +pub struct VdoPair32i__bindgen_ty_2 { + pub w: gint32, + pub h: gint32, +} +#[repr(C)] +pub struct VdoPair32i__bindgen_ty_3 { + pub num: gint32, + pub den: gint32, +} +#[repr(C)] +pub struct VdoPair32u { + pub __bindgen_anon_1: __BindgenUnionField, + pub __bindgen_anon_2: __BindgenUnionField, + pub __bindgen_anon_3: __BindgenUnionField, + pub val: __BindgenUnionField<[guint32; 2usize]>, + pub bindgen_union_field: [u32; 2usize], +} +#[repr(C)] +pub struct VdoPair32u__bindgen_ty_1 { + pub x: guint32, + pub y: guint32, +} +#[repr(C)] +pub struct VdoPair32u__bindgen_ty_2 { + pub w: guint32, + pub h: guint32, +} +#[repr(C)] +pub struct VdoPair32u__bindgen_ty_3 { + pub num: guint32, + pub den: guint32, +} +extern "C" { + pub fn vdo_wdr_mode_get_type() -> GType; +} +extern "C" { + pub fn vdo_format_get_type() -> GType; +} +extern "C" { + pub fn vdo_h264_profile_get_type() -> GType; +} +extern "C" { + pub fn vdo_h265_profile_get_type() -> GType; +} +extern "C" { + pub fn vdo_zipstream_profile_get_type() -> GType; +} +extern "C" { + pub fn vdo_rate_control_mode_get_type() -> GType; +} +extern "C" { + pub fn vdo_rate_control_priority_get_type() -> GType; +} +extern "C" { + pub fn vdo_frame_type_get_type() -> GType; +} +extern "C" { + pub fn vdo_color_get_type() -> GType; +} +extern "C" { + pub fn vdo_timestamp_get_type() -> GType; +} +extern "C" { + pub fn vdo_intent_get_type() -> GType; +} +extern "C" { + pub fn vdo_buffer_access_get_type() -> GType; +} +extern "C" { + pub fn vdo_buffer_strategy_get_type() -> GType; +} +extern "C" { + pub fn vdo_format_to_str(format: VdoFormat) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn vdo_map_get_type() -> GType; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _VdoMap { + _unused: [u8; 0], +} +pub type VdoMap = _VdoMap; +#[repr(C)] +pub struct VdoMapClass { + pub parent_class: GObjectClass, +} +pub type VdoMap_autoptr = *mut VdoMap; +pub type VdoMap_listautoptr = *mut GList; +pub type VdoMap_slistautoptr = *mut GSList; +pub type VdoMap_queueautoptr = *mut GQueue; +pub type VdoMapClass_autoptr = *mut VdoMapClass; +pub type VdoMapClass_listautoptr = *mut GList; +pub type VdoMapClass_slistautoptr = *mut GSList; +pub type VdoMapClass_queueautoptr = *mut GQueue; +extern "C" { + pub fn vdo_map_new() -> *mut VdoMap; +} +extern "C" { + pub fn vdo_map_new_from_variant(dictionary: *mut GVariant) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_map_empty(self_: *const VdoMap) -> gboolean; +} +extern "C" { + pub fn vdo_map_size(self_: *const VdoMap) -> gsize; +} +extern "C" { + pub fn vdo_map_swap(lhs: *mut VdoMap, rhs: *mut VdoMap); +} +extern "C" { + pub fn vdo_map_contains(self_: *const VdoMap, name: *const gchar) -> gboolean; +} +extern "C" { + pub fn vdo_map_contains_va(self_: *const VdoMap, ...) -> gboolean; +} +extern "C" { + pub fn vdo_map_contains_strv(self_: *const VdoMap, names: *const *const gchar) -> gboolean; +} +extern "C" { + pub fn vdo_map_entry_equals( + self_: *const VdoMap, + map: *const VdoMap, + name: *const gchar, + ) -> gboolean; +} +extern "C" { + pub fn vdo_map_entry_updates( + self_: *const VdoMap, + map: *const VdoMap, + name: *const gchar, + ) -> gboolean; +} +extern "C" { + pub fn vdo_map_equals(self_: *const VdoMap, map: *const VdoMap) -> gboolean; +} +extern "C" { + pub fn vdo_map_equals_va(self_: *const VdoMap, map: *const VdoMap, ...) -> gboolean; +} +extern "C" { + pub fn vdo_map_equals_strv( + self_: *const VdoMap, + map: *const VdoMap, + names: *const *const gchar, + ) -> gboolean; +} +extern "C" { + pub fn vdo_map_remove(self_: *mut VdoMap, name: *const gchar); +} +extern "C" { + pub fn vdo_map_remove_va(self_: *mut VdoMap, ...); +} +extern "C" { + pub fn vdo_map_remove_strv(self_: *mut VdoMap, names: *const *const gchar); +} +extern "C" { + pub fn vdo_map_clear(self_: *mut VdoMap); +} +extern "C" { + pub fn vdo_map_filter_prefix(self_: *const VdoMap, prefix: *const gchar) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_map_filter_va(self_: *const VdoMap, ...) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_map_filter_strv(self_: *const VdoMap, names: *const *const gchar) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_map_merge(self_: *mut VdoMap, map: *const VdoMap); +} +extern "C" { + pub fn vdo_map_copy_value(self_: *mut VdoMap, src: *const gchar, dst: *const gchar); +} +extern "C" { + pub fn vdo_map_to_variant(self_: *const VdoMap) -> *mut GVariant; +} +extern "C" { + pub fn vdo_map_dump(self_: *const VdoMap); +} +extern "C" { + pub fn vdo_map_get_variant( + self_: *const VdoMap, + name: *const gchar, + def: *mut GVariant, + ) -> *mut GVariant; +} +extern "C" { + pub fn vdo_map_get_boolean(self_: *const VdoMap, name: *const gchar, def: gboolean) + -> gboolean; +} +extern "C" { + pub fn vdo_map_get_byte(self_: *const VdoMap, name: *const gchar, def: guchar) -> guchar; +} +extern "C" { + pub fn vdo_map_get_int16(self_: *const VdoMap, name: *const gchar, def: gint16) -> gint16; +} +extern "C" { + pub fn vdo_map_get_uint16(self_: *const VdoMap, name: *const gchar, def: guint16) -> guint16; +} +extern "C" { + pub fn vdo_map_get_int32(self_: *const VdoMap, name: *const gchar, def: gint32) -> gint32; +} +extern "C" { + pub fn vdo_map_get_uint32(self_: *const VdoMap, name: *const gchar, def: guint32) -> guint32; +} +extern "C" { + pub fn vdo_map_get_int64(self_: *const VdoMap, name: *const gchar, def: gint64) -> gint64; +} +extern "C" { + pub fn vdo_map_get_uint64(self_: *const VdoMap, name: *const gchar, def: guint64) -> guint64; +} +extern "C" { + pub fn vdo_map_get_double(self_: *const VdoMap, name: *const gchar, def: gdouble) -> gdouble; +} +extern "C" { + pub fn vdo_map_get_string( + self_: *const VdoMap, + name: *const gchar, + size: *mut gsize, + def: *const gchar, + ) -> *const gchar; +} +extern "C" { + pub fn vdo_map_dup_string( + self_: *const VdoMap, + name: *const gchar, + def: *const gchar, + ) -> *mut gchar; +} +extern "C" { + pub fn vdo_map_get_uint32x2( + self_: *const VdoMap, + name: *const gchar, + def: *mut guint32, + ) -> *mut guint32; +} +extern "C" { + pub fn vdo_map_get_uint32x4( + self_: *const VdoMap, + name: *const gchar, + def: *mut guint32, + ) -> *mut guint32; +} +extern "C" { + pub fn vdo_map_get_doublex4( + self_: *const VdoMap, + name: *const gchar, + def: *mut gdouble, + ) -> *mut gdouble; +} +extern "C" { + pub fn vdo_map_get_pair32i( + self_: *const VdoMap, + name: *const gchar, + def: VdoPair32i, + ) -> VdoPair32i; +} +extern "C" { + pub fn vdo_map_get_pair32u( + self_: *const VdoMap, + name: *const gchar, + def: VdoPair32u, + ) -> VdoPair32u; +} +extern "C" { + pub fn vdo_map_set_boolean(self_: *mut VdoMap, name: *const gchar, value: gboolean); +} +extern "C" { + pub fn vdo_map_set_byte(self_: *mut VdoMap, name: *const gchar, value: guchar); +} +extern "C" { + pub fn vdo_map_set_int16(self_: *mut VdoMap, name: *const gchar, value: gint16); +} +extern "C" { + pub fn vdo_map_set_uint16(self_: *mut VdoMap, name: *const gchar, value: guint16); +} +extern "C" { + pub fn vdo_map_set_int32(self_: *mut VdoMap, name: *const gchar, value: gint32); +} +extern "C" { + pub fn vdo_map_set_uint32(self_: *mut VdoMap, name: *const gchar, value: guint32); +} +extern "C" { + pub fn vdo_map_set_int64(self_: *mut VdoMap, name: *const gchar, value: gint64); +} +extern "C" { + pub fn vdo_map_set_uint64(self_: *mut VdoMap, name: *const gchar, value: guint64); +} +extern "C" { + pub fn vdo_map_set_double(self_: *mut VdoMap, name: *const gchar, value: gdouble); +} +extern "C" { + pub fn vdo_map_set_string(self_: *mut VdoMap, name: *const gchar, value: *const gchar); +} +extern "C" { + pub fn vdo_map_set_uint32x2(self_: *mut VdoMap, name: *const gchar, value: *const guint32); +} +extern "C" { + pub fn vdo_map_set_uint32x4(self_: *mut VdoMap, name: *const gchar, value: *const guint32); +} +extern "C" { + pub fn vdo_map_set_doublex4(self_: *mut VdoMap, name: *const gchar, value: *const gdouble); +} +extern "C" { + pub fn vdo_map_set_pair32i(self_: *mut VdoMap, name: *const gchar, value: VdoPair32i); +} +extern "C" { + pub fn vdo_map_set_pair32u(self_: *mut VdoMap, name: *const gchar, value: VdoPair32u); +} +extern "C" { + pub fn vdo_frame_get_type() -> GType; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _VdoFrame { + _unused: [u8; 0], +} +pub type VdoFrame = _VdoFrame; +#[repr(C)] +pub struct VdoFrameClass { + pub parent_class: GObjectClass, +} +pub type VdoFrame_autoptr = *mut VdoFrame; +pub type VdoFrame_listautoptr = *mut GList; +pub type VdoFrame_slistautoptr = *mut GSList; +pub type VdoFrame_queueautoptr = *mut GQueue; +pub type VdoFrameClass_autoptr = *mut VdoFrameClass; +pub type VdoFrameClass_listautoptr = *mut GList; +pub type VdoFrameClass_slistautoptr = *mut GSList; +pub type VdoFrameClass_queueautoptr = *mut GQueue; +pub type VdoFrameFinalizer = ::std::option::Option; +extern "C" { + pub fn vdo_frame_get_frame_type(self_: *mut VdoFrame) -> VdoFrameType; +} +extern "C" { + pub fn vdo_frame_get_sequence_nbr(self_: *mut VdoFrame) -> guint; +} +extern "C" { + pub fn vdo_frame_get_timestamp(self_: *mut VdoFrame) -> guint64; +} +extern "C" { + pub fn vdo_frame_get_custom_timestamp(self_: *mut VdoFrame) -> gint64; +} +extern "C" { + pub fn vdo_frame_get_size(self_: *mut VdoFrame) -> gsize; +} +extern "C" { + pub fn vdo_frame_get_header_size(self_: *mut VdoFrame) -> gssize; +} +extern "C" { + pub fn vdo_frame_get_fd(self_: *mut VdoFrame) -> gint; +} +extern "C" { + pub fn vdo_frame_get_extra_info(self_: *mut VdoFrame) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_frame_get_opaque(self_: *mut VdoFrame) -> gpointer; +} +extern "C" { + pub fn vdo_frame_get_is_last_buffer(self_: *mut VdoFrame) -> gboolean; +} +extern "C" { + pub fn vdo_frame_set_size(self_: *mut VdoFrame, size: gsize); +} +extern "C" { + pub fn vdo_frame_set_frame_type(self_: *mut VdoFrame, type_: VdoFrameType); +} +extern "C" { + pub fn vdo_frame_set_sequence_nbr(self_: *mut VdoFrame, seqnum: guint); +} +extern "C" { + pub fn vdo_frame_set_timestamp(self_: *mut VdoFrame, timestamp: guint64); +} +extern "C" { + pub fn vdo_frame_set_custom_timestamp(self_: *mut VdoFrame, timestamp: gint64); +} +extern "C" { + pub fn vdo_frame_set_is_last_buffer(self_: *mut VdoFrame, is_last_buffer: gboolean); +} +extern "C" { + pub fn vdo_frame_set_extra_info(self_: *mut VdoFrame, extra_info: *mut VdoMap); +} +extern "C" { + pub fn vdo_frame_set_header_size(self_: *mut VdoFrame, size: gssize); +} +extern "C" { + pub fn vdo_frame_memmap(self_: *mut VdoFrame) -> gpointer; +} +extern "C" { + pub fn vdo_frame_unmap(self_: *mut VdoFrame); +} +extern "C" { + pub fn vdo_frame_take_chunk(self_: *mut VdoFrame, error: *mut *mut GError) -> VdoChunk; +} +extern "C" { + pub fn vdo_frame_take_chunk_ex( + self_: *mut VdoFrame, + options: VdoChunkOption, + error: *mut *mut GError, + ) -> VdoChunk; +} +extern "C" { + pub fn vdo_channel_get_type() -> GType; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _VdoChannel { + _unused: [u8; 0], +} +pub type VdoChannel = _VdoChannel; +#[repr(C)] +pub struct VdoChannelClass { + pub parent_class: GObjectClass, +} +pub type VdoChannel_autoptr = *mut VdoChannel; +pub type VdoChannel_listautoptr = *mut GList; +pub type VdoChannel_slistautoptr = *mut GSList; +pub type VdoChannel_queueautoptr = *mut GQueue; +pub type VdoChannelClass_autoptr = *mut VdoChannelClass; +pub type VdoChannelClass_listautoptr = *mut GList; +pub type VdoChannelClass_slistautoptr = *mut GSList; +pub type VdoChannelClass_queueautoptr = *mut GQueue; +extern "C" { + pub fn vdo_channel_get(channel_nbr: guint, error: *mut *mut GError) -> *mut VdoChannel; +} +extern "C" { + pub fn vdo_channel_get_all(error: *mut *mut GError) -> *mut GList; +} +extern "C" { + pub fn vdo_channel_get_filtered(filter: *mut VdoMap, error: *mut *mut GError) -> *mut GList; +} +extern "C" { + pub fn vdo_channel_get_info(self_: *mut VdoChannel, error: *mut *mut GError) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_channel_get_settings(self_: *mut VdoChannel, error: *mut *mut GError) + -> *mut VdoMap; +} +extern "C" { + pub fn vdo_channel_set_settings( + self_: *mut VdoChannel, + settings: *mut VdoMap, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_channel_get_stream_profile( + self_: *mut VdoChannel, + profile: *const gchar, + format: VdoFormat, + error: *mut *mut GError, + ) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_channel_get_resolutions( + self_: *mut VdoChannel, + filter: *mut VdoMap, + error: *mut *mut GError, + ) -> *mut VdoResolutionSet; +} +extern "C" { + pub fn vdo_channel_get_id(self_: *mut VdoChannel) -> guint; +} +extern "C" { + pub fn vdo_channel_set_stream_profile( + self_: *mut VdoChannel, + profile: *const gchar, + format: VdoFormat, + settings: *mut VdoMap, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_channel_set_crop_dptz( + self_: *mut VdoChannel, + x: guint, + y: guint, + width: guint, + height: guint, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_channel_apply_alpha_blending( + self_: *mut VdoChannel, + fd: gint, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_channel_set_framerate( + self_: *mut VdoChannel, + framerate: gdouble, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_channel_new( + id: guint, + resolution_set: *mut VdoResolutionSet, + info: *mut VdoMap, + error: *mut *mut GError, + ) -> *mut VdoChannel; +} +extern "C" { + pub fn vdo_channel_destroy(self_: *mut VdoChannel, error: *mut *mut GError) -> gboolean; +} +pub type VdoBuffer = VdoFrame; +pub type VdoBuffer_autoptr = *mut VdoBuffer; +pub type VdoBuffer_listautoptr = *mut GList; +pub type VdoBuffer_slistautoptr = *mut GSList; +pub type VdoBuffer_queueautoptr = *mut GQueue; +pub type VdoBufferFinalizer = ::std::option::Option; +extern "C" { + pub fn vdo_buffer_new( + fd: gint, + capacity: gsize, + offset: guint64, + opaque: gpointer, + ) -> *mut VdoBuffer; +} +extern "C" { + pub fn vdo_buffer_new_full( + fd: gint, + capacity: gsize, + offset: guint64, + opaque: gpointer, + settings: *mut VdoMap, + ) -> *mut VdoBuffer; +} +extern "C" { + pub fn vdo_buffer_get_id(self_: *mut VdoBuffer) -> guint32; +} +extern "C" { + pub fn vdo_buffer_get_fd(self_: *mut VdoBuffer) -> gint; +} +extern "C" { + pub fn vdo_buffer_get_offset(self_: *mut VdoBuffer) -> gint64; +} +extern "C" { + pub fn vdo_buffer_get_capacity(self_: *mut VdoBuffer) -> gsize; +} +extern "C" { + pub fn vdo_buffer_is_complete(self_: *mut VdoBuffer) -> gboolean; +} +extern "C" { + pub fn vdo_buffer_get_opaque(self_: *mut VdoBuffer) -> gpointer; +} +extern "C" { + pub fn vdo_buffer_get_data(self_: *mut VdoBuffer) -> gpointer; +} +extern "C" { + pub fn vdo_buffer_get_frame(self_: *mut VdoBuffer) -> *mut VdoFrame; +} +extern "C" { + pub fn vdo_stream_get_type() -> GType; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _VdoStream { + _unused: [u8; 0], +} +pub type VdoStream = _VdoStream; +#[repr(C)] +pub struct VdoStreamClass { + pub parent_class: GObjectClass, +} +pub type VdoStream_autoptr = *mut VdoStream; +pub type VdoStream_listautoptr = *mut GList; +pub type VdoStream_slistautoptr = *mut GSList; +pub type VdoStream_queueautoptr = *mut GQueue; +pub type VdoStreamClass_autoptr = *mut VdoStreamClass; +pub type VdoStreamClass_listautoptr = *mut GList; +pub type VdoStreamClass_slistautoptr = *mut GSList; +pub type VdoStreamClass_queueautoptr = *mut GQueue; +extern "C" { + pub fn vdo_stream_new( + settings: *mut VdoMap, + fin: VdoBufferFinalizer, + error: *mut *mut GError, + ) -> *mut VdoStream; +} +extern "C" { + pub fn vdo_stream_get(id: guint, error: *mut *mut GError) -> *mut VdoStream; +} +extern "C" { + pub fn vdo_stream_get_all(error: *mut *mut GError) -> *mut GList; +} +extern "C" { + pub fn vdo_stream_get_id(self_: *mut VdoStream) -> guint; +} +extern "C" { + pub fn vdo_stream_get_fd(self_: *mut VdoStream, error: *mut *mut GError) -> gint; +} +extern "C" { + pub fn vdo_stream_get_event_fd(self_: *mut VdoStream, error: *mut *mut GError) -> gint; +} +extern "C" { + pub fn vdo_stream_get_info(self_: *mut VdoStream, error: *mut *mut GError) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_stream_get_settings(self_: *mut VdoStream, error: *mut *mut GError) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_stream_set_settings( + self_: *mut VdoStream, + settings: *mut VdoMap, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_stream_set_framerate( + self_: *mut VdoStream, + framerate: gdouble, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_stream_attach( + self_: *mut VdoStream, + intent: *mut VdoMap, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_stream_start(self_: *mut VdoStream, error: *mut *mut GError) -> gboolean; +} +extern "C" { + pub fn vdo_stream_play( + self_: *mut VdoStream, + settings: *mut VdoMap, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_stream_stop(self_: *mut VdoStream); +} +extern "C" { + pub fn vdo_stream_force_key_frame(self_: *mut VdoStream, error: *mut *mut GError) -> gboolean; +} +extern "C" { + pub fn vdo_stream_buffer_alloc( + self_: *mut VdoStream, + opaque: gpointer, + error: *mut *mut GError, + ) -> *mut VdoBuffer; +} +extern "C" { + pub fn vdo_stream_buffer_unref( + self_: *mut VdoStream, + buffer: *mut *mut VdoBuffer, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_stream_buffer_enqueue( + self_: *mut VdoStream, + buffer: *mut VdoBuffer, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_stream_get_buffer(self_: *mut VdoStream, error: *mut *mut GError) -> *mut VdoBuffer; +} +extern "C" { + pub fn vdo_stream_to_fd( + settings: *mut VdoMap, + data_fd: ::std::os::raw::c_int, + meta_fd: ::std::os::raw::c_int, + error: *mut *mut GError, + ) -> *mut VdoStream; +} +extern "C" { + pub fn vdo_stream_snapshot(settings: *mut VdoMap, error: *mut *mut GError) -> *mut VdoBuffer; +} +extern "C" { + pub fn vdo_stream_encode( + self_: *mut VdoStream, + in_buf: *mut *mut VdoBuffer, + settings: *mut VdoMap, + error: *mut *mut GError, + ) -> gboolean; +} +extern "C" { + pub fn vdo_stream_get_event(self_: *mut VdoStream, error: *mut *mut GError) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_stream_enqueue_buffer( + self_: *mut VdoStream, + fd: gint, + offset: guint64, + max_size: gsize, + opaque: gpointer, + error: *mut *mut GError, + ) -> gboolean; +} diff --git a/crates/vdo-sys/src/lib.rs b/crates/vdo-sys/src/lib.rs new file mode 100644 index 00000000..e4a83645 --- /dev/null +++ b/crates/vdo-sys/src/lib.rs @@ -0,0 +1,31 @@ +//! Fairly nasty binding for VdoResolutionSet due to "Flexible Array Member" +//! https://rust-lang.github.io/rust-bindgen/using-fam.html +//! https://github.com/rust-lang/rust-bindgen/issues/1680#issuecomment-554296347 +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +use glib_sys::*; +use gobject_sys::*; + +type gchar = core::ffi::c_char; +type guchar = core::ffi::c_uchar; +type gdouble = core::ffi::c_double; + +type gint = core::ffi::c_int; +type guint = core::ffi::c_uint; +type gint16 = i16; +type guint16 = u16; +type gint32 = i32; +type guint32 = u32; +type gint64 = i64; +type guint64 = u64; + +type gsize = usize; +type gssize = isize; + +#[cfg(not(target_arch = "x86_64"))] +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +#[cfg(target_arch = "x86_64")] +include!("bindings.rs"); diff --git a/crates/vdo-sys/wrapper.h b/crates/vdo-sys/wrapper.h new file mode 100644 index 00000000..202eec13 --- /dev/null +++ b/crates/vdo-sys/wrapper.h @@ -0,0 +1,4 @@ +#include +#include +#include +#include From 2e33daab6ea12757f4387f7df99fee6d2821f267 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 15 Nov 2024 23:52:09 -0500 Subject: [PATCH 30/54] Add vdo. --- Cargo.lock | 96 ++++++++++++++++++++++++++------ Cargo.toml | 1 + crates/vdo/Cargo.toml | 16 ++++++ crates/vdo/src/lib.rs | 124 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 221 insertions(+), 16 deletions(-) create mode 100644 crates/vdo/Cargo.toml create mode 100644 crates/vdo/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 350e5f60..071dc760 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,7 +180,7 @@ version = "0.0.0" dependencies = [ "axevent-sys", "glib", - "glib-sys", + "glib-sys 0.19.8", "log", "thiserror", ] @@ -190,7 +190,7 @@ name = "axevent-sys" version = "0.0.0" dependencies = [ "bindgen", - "glib-sys", + "glib-sys 0.19.8", "pkg-config", ] @@ -200,7 +200,7 @@ version = "0.0.0" dependencies = [ "axstorage-sys", "glib", - "glib-sys", + "glib-sys 0.19.8", ] [[package]] @@ -208,7 +208,7 @@ name = "axstorage-sys" version = "0.0.0" dependencies = [ "bindgen", - "glib-sys", + "glib-sys 0.19.8", "pkg-config", ] @@ -453,6 +453,16 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cfg-expr" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c360837f8f19e2e4468275138f1c0dec1647d1e17bb7c0215fe3cd7530e93c25" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -953,10 +963,10 @@ version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cd743ba4714d671ad6b6234e8ab2a13b42304d0e13ab7eba1dcdd78a7d6d4ef" dependencies = [ - "glib-sys", - "gobject-sys", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", "libc", - "system-deps", + "system-deps 6.2.2", "windows-sys 0.52.0", ] @@ -974,8 +984,8 @@ dependencies = [ "futures-util", "gio-sys", "glib-macros", - "glib-sys", - "gobject-sys", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", "libc", "memchr", "smallvec", @@ -1002,7 +1012,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c2dc18d3a82b0006d470b13304fbbb3e0a9bd4884cf985a60a7ed733ac2c4a5" dependencies = [ "libc", - "system-deps", + "system-deps 6.2.2", +] + +[[package]] +name = "glib-sys" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0b1827e8621fc42c0dfb228e5d57ff6a71f9699e666ece8113f979ad87c2de" +dependencies = [ + "libc", + "system-deps 7.0.3", ] [[package]] @@ -1017,9 +1037,20 @@ version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e697e252d6e0416fd1d9e169bda51c0f1c926026c39ca21fbe8b1bb5c3b8b9e" dependencies = [ - "glib-sys", + "glib-sys 0.19.8", "libc", - "system-deps", + "system-deps 6.2.2", +] + +[[package]] +name = "gobject-sys" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c674d2ff8478cf0ec29d2be730ed779fef54415a2fb4b565c52def62696462" +dependencies = [ + "glib-sys 0.20.6", + "libc", + "system-deps 7.0.3", ] [[package]] @@ -1341,7 +1372,7 @@ dependencies = [ name = "licensekey" version = "0.0.0" dependencies = [ - "glib-sys", + "glib-sys 0.19.8", "libc", "licensekey-sys", "thiserror", @@ -2245,7 +2276,20 @@ version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ - "cfg-expr", + "cfg-expr 0.15.8", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "system-deps" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" +dependencies = [ + "cfg-expr 0.17.1", "heck", "pkg-config", "toml", @@ -2265,9 +2309,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" @@ -2733,6 +2777,26 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vdo" +version = "0.1.0" +dependencies = [ + "anyhow", + "gobject-sys 0.20.4", + "log", + "vdo-sys", +] + +[[package]] +name = "vdo-sys" +version = "0.0.0" +dependencies = [ + "bindgen", + "glib-sys 0.19.8", + "gobject-sys 0.20.4", + "pkg-config", +] + [[package]] name = "version-compare" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index f94f7bc9..6a00c4ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ licensekey = { path = "crates/licensekey" } licensekey-sys = { path = "crates/licensekey-sys" } mdb = { path = "crates/mdb" } mdb-sys = { path = "crates/mdb-sys" } +vdo = { path = "crates/vdo" } vdo-sys = { path = "crates/vdo-sys" } [workspace.package] diff --git a/crates/vdo/Cargo.toml b/crates/vdo/Cargo.toml new file mode 100644 index 00000000..a2f6276c --- /dev/null +++ b/crates/vdo/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "vdo" +version = "0.1.0" +edition.workspace = true + +[dependencies] +vdo-sys = { workspace = true } +log.workspace = true +anyhow.workspace = true +gobject-sys = "0.20.4" + +[features] +device-tests = [] + +[[example]] +name = "basic" \ No newline at end of file diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs new file mode 100644 index 00000000..fef6283e --- /dev/null +++ b/crates/vdo/src/lib.rs @@ -0,0 +1,124 @@ +use gobject_sys::{g_object_unref, GObject}; +use std::ffi::CString; +use vdo_sys::*; + +type Result = std::result::Result; +pub enum Error { + NullPointer, + CStringAllocation, +} + +pub struct Map { + raw: *mut VdoMap, +} + +impl Map { + /// Create a new larodMap object + pub fn new() -> Result { + let map = unsafe { vdo_map_new() }; + if !map.is_null() { + Ok(Self { raw: map }) + } else { + Err(Error::NullPointer) + } + } + + pub fn set_u32(&self, key: &str, value: u32) -> Result<()> { + let Ok(key_cstr) = CString::new(key) else { + return Err(Error::CStringAllocation); + }; + unsafe { + vdo_map_set_uint32(self.raw, key_cstr.as_ptr(), value); + } + Ok(()) + } +} + +impl Drop for Map { + // using g_object_unref is sourced from the vdo-larod examples + // https://github.com/AxisCommunications/acap-native-sdk-examples/blob/36800ed4c28dd96a2b659db3cb2c8a937c61d6d0/vdo-larod/app/imgprovider.c#L355 + fn drop(&mut self) { + unsafe { g_object_unref(self.raw as *mut GObject) } + } +} + +pub struct StreamBuilder { + format: VdoFormat, + buffer_access: u32, + buffer_count: u32, + buffer_strategy: u32, + input: u32, + channel: u32, + width: u32, + height: u32, + framerate: u32, + compression: u32, + rotation: u32, + horizontal_flip: bool, + monochrome: bool, + dynamic_gop: bool, + dynamic_bitrate: bool, + dynamic_framerate: bool, + dynamic_compression: bool, + qp_i: u32, + qp_p: u32, + bitrate: u32, + rc_mode: VdoRateControlMode, + rc_prio: VdoRateControlPriority, + gop_length: u32, + overlays: Option, +} + +impl Default for StreamBuilder { + fn default() -> Self { + StreamBuilder { + format: VdoFormat::VDO_FORMAT_H264, + buffer_access: 0, + buffer_count: 0, + buffer_strategy: 0, + input: 0, + channel: 0, + width: 0, + height: 0, + framerate: 0, + compression: 0, + rotation: 0, + horizontal_flip: false, + monochrome: false, + dynamic_gop: false, + dynamic_bitrate: false, + dynamic_framerate: false, + dynamic_compression: false, + qp_i: 0, + qp_p: 0, + bitrate: 0, + rc_mode: VdoRateControlMode::VDO_RATE_CONTROL_MODE_NONE, + rc_prio: VdoRateControlPriority::VDO_RATE_CONTROL_PRIORITY_NONE, + gop_length: 0, + overlays: None, + } + } +} + +impl StreamBuilder { + pub fn new() -> Self { + StreamBuilder::default() + } + + pub fn build(self) -> Result { + let map = Map::new()?; + map.set_u32("channel", self.channel)?; + map.set_u32("format", self.format as u32)?; + map.set_u32("width", self.width)?; + map.set_u32("height", self.height)?; + Ok(Stream {}) + } +} + +pub struct Stream {} + +impl Stream { + fn builder() -> StreamBuilder { + StreamBuilder::new() + } +} From 774322f64f4fa584ad787405aabccba4d6a66e4b Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 20 Nov 2024 09:50:22 -0500 Subject: [PATCH 31/54] Add configuration functions to StreamBuilder. --- Cargo.lock | 2 ++ crates/vdo/Cargo.toml | 2 ++ crates/vdo/src/lib.rs | 67 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 071dc760..0aa3f055 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2782,6 +2782,8 @@ name = "vdo" version = "0.1.0" dependencies = [ "anyhow", + "glib", + "glib-sys 0.19.8", "gobject-sys 0.20.4", "log", "vdo-sys", diff --git a/crates/vdo/Cargo.toml b/crates/vdo/Cargo.toml index a2f6276c..593f2984 100644 --- a/crates/vdo/Cargo.toml +++ b/crates/vdo/Cargo.toml @@ -7,6 +7,8 @@ edition.workspace = true vdo-sys = { workspace = true } log.workspace = true anyhow.workspace = true +glib.workspace = true +glib-sys.workspace = true gobject-sys = "0.20.4" [features] diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index fef6283e..92307065 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -1,9 +1,46 @@ +//! Stream buffer strategy seems to be round robin. Allocate N buffers with +//! `vod_stream_buffer_alloc`, enqueue the buffers with, +//! `vdo_stream_buffer_alloc`, and VDO will round robin the buffers and place +//! frame data in them. +//! + +use glib::translate::from_glib_full; +use glib_sys::{gboolean, gpointer, GError}; use gobject_sys::{g_object_unref, GObject}; use std::ffi::CString; +use std::ptr; use vdo_sys::*; +macro_rules! try_func { + ($func:ident $(,)?) => {{ + let mut error: *mut GError = ptr::null_mut(); + let success = $func(&mut error); + if error.is_null() { + (success, None) + } else { + (success, Some(Error::VDOError(VDOError{inner: error}))) + } + }}; + ($func:ident, $($arg:expr),+ $(,)?) => {{ + let mut error: *mut GError = ptr::null_mut(); + let success = $func($( $arg ),+, &mut error); + if error.is_null() { + (success, None) + } else { + (success, Some(Error::VDOError(VDOError{inner: error}))) + } + + }} +} + +#[derive(Debug)] +pub struct VDOError { + inner: *mut GError, +} + type Result = std::result::Result; pub enum Error { + VDOError(VDOError), NullPointer, CStringAllocation, } @@ -105,20 +142,44 @@ impl StreamBuilder { StreamBuilder::default() } + pub fn with_channel(mut self, chan: u32) -> Self { + self.channel = chan; + self + } + pub fn with_format(mut self, format: VdoFormat) -> Self { + self.format = format; + self + } + pub fn with_width(mut self, width: u32) -> Self { + self.width = width; + self + } + pub fn with_height(mut self, height: u32) -> Self { + self.height = height; + self + } + pub fn build(self) -> Result { let map = Map::new()?; map.set_u32("channel", self.channel)?; map.set_u32("format", self.format as u32)?; map.set_u32("width", self.width)?; map.set_u32("height", self.height)?; - Ok(Stream {}) + let (stream_raw, maybe_error) = unsafe { try_func!(vdo_stream_new, map.raw, None) }; + Ok(Stream { raw: stream_raw }) } } -pub struct Stream {} +pub struct Stream { + raw: *mut VdoStream, +} impl Stream { - fn builder() -> StreamBuilder { + pub fn builder() -> StreamBuilder { StreamBuilder::new() } + + pub fn new() -> Result { + StreamBuilder::new().build() + } } From a0c822c86ab0f422b825a194c0061335cd9e9ac0 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 20 Nov 2024 09:50:33 -0500 Subject: [PATCH 32/54] Add missing vdo-sys/Cargo.toml --- crates/vdo-sys/Cargo.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 crates/vdo-sys/Cargo.toml diff --git a/crates/vdo-sys/Cargo.toml b/crates/vdo-sys/Cargo.toml new file mode 100644 index 00000000..18511fd7 --- /dev/null +++ b/crates/vdo-sys/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "vdo-sys" +version = "0.0.0" +edition.workspace = true + +[build-dependencies] +bindgen = { workspace = true } +pkg-config = { workspace = true } + +[dependencies] +glib-sys = { workspace = true } +gobject-sys = "0.20.4" From 9b57b94ac3a68cfefa787880da5d0074cb9ff550 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 20 Nov 2024 09:50:49 -0500 Subject: [PATCH 33/54] Add vdo basic example. --- crates/vdo/examples/basic.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 crates/vdo/examples/basic.rs diff --git a/crates/vdo/examples/basic.rs b/crates/vdo/examples/basic.rs new file mode 100644 index 00000000..1b46bbd2 --- /dev/null +++ b/crates/vdo/examples/basic.rs @@ -0,0 +1,14 @@ +use anyhow::{Error, Result}; +use vdo::Stream; + +fn main() -> Result<()> { + let stream = Stream::builder() + .with_channel() + .with_format() + .with_width() + .with_height() + .build(); + stream.start(); + while let Ok(frame) = stream.next_frame() {} + Ok(()) +} From fcabb5f5c92f5532ebe08005c3906497f3eb2ed1 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 5 Dec 2024 15:53:35 -0500 Subject: [PATCH 34/54] Updated bindings and build.rs. --- crates/vdo-sys/build.rs | 3 ++ crates/vdo-sys/src/bindings.rs | 59 ++++++++++++++++++++++++++++------ crates/vdo-sys/wrapper.h | 1 + 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/crates/vdo-sys/build.rs b/crates/vdo-sys/build.rs index d950addc..6fbe6d15 100644 --- a/crates/vdo-sys/build.rs +++ b/crates/vdo-sys/build.rs @@ -7,9 +7,12 @@ fn populated_bindings(dst: &path::PathBuf) { .allowlist_recursively(false) .allowlist_function("^(vdo.*)$") .allowlist_type("^(_?Vdo.*)$") + .allowlist_var("^(VDO_ERROR.*)$") + .blocklist_function("vdo_stream_enqueue_buffer") .default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true, }) + // .constified_enum("^(VDO_ERROR.*)$") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .layout_tests(false); for path in library.include_paths { diff --git a/crates/vdo-sys/src/bindings.rs b/crates/vdo-sys/src/bindings.rs index e6ad9fbb..ca9641ef 100644 --- a/crates/vdo-sys/src/bindings.rs +++ b/crates/vdo-sys/src/bindings.rs @@ -1007,13 +1007,54 @@ extern "C" { extern "C" { pub fn vdo_stream_get_event(self_: *mut VdoStream, error: *mut *mut GError) -> *mut VdoMap; } -extern "C" { - pub fn vdo_stream_enqueue_buffer( - self_: *mut VdoStream, - fd: gint, - offset: guint64, - max_size: gsize, - opaque: gpointer, - error: *mut *mut GError, - ) -> gboolean; +pub const VDO_ERROR_NOT_FOUND: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NOT_FOUND; +pub const VDO_ERROR_EXISTS: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_EXISTS; +pub const VDO_ERROR_INVALID_ARGUMENT: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_INVALID_ARGUMENT; +pub const VDO_ERROR_PERMISSION_DENIED: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_PERMISSION_DENIED; +pub const VDO_ERROR_NOT_SUPPORTED: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NOT_SUPPORTED; +pub const VDO_ERROR_CLOSED: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_CLOSED; +pub const VDO_ERROR_BUSY: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_BUSY; +pub const VDO_ERROR_IO: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_IO; +pub const VDO_ERROR_HAL: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_HAL; +pub const VDO_ERROR_DBUS: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_DBUS; +pub const VDO_ERROR_OOM: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_OOM; +pub const VDO_ERROR_IDLE: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_IDLE; +pub const VDO_ERROR_NO_DATA: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NO_DATA; +pub const VDO_ERROR_NO_BUFFER_SPACE: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NO_BUFFER_SPACE; +pub const VDO_ERROR_BUFFER_FAILURE: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_BUFFER_FAILURE; +pub const VDO_ERROR_INTERFACE_DOWN: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_INTERFACE_DOWN; +pub const VDO_ERROR_FAILED: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_FAILED; +pub const VDO_ERROR_FATAL: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_FATAL; +pub const VDO_ERROR_NOT_CONTROLLED: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NOT_CONTROLLED; +pub const VDO_ERROR_NO_EVENT: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NO_EVENT; +#[repr(u32)] +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum _bindgen_ty_24 { + VDO_ERROR_NOT_FOUND = 1, + VDO_ERROR_EXISTS = 2, + VDO_ERROR_INVALID_ARGUMENT = 3, + VDO_ERROR_PERMISSION_DENIED = 4, + VDO_ERROR_NOT_SUPPORTED = 5, + VDO_ERROR_CLOSED = 6, + VDO_ERROR_BUSY = 7, + VDO_ERROR_IO = 8, + VDO_ERROR_HAL = 9, + VDO_ERROR_DBUS = 10, + VDO_ERROR_OOM = 11, + VDO_ERROR_IDLE = 12, + VDO_ERROR_NO_DATA = 13, + VDO_ERROR_NO_BUFFER_SPACE = 14, + VDO_ERROR_BUFFER_FAILURE = 15, + VDO_ERROR_INTERFACE_DOWN = 16, + VDO_ERROR_FAILED = 17, + VDO_ERROR_FATAL = 18, + VDO_ERROR_NOT_CONTROLLED = 19, + VDO_ERROR_NO_EVENT = 20, +} +extern "C" { + pub fn vdo_error_quark() -> GQuark; +} +extern "C" { + pub fn vdo_error_is_expected(error: *mut *mut GError) -> gboolean; } diff --git a/crates/vdo-sys/wrapper.h b/crates/vdo-sys/wrapper.h index 202eec13..6d5328f6 100644 --- a/crates/vdo-sys/wrapper.h +++ b/crates/vdo-sys/wrapper.h @@ -2,3 +2,4 @@ #include #include #include +#include From 147cc9261cae95c3f9cc7bd679734f4f355a99d7 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 12 Dec 2024 16:25:05 -0500 Subject: [PATCH 35/54] Add ability to iterate over frames and buffers. --- Cargo.lock | 2 + crates/vdo/Cargo.toml | 6 +- crates/vdo/examples/basic.rs | 29 +- crates/vdo/src/lib.rs | 518 +++++++++++++++++++++++++++++++++-- 4 files changed, 529 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0aa3f055..a997c491 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2782,10 +2782,12 @@ name = "vdo" version = "0.1.0" dependencies = [ "anyhow", + "env_logger", "glib", "glib-sys 0.19.8", "gobject-sys 0.20.4", "log", + "thiserror", "vdo-sys", ] diff --git a/crates/vdo/Cargo.toml b/crates/vdo/Cargo.toml index 593f2984..43a60934 100644 --- a/crates/vdo/Cargo.toml +++ b/crates/vdo/Cargo.toml @@ -10,9 +10,13 @@ anyhow.workspace = true glib.workspace = true glib-sys.workspace = true gobject-sys = "0.20.4" +thiserror.workspace = true [features] device-tests = [] [[example]] -name = "basic" \ No newline at end of file +name = "basic" + +[dev-dependencies] +env_logger.workspace = true diff --git a/crates/vdo/examples/basic.rs b/crates/vdo/examples/basic.rs index 1b46bbd2..15936e3b 100644 --- a/crates/vdo/examples/basic.rs +++ b/crates/vdo/examples/basic.rs @@ -1,14 +1,25 @@ use anyhow::{Error, Result}; -use vdo::Stream; +use vdo::{Stream, VdoBufferStrategy, VdoFormat}; fn main() -> Result<()> { - let stream = Stream::builder() - .with_channel() - .with_format() - .with_width() - .with_height() - .build(); - stream.start(); - while let Ok(frame) = stream.next_frame() {} + let mut stream = Stream::builder() + .channel(0) + .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) + .width(1920) + .height(1080) + .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) + .build() + .expect("Unable to create stream"); + stream + .allocate_buffers(5) + .expect("failed to allocate buffers"); + let mut running_stream = stream.start().expect("failed to start stream"); + for (idx, buffer) in running_stream.iter().enumerate() { + let frame = buffer.frame().expect("failed to get frame from buffer"); + println!("frame size: {}", frame.size()); + if idx > 5 { + break; + } + } Ok(()) } diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index 92307065..cf7f6a31 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -5,11 +5,18 @@ //! use glib::translate::from_glib_full; -use glib_sys::{gboolean, gpointer, GError}; +use glib_sys::{gboolean, gpointer, GError, GTRUE}; use gobject_sys::{g_object_unref, GObject}; -use std::ffi::CString; -use std::ptr; +use log::{debug, error, info}; +use std::ffi::{CStr, CString}; +use std::fmt::{Debug, Display}; +use std::marker::PhantomData; +use std::mem; +use std::sync::{mpsc, Arc, Mutex, PoisonError}; +use std::thread::JoinHandle; +use std::{ptr, thread}; use vdo_sys::*; +pub use vdo_sys::{VdoBufferStrategy, VdoFormat}; macro_rules! try_func { ($func:ident $(,)?) => {{ @@ -18,7 +25,7 @@ macro_rules! try_func { if error.is_null() { (success, None) } else { - (success, Some(Error::VDOError(VDOError{inner: error}))) + (success, Some(Error::VDOError(VDOError::from_gerror(error)))) } }}; ($func:ident, $($arg:expr),+ $(,)?) => {{ @@ -27,22 +34,141 @@ macro_rules! try_func { if error.is_null() { (success, None) } else { - (success, Some(Error::VDOError(VDOError{inner: error}))) + (success, Some(Error::VDOError(VDOError::from_gerror(error)))) } }} } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct VDOError { - inner: *mut GError, + code: i32, + message: String, } +impl Display for VDOError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "VDOError {{ code: {}, message: {} }}", + self.get_code_message(), + self.message + ) + } +} + +impl VDOError { + fn get_code_message(&self) -> &str { + let code_u = u32::try_from(self.code).unwrap_or(0); + if code_u == VDO_ERROR_NOT_FOUND as u32 { + "VDO_ERROR_NOT_FOUND" + } else if code_u == VDO_ERROR_EXISTS as u32 { + "VDO_ERROR_EXISTS" + } else if code_u == VDO_ERROR_INVALID_ARGUMENT as u32 { + "VDO_ERROR_INVALID_ARGUMENT" + } else if code_u == VDO_ERROR_PERMISSION_DENIED as u32 { + "VDO_ERROR_PERMISSION_DENIED" + } else if code_u == VDO_ERROR_NOT_SUPPORTED as u32 { + "VDO_ERROR_NOT_SUPPORTED" + } else if code_u == VDO_ERROR_CLOSED as u32 { + "VDO_ERROR_CLOSED" + } else if code_u == VDO_ERROR_BUSY as u32 { + "VDO_ERROR_BUSY" + } else if code_u == VDO_ERROR_IO as u32 { + "VDO_ERROR_IO" + } else if code_u == VDO_ERROR_HAL as u32 { + "VDO_ERROR_HAL" + } else if code_u == VDO_ERROR_DBUS as u32 { + "VDO_ERROR_DBUS" + } else if code_u == VDO_ERROR_OOM as u32 { + "VDO_ERROR_OOM" + } else if code_u == VDO_ERROR_IDLE as u32 { + "VDO_ERROR_IDLE" + } else if code_u == VDO_ERROR_NO_DATA as u32 { + "VDO_ERROR_NO_DATA" + } else if code_u == VDO_ERROR_NO_BUFFER_SPACE as u32 { + "VDO_ERROR_NO_BUFFER_SPACE" + } else if code_u == VDO_ERROR_BUFFER_FAILURE as u32 { + "VDO_ERROR_BUFFER_FAILURE" + } else if code_u == VDO_ERROR_INTERFACE_DOWN as u32 { + "VDO_ERROR_INTERFACE_DOWN" + } else if code_u == VDO_ERROR_FAILED as u32 { + "VDO_ERROR_FAILED" + } else if code_u == VDO_ERROR_FATAL as u32 { + "VDO_ERROR_FATAL" + } else if code_u == VDO_ERROR_NOT_CONTROLLED as u32 { + "VDO_ERROR_NOT_CONTROLLED" + } else if code_u == VDO_ERROR_NO_EVENT as u32 { + "VDO_ERROR_NO_EVENT" + } else { + "VDO_ERROR_FAILED" + } + } + + fn from_gerror(gerror: *mut GError) -> Self { + if !gerror.is_null() { + let g_error = unsafe { *gerror }; + if !g_error.message.is_null() { + let msg = unsafe { CStr::from_ptr(g_error.message) }; + VDOError { + code: g_error.code, + message: String::from(msg.to_str().unwrap_or("Invalid message")), + } + } else { + VDOError { + code: g_error.code, + message: String::from("Invalid message"), + } + } + } else { + VDOError::default() + } + } +} + +impl std::error::Error for VDOError {} + +// impl Display for VDOError { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// write!(f, "{:?}", self) +// } +// } + +// impl Debug for VDOError { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// if !self.inner.is_null() { +// let g_error = unsafe { *self.inner }; +// if !g_error.message.is_null() { +// let msg = unsafe { CStr::from_ptr(g_error.message) }; +// f.debug_struct(&format!("GError @ {self:p}")) +// .field("domain", &g_error.domain) +// .field("code", &g_error.code) +// .field("message", &msg.to_str().unwrap_or("Invalid message data")) +// .finish() +// } else { +// write!(f, "{:?}", g_error) +// } +// } else { +// write!(f, "Error returned with null pointer to GError") +// } +// } +// } + type Result = std::result::Result; +#[derive(thiserror::Error, Debug)] pub enum Error { - VDOError(VDOError), + #[error(transparent)] + VDOError(#[from] VDOError), + #[error("libvdo returned an unexpected null pointer")] NullPointer, + #[error("could not allocate memory for CString")] CStringAllocation, + #[error("missing error data from libvdo")] + MissingVDOError, + #[error("poisoned pointer to stream")] + PoisonedStream, + #[error("no buffers are allocated for the stream")] + NoBuffersAllocated, } pub struct Map { @@ -69,6 +195,12 @@ impl Map { } Ok(()) } + + pub fn dump(&self) { + unsafe { + vdo_map_dump(self.raw); + } + } } impl Drop for Map { @@ -83,7 +215,7 @@ pub struct StreamBuilder { format: VdoFormat, buffer_access: u32, buffer_count: u32, - buffer_strategy: u32, + buffer_strategy: VdoBufferStrategy, input: u32, channel: u32, width: u32, @@ -112,7 +244,7 @@ impl Default for StreamBuilder { format: VdoFormat::VDO_FORMAT_H264, buffer_access: 0, buffer_count: 0, - buffer_strategy: 0, + buffer_strategy: VdoBufferStrategy::VDO_BUFFER_STRATEGY_NONE, input: 0, channel: 0, width: 0, @@ -142,19 +274,24 @@ impl StreamBuilder { StreamBuilder::default() } - pub fn with_channel(mut self, chan: u32) -> Self { + pub fn buffer_strategy(mut self, strategy: VdoBufferStrategy) -> Self { + self.buffer_strategy = strategy; + self + } + + pub fn channel(mut self, chan: u32) -> Self { self.channel = chan; self } - pub fn with_format(mut self, format: VdoFormat) -> Self { + pub fn format(mut self, format: VdoFormat) -> Self { self.format = format; self } - pub fn with_width(mut self, width: u32) -> Self { + pub fn width(mut self, width: u32) -> Self { self.width = width; self } - pub fn with_height(mut self, height: u32) -> Self { + pub fn height(mut self, height: u32) -> Self { self.height = height; self } @@ -165,13 +302,125 @@ impl StreamBuilder { map.set_u32("format", self.format as u32)?; map.set_u32("width", self.width)?; map.set_u32("height", self.height)?; + map.set_u32("buffer.strategy", self.buffer_strategy as u32)?; let (stream_raw, maybe_error) = unsafe { try_func!(vdo_stream_new, map.raw, None) }; - Ok(Stream { raw: stream_raw }) + if !stream_raw.is_null() { + debug_assert!( + maybe_error.is_none(), + "vdo_stream_new returned an stream pointer AND returned an error!" + ); + Ok(Stream { + raw: StreamWrapper(stream_raw), + buffers: Vec::new(), + }) + } else { + Err(maybe_error.unwrap_or(Error::MissingVDOError)) + } } } +pub struct Frame<'a> { + raw: *mut VdoFrame, + phantom: PhantomData<&'a Buffer>, +} + +impl<'a> Frame<'a> { + pub fn size(&self) -> usize { + unsafe { vdo_frame_get_size(self.raw) } + } +} + +pub struct Buffer { + raw: *mut VdoBuffer, +} + +pub struct StreamBuffer<'a> { + buffer: Buffer, + stream: &'a Stream, +} + +impl<'a> StreamBuffer<'a> { + pub fn frame(&self) -> Result { + let frame = unsafe { vdo_buffer_get_frame(self.buffer.raw) }; + Ok(Frame { + raw: frame, + phantom: PhantomData, + }) + } +} + +impl<'a> Drop for StreamBuffer<'a> { + fn drop(&mut self) { + let (success, maybe_error) = unsafe { + try_func!( + vdo_stream_buffer_unref, + self.stream.raw.0, + &mut self.buffer.raw + ) + }; + } +} + +// unsafe impl Send for Buffer {} + +unsafe impl Send for StreamWrapper {} + +struct StreamWrapper(*mut VdoStream); pub struct Stream { - raw: *mut VdoStream, + raw: StreamWrapper, + buffers: Vec<*mut VdoBuffer>, +} + +pub struct StreamIterator<'a> { + stream: &'a Stream, +} + +impl<'a> Iterator for StreamIterator<'a> { + type Item = StreamBuffer<'a>; + fn next(&mut self) -> Option { + let (buffer_ptr, maybe_error) = + unsafe { try_func!(vdo_stream_get_buffer, self.stream.raw.0) }; + if !buffer_ptr.is_null() { + debug_assert!( + maybe_error.is_none(), + "vdo_stream_get_buffer returned an stream pointer AND returned an error!" + ); + debug!("fetched buffer from vdo stream"); + Some(StreamBuffer { + buffer: Buffer { raw: buffer_ptr }, + stream: self.stream, + }) + } else { + None + } + } +} + +pub struct RunningStream<'a> { + stream: &'a mut Stream, +} + +impl<'a> RunningStream<'a> { + pub fn iter(&mut self) -> StreamIterator { + StreamIterator { + stream: self.stream, + } + } + pub fn stop(&mut self) -> Result<()> { + unsafe { vdo_stream_stop(self.stream.raw.0) }; + Ok(()) + } +} + +impl<'a> IntoIterator for &'a RunningStream<'a> { + type Item = StreamBuffer<'a>; + type IntoIter = StreamIterator<'a>; + + fn into_iter(self) -> Self::IntoIter { + StreamIterator { + stream: self.stream, + } + } } impl Stream { @@ -182,4 +431,241 @@ impl Stream { pub fn new() -> Result { StreamBuilder::new().build() } + + pub fn info(&self) -> Result { + let (map_raw, maybe_error) = unsafe { try_func!(vdo_stream_get_info, self.raw.0) }; + if !map_raw.is_null() { + debug_assert!( + maybe_error.is_none(), + "vdo_stream_get_info returned a pointer AND returned an error!" + ); + Ok(Map { raw: map_raw }) + } else { + Err(maybe_error.unwrap_or(Error::MissingVDOError)) + } + } + + pub fn settings(&self) -> Result { + let (map_raw, maybe_error) = unsafe { try_func!(vdo_stream_get_settings, self.raw.0) }; + if !map_raw.is_null() { + debug_assert!( + maybe_error.is_none(), + "vdo_stream_get_settings returned a pointer AND returned an error!" + ); + Ok(Map { raw: map_raw }) + } else { + Err(maybe_error.unwrap_or(Error::MissingVDOError)) + } + } + + pub fn allocate_buffer(&mut self) -> Result<()> { + let (buffer_ptr, maybe_error) = + unsafe { try_func!(vdo_stream_buffer_alloc, self.raw.0, ptr::null_mut()) }; + if !buffer_ptr.is_null() { + debug_assert!( + maybe_error.is_none(), + "vdo_stream_buffer_alloc returned a buffer pointer AND returned an error!" + ); + self.buffers.push(buffer_ptr); + Ok(()) + } else { + Err(maybe_error.unwrap_or(Error::MissingVDOError)) + } + } + + pub fn allocate_buffers(&mut self, num: usize) -> Result<()> { + for _ in 0..num { + let (buffer_ptr, maybe_error) = + unsafe { try_func!(vdo_stream_buffer_alloc, self.raw.0, ptr::null_mut()) }; + if !buffer_ptr.is_null() { + debug_assert!( + maybe_error.is_none(), + "vdo_stream_buffer_alloc returned a buffer pointer AND returned an error!" + ); + self.buffers.push(buffer_ptr); + } else { + return Err(maybe_error.unwrap_or(Error::MissingVDOError)); + } + } + Ok(()) + } + + // Not sure this function is needed + // pub fn buffers(&self) -> &[Buffer] { + // self.buffers.as_slice() + // } + + /// Start a background thread that fetches frames and passes them to the foreground thread + /// via an mpsc::channel. + pub fn start(&mut self) -> Result { + if self.buffers.is_empty() { + return Err(Error::NoBuffersAllocated); + } + for buffer in self.buffers.iter() { + let (success_enqueue, maybe_error) = + unsafe { try_func!(vdo_stream_buffer_enqueue, self.raw.0, *buffer) }; + if success_enqueue == GTRUE { + debug!("enqueued buffer to stream"); + debug_assert!( + maybe_error.is_none(), + "vdo_stream_buffer_enqueue indicated success AND returned an error!" + ); + } else { + return Err(maybe_error.unwrap_or(Error::MissingVDOError)); + } + } + let (success_start, maybe_error) = unsafe { try_func!(vdo_stream_start, self.raw.0) }; + if success_start == GTRUE { + debug_assert!( + maybe_error.is_none(), + "vdo_stream_new returned an stream pointer AND returned an error!" + ); + Ok(RunningStream { stream: self }) + } else { + Err(maybe_error.unwrap_or(Error::MissingVDOError)) + } + } + + // Do we want to spawn a fetcher thread? + // pub fn start_with_channel(&mut self) -> Result<()> { + // let Ok(raw_stream_ptr) = self.raw.lock() else { + // return Err(Error::PoisonedStream); + // }; + // for buffer in self.buffers.iter() { + // let (success_enqueue, maybe_error) = + // unsafe { try_func!(vdo_stream_buffer_enqueue, raw_stream_ptr.0, buffer.raw) }; + // if success_enqueue == GTRUE { + // debug!("enqueued buffer to stream"); + // debug_assert!( + // maybe_error.is_none(), + // "vdo_stream_buffer_enqueue indicated success AND returned an error!" + // ); + // } else { + // return Err(maybe_error.unwrap_or(Error::MissingVDOError)); + // } + // } + // let (success_start, maybe_error) = unsafe { try_func!(vdo_stream_start, raw_stream_ptr.0) }; + // drop(raw_stream_ptr); + // if success_start == GTRUE { + // debug_assert!( + // maybe_error.is_none(), + // "vdo_stream_new returned an stream pointer AND returned an error!" + // ); + // } else { + // return Err(maybe_error.unwrap_or(Error::MissingVDOError)); + // } + // let (sender, receiver) = mpsc::channel(); + // let stream_c = self.raw.clone(); + // self.rx_channel = Some(receiver); + // self.fetcher_thread = Some(thread::spawn(move || { + // debug!("starting frame fetcher thread"); + // let Ok(raw_stream_ptr) = stream_c.lock() else { + // return Err(StreamError::PoisonedStream); + // }; + // let (buffer_ptr, maybe_error) = + // unsafe { try_func!(vdo_stream_get_buffer, raw_stream_ptr.0) }; + // if !buffer_ptr.is_null() { + // debug_assert!( + // maybe_error.is_none(), + // "vdo_stream_get_buffer returned an stream pointer AND returned an error!" + // ); + // debug!("fetched buffer from vdo stream"); + // sender.send(Buffer { raw: buffer_ptr }); + // } else { + // error!("error while fetching buffer: {}", maybe_error.unwrap()); + // } + // Ok(()) + // })); + // Ok(()) + // } +} + +impl Drop for Stream { + fn drop(&mut self) { + unsafe { + vdo_stream_stop(self.raw.0); + } + for mut buffer in mem::take(&mut self.buffers).into_iter() { + let (success, maybe_error) = + unsafe { try_func!(vdo_stream_buffer_unref, self.raw.0, &mut buffer) }; + } + } +} + +#[cfg(all(test, target_arch = "aarch64", feature = "device-tests"))] +mod tests { + use super::*; + + #[test] + fn stream_error_with_no_buffers() { + env_logger::try_init(); + let mut stream = Stream::builder() + .channel(0) + .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) + .width(1920) + .height(1080) + .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) + .build() + .expect("Unable to create stream"); + let settings = stream + .settings() + .expect("error while getting stream settings"); + settings.dump(); + let info = stream.info().expect("error while getting stream info"); + info.dump(); + let r = stream.start(); + assert!(r.is_err()); + assert!(matches!(r, Err(Error::NoBuffersAllocated))); + } + + #[test] + fn it_starts_stream() { + env_logger::try_init(); + let mut stream = Stream::builder() + .channel(0) + .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) + .width(1920) + .height(1080) + .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) + .build() + .expect("Unable to create stream"); + let settings = stream + .settings() + .expect("error while getting stream settings"); + settings.dump(); + let info = stream.info().expect("error while getting stream info"); + info.dump(); + stream.allocate_buffers(5); + let mut r = stream.start().expect("starting stream returned error"); + + let s = r.stop(); + assert!(s.is_ok()); + } + + #[test] + fn stream_fetches_frames() { + env_logger::try_init(); + let mut stream = Stream::builder() + .channel(0) + .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) + .width(1920) + .height(1080) + .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) + .build() + .expect("Unable to create stream"); + stream.allocate_buffers(5); + let mut r = stream.start().expect("starting stream returned error"); + + { + let buff = r.iter().next().expect("failed to fetch frame"); + let size = buff + .frame() + .expect("error fetching frame for buffer") + .size(); + info!("frame size: {}", size); + assert!(size > 0); + } + + let s = r.stop(); + } } From e8e40fc47cf4d2f67a34ce392c0d86755a40feb2 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 12 Dec 2024 16:25:35 -0500 Subject: [PATCH 36/54] Pass additional arguments through remote-test.sh. --- remote-test.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100755 remote-test.sh diff --git a/remote-test.sh b/remote-test.sh new file mode 100755 index 00000000..1f5cf009 --- /dev/null +++ b/remote-test.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -e +if [ -n "${CARGO_TEST_CAMERA}" ]; then + f=`basename $1` + scp "$1" $CARGO_TEST_CAMERA:. + # echo $f + ssh $CARGO_TEST_CAMERA "chmod +x /root/$f" + shift + ssh $CARGO_TEST_CAMERA "/root/$f" "$@" +else + $1 +fi From bc53fc8aa3d1e1a7e925b9a452fab05564c6934d Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 12 Dec 2024 16:54:39 -0500 Subject: [PATCH 37/54] Don't derive debug for VDOError. --- crates/vdo/src/lib.rs | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index cf7f6a31..6009a71c 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -40,7 +40,7 @@ macro_rules! try_func { }} } -#[derive(Debug, Default)] +#[derive(Default)] pub struct VDOError { code: i32, message: String, @@ -50,7 +50,8 @@ impl Display for VDOError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "VDOError {{ code: {}, message: {} }}", + "VDOError {{ code: {}, phrase: {}, message: {} }}", + self.code, self.get_code_message(), self.message ) @@ -128,31 +129,11 @@ impl VDOError { impl std::error::Error for VDOError {} -// impl Display for VDOError { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// write!(f, "{:?}", self) -// } -// } - -// impl Debug for VDOError { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// if !self.inner.is_null() { -// let g_error = unsafe { *self.inner }; -// if !g_error.message.is_null() { -// let msg = unsafe { CStr::from_ptr(g_error.message) }; -// f.debug_struct(&format!("GError @ {self:p}")) -// .field("domain", &g_error.domain) -// .field("code", &g_error.code) -// .field("message", &msg.to_str().unwrap_or("Invalid message data")) -// .finish() -// } else { -// write!(f, "{:?}", g_error) -// } -// } else { -// write!(f, "Error returned with null pointer to GError") -// } -// } -// } +impl Debug for VDOError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self) + } +} type Result = std::result::Result; #[derive(thiserror::Error, Debug)] From 1b0d7b2a321d56a4d7842fc38f2d6d01faa17241 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 12 Dec 2024 16:55:29 -0500 Subject: [PATCH 38/54] Comment out a few lines from test that were for information only. --- crates/vdo/src/lib.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index 6009a71c..a24ccdae 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -588,12 +588,12 @@ mod tests { .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) .build() .expect("Unable to create stream"); - let settings = stream - .settings() - .expect("error while getting stream settings"); - settings.dump(); - let info = stream.info().expect("error while getting stream info"); - info.dump(); + // let settings = stream + // .settings() + // .expect("error while getting stream settings"); + // settings.dump(); + // let info = stream.info().expect("error while getting stream info"); + // info.dump(); let r = stream.start(); assert!(r.is_err()); assert!(matches!(r, Err(Error::NoBuffersAllocated))); @@ -610,12 +610,12 @@ mod tests { .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) .build() .expect("Unable to create stream"); - let settings = stream - .settings() - .expect("error while getting stream settings"); - settings.dump(); - let info = stream.info().expect("error while getting stream info"); - info.dump(); + // let settings = stream + // .settings() + // .expect("error while getting stream settings"); + // settings.dump(); + // let info = stream.info().expect("error while getting stream info"); + // info.dump(); stream.allocate_buffers(5); let mut r = stream.start().expect("starting stream returned error"); From 4078645b961fa21a248f0ba8d13ff45b02b73b5e Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 12 Dec 2024 16:56:04 -0500 Subject: [PATCH 39/54] Remove buffers() method from Stream. --- crates/vdo/src/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index a24ccdae..e8ab9ae6 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -471,13 +471,7 @@ impl Stream { Ok(()) } - // Not sure this function is needed - // pub fn buffers(&self) -> &[Buffer] { - // self.buffers.as_slice() - // } - - /// Start a background thread that fetches frames and passes them to the foreground thread - /// via an mpsc::channel. + /// Request the Larod service to start fetching frames and passing back buffers. pub fn start(&mut self) -> Result { if self.buffers.is_empty() { return Err(Error::NoBuffersAllocated); From f6b5dfd1a21c7c992c45d5c1e37d083dc756e3f6 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 12 Dec 2024 16:56:51 -0500 Subject: [PATCH 40/54] Mark remote-test.sh as executable. --- remote-test.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 remote-test.sh diff --git a/remote-test.sh b/remote-test.sh old mode 100644 new mode 100755 From fa2082e86da7bbf6db94a91d978dd73b30299bed Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 12 Dec 2024 17:03:21 -0500 Subject: [PATCH 41/54] Drop larod. --- Cargo.lock | 16 - crates/larod-sys/Cargo.toml | 11 - crates/larod-sys/build.rs | 26 - crates/larod-sys/src/bindings.rs | 568 --------------------- crates/larod-sys/src/lib.rs | 5 - crates/larod-sys/wrapper.h | 1 - crates/larod/Cargo.toml | 15 - crates/larod/examples/basic.rs | 29 -- crates/larod/src/lib.rs | 845 ------------------------------- 9 files changed, 1516 deletions(-) delete mode 100644 crates/larod-sys/Cargo.toml delete mode 100644 crates/larod-sys/build.rs delete mode 100644 crates/larod-sys/src/bindings.rs delete mode 100644 crates/larod-sys/src/lib.rs delete mode 100644 crates/larod-sys/wrapper.h delete mode 100644 crates/larod/Cargo.toml delete mode 100644 crates/larod/examples/basic.rs delete mode 100644 crates/larod/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index b9fe7d67..1e19079d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1327,22 +1327,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "larod" -version = "0.1.0" -dependencies = [ - "larod-sys", - "log", -] - -[[package]] -name = "larod-sys" -version = "0.1.0" -dependencies = [ - "bindgen", - "pkg-config", -] - [[package]] name = "lazy_static" version = "1.4.0" diff --git a/crates/larod-sys/Cargo.toml b/crates/larod-sys/Cargo.toml deleted file mode 100644 index 9e518dde..00000000 --- a/crates/larod-sys/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -build = "build.rs" -name = "larod-sys" -version = "0.1.0" -edition.workspace = true - -[build-dependencies] -bindgen = { workspace = true } -pkg-config = { workspace = true } - -[dependencies] diff --git a/crates/larod-sys/build.rs b/crates/larod-sys/build.rs deleted file mode 100644 index 771449c1..00000000 --- a/crates/larod-sys/build.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::{env, path}; - -fn populated_bindings(dst: &path::PathBuf) { - let library = pkg_config::Config::new().probe("liblarod").unwrap(); - let mut bindings = bindgen::Builder::default() - .header("wrapper.h") - .allowlist_recursively(false) - .allowlist_function("^(larod.*)$") - .allowlist_type("^(_?larod.*)$") - .default_enum_style(bindgen::EnumVariation::Rust { - non_exhaustive: true, - }) - .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) - .layout_tests(false); - for path in library.include_paths { - bindings = bindings.clang_args(&["-F", path.to_str().unwrap()]); - } - bindings.generate().unwrap().write_to_file(dst).unwrap(); -} - -fn main() { - let dst = path::PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); - if env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default() != "x86_64" { - populated_bindings(&dst); - } -} diff --git a/crates/larod-sys/src/bindings.rs b/crates/larod-sys/src/bindings.rs deleted file mode 100644 index 6e5497ab..00000000 --- a/crates/larod-sys/src/bindings.rs +++ /dev/null @@ -1,568 +0,0 @@ -/* automatically generated by rust-bindgen 0.69.4 */ - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct larodDevice { - _unused: [u8; 0], -} -#[repr(u32)] -#[non_exhaustive] -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum larodAccess { - LAROD_ACCESS_INVALID = 0, - LAROD_ACCESS_PRIVATE = 1, - LAROD_ACCESS_PUBLIC = 2, -} -#[repr(i32)] -#[non_exhaustive] -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum larodErrorCode { - LAROD_ERROR_NONE = 0, - LAROD_ERROR_JOB = -1, - LAROD_ERROR_LOAD_MODEL = -2, - LAROD_ERROR_FD = -3, - LAROD_ERROR_MODEL_NOT_FOUND = -4, - LAROD_ERROR_PERMISSION = -5, - LAROD_ERROR_CONNECTION = -6, - LAROD_ERROR_CREATE_SESSION = -7, - LAROD_ERROR_KILL_SESSION = -8, - LAROD_ERROR_INVALID_CHIP_ID = -9, - LAROD_ERROR_INVALID_ACCESS = -10, - LAROD_ERROR_DELETE_MODEL = -11, - LAROD_ERROR_TENSOR_MISMATCH = -12, - LAROD_ERROR_VERSION_MISMATCH = -13, - LAROD_ERROR_ALLOC = -14, - LAROD_ERROR_MAX_ERRNO = 1024, -} -#[repr(u32)] -#[non_exhaustive] -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum larodTensorDataType { - LAROD_TENSOR_DATA_TYPE_INVALID = 0, - LAROD_TENSOR_DATA_TYPE_UNSPECIFIED = 1, - LAROD_TENSOR_DATA_TYPE_BOOL = 2, - LAROD_TENSOR_DATA_TYPE_UINT8 = 3, - LAROD_TENSOR_DATA_TYPE_INT8 = 4, - LAROD_TENSOR_DATA_TYPE_UINT16 = 5, - LAROD_TENSOR_DATA_TYPE_INT16 = 6, - LAROD_TENSOR_DATA_TYPE_UINT32 = 7, - LAROD_TENSOR_DATA_TYPE_INT32 = 8, - LAROD_TENSOR_DATA_TYPE_UINT64 = 9, - LAROD_TENSOR_DATA_TYPE_INT64 = 10, - LAROD_TENSOR_DATA_TYPE_FLOAT16 = 11, - LAROD_TENSOR_DATA_TYPE_FLOAT32 = 12, - LAROD_TENSOR_DATA_TYPE_FLOAT64 = 13, - LAROD_TENSOR_DATA_TYPE_MAX = 14, -} -#[repr(u32)] -#[non_exhaustive] -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum larodTensorLayout { - LAROD_TENSOR_LAYOUT_INVALID = 0, - LAROD_TENSOR_LAYOUT_UNSPECIFIED = 1, - LAROD_TENSOR_LAYOUT_NHWC = 2, - LAROD_TENSOR_LAYOUT_NCHW = 3, - LAROD_TENSOR_LAYOUT_420SP = 4, - LAROD_TENSOR_LAYOUT_MAX = 5, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct larodError { - pub code: larodErrorCode, - pub msg: *const ::std::os::raw::c_char, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct larodTensorDims { - pub dims: [usize; 12usize], - pub len: usize, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct larodTensorPitches { - pub pitches: [usize; 12usize], - pub len: usize, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct larodModel { - _unused: [u8; 0], -} -pub type larodLoadModelCallback = ::std::option::Option< - unsafe extern "C" fn( - model: *mut larodModel, - userData: *mut ::std::os::raw::c_void, - error: *mut larodError, - ), ->; -pub type larodRunJobCallback = ::std::option::Option< - unsafe extern "C" fn(userData: *mut ::std::os::raw::c_void, error: *mut larodError), ->; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct larodConnection { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct larodJobRequest { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct larodTensor { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct larodMap { - _unused: [u8; 0], -} -extern "C" { - pub fn larodClearError(error: *mut *mut larodError); -} -extern "C" { - pub fn larodConnect(conn: *mut *mut larodConnection, error: *mut *mut larodError) -> bool; -} -extern "C" { - pub fn larodDisconnect(conn: *mut *mut larodConnection, error: *mut *mut larodError) -> bool; -} -extern "C" { - pub fn larodGetNumSessions( - conn: *mut larodConnection, - numSessions: *mut u64, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetDevice( - conn: *const larodConnection, - name: *const ::std::os::raw::c_char, - instance: u32, - error: *mut *mut larodError, - ) -> *const larodDevice; -} -extern "C" { - pub fn larodGetDeviceName( - dev: *const larodDevice, - error: *mut *mut larodError, - ) -> *const ::std::os::raw::c_char; -} -extern "C" { - pub fn larodGetDeviceInstance( - dev: *const larodDevice, - instance: *mut u32, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodListDevices( - conn: *mut larodConnection, - numDevices: *mut usize, - error: *mut *mut larodError, - ) -> *mut *const larodDevice; -} -extern "C" { - pub fn larodLoadModel( - conn: *mut larodConnection, - fd: ::std::os::raw::c_int, - dev: *const larodDevice, - access: larodAccess, - name: *const ::std::os::raw::c_char, - params: *const larodMap, - error: *mut *mut larodError, - ) -> *mut larodModel; -} -extern "C" { - pub fn larodLoadModelAsync( - conn: *mut larodConnection, - inFd: ::std::os::raw::c_int, - dev: *const larodDevice, - access: larodAccess, - name: *const ::std::os::raw::c_char, - params: *const larodMap, - callback: larodLoadModelCallback, - userData: *mut ::std::os::raw::c_void, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetModel( - conn: *mut larodConnection, - modelId: u64, - error: *mut *mut larodError, - ) -> *mut larodModel; -} -extern "C" { - pub fn larodGetModels( - conn: *mut larodConnection, - numModels: *mut usize, - error: *mut *mut larodError, - ) -> *mut *mut larodModel; -} -extern "C" { - pub fn larodDestroyModel(model: *mut *mut larodModel); -} -extern "C" { - pub fn larodDestroyModels(models: *mut *mut *mut larodModel, numModels: usize); -} -extern "C" { - pub fn larodDeleteModel( - conn: *mut larodConnection, - model: *mut larodModel, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetModelId(model: *const larodModel, error: *mut *mut larodError) -> u64; -} -extern "C" { - pub fn larodGetModelDevice( - model: *const larodModel, - error: *mut *mut larodError, - ) -> *const larodDevice; -} -extern "C" { - pub fn larodGetModelSize(model: *const larodModel, error: *mut *mut larodError) -> usize; -} -extern "C" { - pub fn larodGetModelName( - model: *const larodModel, - error: *mut *mut larodError, - ) -> *const ::std::os::raw::c_char; -} -extern "C" { - pub fn larodGetModelAccess( - model: *const larodModel, - error: *mut *mut larodError, - ) -> larodAccess; -} -extern "C" { - pub fn larodGetModelNumInputs(model: *const larodModel, error: *mut *mut larodError) -> usize; -} -extern "C" { - pub fn larodGetModelNumOutputs(model: *const larodModel, error: *mut *mut larodError) -> usize; -} -extern "C" { - pub fn larodGetModelInputByteSizes( - model: *const larodModel, - numInputs: *mut usize, - error: *mut *mut larodError, - ) -> *mut usize; -} -extern "C" { - pub fn larodGetModelOutputByteSizes( - model: *const larodModel, - numOutputs: *mut usize, - error: *mut *mut larodError, - ) -> *mut usize; -} -extern "C" { - pub fn larodCreateModelInputs( - model: *const larodModel, - numTensors: *mut usize, - error: *mut *mut larodError, - ) -> *mut *mut larodTensor; -} -extern "C" { - pub fn larodCreateModelOutputs( - model: *const larodModel, - numTensors: *mut usize, - error: *mut *mut larodError, - ) -> *mut *mut larodTensor; -} -extern "C" { - pub fn larodAllocModelInputs( - conn: *mut larodConnection, - model: *const larodModel, - fdPropFlags: u32, - numTensors: *mut usize, - params: *mut larodMap, - error: *mut *mut larodError, - ) -> *mut *mut larodTensor; -} -extern "C" { - pub fn larodAllocModelOutputs( - conn: *mut larodConnection, - model: *const larodModel, - fdPropFlags: u32, - numTensors: *mut usize, - params: *mut larodMap, - error: *mut *mut larodError, - ) -> *mut *mut larodTensor; -} -extern "C" { - pub fn larodCreateTensors( - numTensors: usize, - error: *mut *mut larodError, - ) -> *mut *mut larodTensor; -} -extern "C" { - pub fn larodDestroyTensors( - conn: *mut larodConnection, - tensors: *mut *mut *mut larodTensor, - numTensors: usize, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodSetTensorDims( - tensor: *mut larodTensor, - dims: *const larodTensorDims, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetTensorDims( - tensor: *const larodTensor, - error: *mut *mut larodError, - ) -> *const larodTensorDims; -} -extern "C" { - pub fn larodSetTensorPitches( - tensor: *mut larodTensor, - pitches: *const larodTensorPitches, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetTensorPitches( - tensor: *const larodTensor, - error: *mut *mut larodError, - ) -> *const larodTensorPitches; -} -extern "C" { - pub fn larodSetTensorDataType( - tensor: *mut larodTensor, - dataType: larodTensorDataType, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetTensorDataType( - tensor: *const larodTensor, - error: *mut *mut larodError, - ) -> larodTensorDataType; -} -extern "C" { - pub fn larodSetTensorLayout( - tensor: *mut larodTensor, - layout: larodTensorLayout, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetTensorLayout( - tensor: *const larodTensor, - error: *mut *mut larodError, - ) -> larodTensorLayout; -} -extern "C" { - pub fn larodSetTensorFd( - tensor: *mut larodTensor, - fd: ::std::os::raw::c_int, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetTensorFd( - tensor: *const larodTensor, - error: *mut *mut larodError, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn larodSetTensorFdSize( - tensor: *mut larodTensor, - size: usize, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetTensorFdSize( - tensor: *const larodTensor, - size: *mut usize, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodSetTensorFdOffset( - tensor: *mut larodTensor, - offset: i64, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetTensorFdOffset(tensor: *const larodTensor, error: *mut *mut larodError) -> i64; -} -extern "C" { - pub fn larodTrackTensor( - conn: *mut larodConnection, - tensor: *mut larodTensor, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodSetTensorFdProps( - tensor: *mut larodTensor, - fdPropFlags: u32, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetTensorFdProps( - tensor: *const larodTensor, - fdPropFlags: *mut u32, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodGetTensorName( - tensor: *const larodTensor, - error: *mut *mut larodError, - ) -> *const ::std::os::raw::c_char; -} -extern "C" { - pub fn larodGetTensorByteSize( - tensor: *const larodTensor, - byteSize: *mut usize, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodCreateMap(error: *mut *mut larodError) -> *mut larodMap; -} -extern "C" { - pub fn larodDestroyMap(map: *mut *mut larodMap); -} -extern "C" { - pub fn larodMapSetStr( - map: *mut larodMap, - key: *const ::std::os::raw::c_char, - value: *const ::std::os::raw::c_char, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodMapSetInt( - map: *mut larodMap, - key: *const ::std::os::raw::c_char, - value: i64, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodMapSetIntArr2( - map: *mut larodMap, - key: *const ::std::os::raw::c_char, - value0: i64, - value1: i64, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodMapSetIntArr4( - map: *mut larodMap, - key: *const ::std::os::raw::c_char, - value0: i64, - value1: i64, - value2: i64, - value3: i64, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodMapGetStr( - map: *mut larodMap, - key: *const ::std::os::raw::c_char, - error: *mut *mut larodError, - ) -> *const ::std::os::raw::c_char; -} -extern "C" { - pub fn larodMapGetInt( - map: *mut larodMap, - key: *const ::std::os::raw::c_char, - value: *mut i64, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodMapGetIntArr2( - map: *mut larodMap, - key: *const ::std::os::raw::c_char, - error: *mut *mut larodError, - ) -> *const i64; -} -extern "C" { - pub fn larodMapGetIntArr4( - map: *mut larodMap, - key: *const ::std::os::raw::c_char, - error: *mut *mut larodError, - ) -> *const i64; -} -extern "C" { - pub fn larodCreateJobRequest( - model: *const larodModel, - inputTensors: *mut *mut larodTensor, - numInputs: usize, - outputTensors: *mut *mut larodTensor, - numOutputs: usize, - params: *mut larodMap, - error: *mut *mut larodError, - ) -> *mut larodJobRequest; -} -extern "C" { - pub fn larodDestroyJobRequest(jobReq: *mut *mut larodJobRequest); -} -extern "C" { - pub fn larodSetJobRequestModel( - jobReq: *mut larodJobRequest, - model: *const larodModel, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodSetJobRequestInputs( - jobReq: *mut larodJobRequest, - tensors: *mut *mut larodTensor, - numTensors: usize, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodSetJobRequestOutputs( - jobReq: *mut larodJobRequest, - tensors: *mut *mut larodTensor, - numTensors: usize, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodSetJobRequestPriority( - jobReq: *mut larodJobRequest, - priority: u8, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodSetJobRequestParams( - jobReq: *mut larodJobRequest, - params: *const larodMap, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodRunJob( - conn: *mut larodConnection, - jobReq: *const larodJobRequest, - error: *mut *mut larodError, - ) -> bool; -} -extern "C" { - pub fn larodRunJobAsync( - conn: *mut larodConnection, - jobReq: *const larodJobRequest, - callback: larodRunJobCallback, - userData: *mut ::std::os::raw::c_void, - error: *mut *mut larodError, - ) -> bool; -} diff --git a/crates/larod-sys/src/lib.rs b/crates/larod-sys/src/lib.rs deleted file mode 100644 index 36fd376f..00000000 --- a/crates/larod-sys/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[cfg(not(target_arch = "x86_64"))] -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); - -#[cfg(target_arch = "x86_64")] -include!("bindings.rs"); diff --git a/crates/larod-sys/wrapper.h b/crates/larod-sys/wrapper.h deleted file mode 100644 index bda0612d..00000000 --- a/crates/larod-sys/wrapper.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/crates/larod/Cargo.toml b/crates/larod/Cargo.toml deleted file mode 100644 index d6005897..00000000 --- a/crates/larod/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "larod" -version = "0.1.0" -edition.workspace = true - -[dependencies] - -larod-sys = { workspace = true } -log.workspace = true - -[features] -device-tests = [] - -[[example]] -name = "basic" \ No newline at end of file diff --git a/crates/larod/examples/basic.rs b/crates/larod/examples/basic.rs deleted file mode 100644 index 96316088..00000000 --- a/crates/larod/examples/basic.rs +++ /dev/null @@ -1,29 +0,0 @@ -use larod::{Error, Session}; - -fn main() -> Result<(), Error> { - let session = Session::new(); - let devices = match session.devices() { - Ok(d) => d, - Err(Error::LarodError(e)) => { - if let Ok(msg) = e.msg() { - eprintln!("Error while listing available devices! {}", msg); - } else { - eprintln!("Error while listing available devices. Error returned ") - } - return Err(Error::LarodError(e)); - } - Err(e) => { - eprintln!("Unknown error while listing devices: {:?}", e); - return Err(e); - } - }; - println!("Devices:"); - for d in devices { - println!( - "{} ({})", - d.name().expect("Couldn't get device name"), - d.instance().expect("Couldn't get device instance") - ); - } - Ok(()) -} diff --git a/crates/larod/src/lib.rs b/crates/larod/src/lib.rs deleted file mode 100644 index fff2ee33..00000000 --- a/crates/larod/src/lib.rs +++ /dev/null @@ -1,845 +0,0 @@ -//! A safe warpper around the larod-sys bindings to the larod C library. -//! -//! # Gotchas -//! Many of the C functions return either a bool or a pointer to some object. -//! Additionally, one of the out arguments is a pointer to a larodError -//! object. If the normal return type is true, or not NULL in the case of a -//! pointer, the pointer to the larodError struct is expected to be NULL. This -//! represents two potentially conflicting indicators of whether the function -//! succeeded. -//! -//! Crucially, objects pointed to by returned pointers *AND* a non-NULL pointer -//! to a larodError struct need to be dealocated. That is handled appropriately -//! by constructing the LarodError struct if the larodError pointer is non-NULL -//! and the impl Drop for LarodError will dealocate the object appropriately. -//! -//! Example -//! ```rust -//! use larod::Session; -//! let session = Session::new(); -//! let devices = session.devices(); -//! ``` -//! -//! # TODOs: -//! - [ ] [larodDisconnect](https://axiscommunications.github.io/acap-documentation/docs/api/src/api/larod/html/larod_8h.html#ab8f97b4b4d15798384ca25f32ca77bba) -//! indicates it may fail to "kill a session." What are the implications if it fails to kill a session? Can we clear the sessions? - -use core::slice; -pub use larod_sys::larodAccess as LarodAccess; -use larod_sys::*; -use std::{ - collections::HashMap, - ffi::{c_char, CStr, CString}, - fs::File, - marker::PhantomData, - os::fd::AsRawFd, - path::Path, - ptr::{self}, -}; - -type Result = std::result::Result; - -macro_rules! try_func { - ($func:ident $(,)?) => {{ - let mut error: *mut larodError = ptr::null_mut(); - let success = $func(&mut error); - if error.is_null() { - (success, None) - } else { - (success, Some(Error::LarodError(LarodError{inner: error}))) - } - }}; - ($func:ident, $($arg:expr),+ $(,)?) => {{ - let mut error: *mut larodError = ptr::null_mut(); - let success = $func($( $arg ),+, &mut error); - if error.is_null() { - (success, None) - } else { - (success, Some(Error::LarodError(LarodError{inner: error}))) - } - - }} -} - -// Most larod functions require a `NULL`` pointer to a larodError AND may -// produce either a `NULL`` output pointer or `false` if an error occurs. This -// results in two potential indicators of whether the function succeeded. If we -// get a`true` output, we expect the larodError to be a pointer to `NULL` still. -// In the possibly rare event that a function succeeds but the larodError -// pointer is not `NULL`, we need to deallocate that memory by calling -// `larodClearError`. The `try_func` macro always checks to see if the -// larodError pointer is `NULL` and return a `LarodError` if not. Doing so will -// call `larodClearError` when it is ultimately dropped. -#[derive(Debug)] -pub struct LarodError { - inner: *mut larodError, -} - -impl LarodError { - pub fn msg(&self) -> Result { - if self.inner.is_null() { - Err(Error::NullLarodPointer) - } else { - let msg_slice = unsafe { CStr::from_ptr((*self.inner).msg).to_str() }; - match msg_slice { - Ok(m) => Ok(m.into()), - Err(e) => { - log::error!("larodError.msg contained invalid UTF-8: {:?}", e); - Err(Error::InvalidLarodMessage) - } - } - } - } - - pub fn code(&self) -> larodErrorCode { - unsafe { (*self.inner).code } - } -} - -impl Drop for LarodError { - fn drop(&mut self) { - if !self.inner.is_null() { - unsafe { larodClearError(&mut self.inner) } - } - } -} - -#[derive(Debug)] -pub enum Error { - LarodError(LarodError), - NullLarodPointer, - InvalidLarodMessage, - PointerToInvalidData, - CStringAllocation, - MissingLarodError, - IOError(std::io::Error), -} - -// impl LarodError { -// /// Convert from liblarod larodError to LarodError -// /// If larodError is not NULL, it must be dealocated by calling larodClearError -// fn from(e: *mut larodError) -> Self { -// if e.is_null() { -// Self::default() -// } else { -// let le = unsafe { *e }; -// let msg: String = unsafe { -// CStr::from_ptr(le.msg) -// .to_str() -// .unwrap_or("Error message invalid") -// .into() -// }; -// let code: LarodErrorCode = le.code.into(); -// // unsafe { -// // larodClearError(&mut e); -// // } -// Self { msg, code } -// } -// } -// } - -/// A type representing a larodMap. -pub struct LarodMap { - raw: *mut larodMap, -} - -impl LarodMap { - /// Create a new larodMap object - pub fn new() -> Result { - let (map, maybe_error): (*mut larodMap, Option) = - unsafe { try_func!(larodCreateMap) }; - if !map.is_null() { - debug_assert!( - maybe_error.is_none(), - "larodCreateMap allocated a map AND returned an error!" - ); - Ok(Self { raw: map }) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - - /// Add a string to a larodMap object. - /// Example - /// ```rust - /// use larod::LarodMap; - /// - /// let map = LarodMap::new().expect("Error creating map"); - /// map.set_string("key", "value").expect("Error setting string value for larodMap"); - /// ``` - pub fn set_string(&mut self, k: &str, v: &str) -> Result<()> { - let Ok(key_cstr) = CString::new(k.as_bytes()) else { - return Err(Error::CStringAllocation); - }; - let Ok(value_cstr) = CString::new(v.as_bytes()) else { - return Err(Error::CStringAllocation); - }; - let (success, maybe_error): (bool, Option) = unsafe { - try_func!( - larodMapSetStr, - self.raw, - key_cstr.as_ptr(), - value_cstr.as_ptr(), - ) - }; - if success { - debug_assert!( - maybe_error.is_none(), - "larodMapSetStr indicated success AND returned an error!" - ); - Ok(()) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - - /// Add an integer to a larodMap object. - /// Example - /// ```rust - /// use larod::LarodMap; - /// - /// let map = LarodMap::new().expect("Error creating map"); - /// map.set_int("key", 45).expect("Error setting integer value for larodMap"); - /// ``` - pub fn set_int(&mut self, k: &str, v: i64) -> Result<()> { - let Ok(key_cstr) = CString::new(k.as_bytes()) else { - return Err(Error::CStringAllocation); - }; - let (success, maybe_error): (bool, Option) = - unsafe { try_func!(larodMapSetInt, self.raw, key_cstr.as_ptr(), v) }; - if success { - debug_assert!( - maybe_error.is_none(), - "larodMapSetInt indicated success AND returned an error!" - ); - Ok(()) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - - /// Add an integer array of two items to a larodMap object. - /// Example - /// ```rust - /// use larod::LarodMap; - /// - /// let map = LarodMap::new().expect("Error creating map"); - /// map.set_int_arr2("key", (45, 64)).expect("Error setting integer array for larodMap"); - /// ``` - pub fn set_int_arr2(&mut self, k: &str, v: (i64, i64)) -> Result<()> { - let Ok(key_cstr) = CString::new(k.as_bytes()) else { - return Err(Error::CStringAllocation); - }; - let (success, maybe_error): (bool, Option) = - unsafe { try_func!(larodMapSetIntArr2, self.raw, key_cstr.as_ptr(), v.0, v.1) }; - - if success { - debug_assert!( - maybe_error.is_none(), - "larodMapSetIntArr2 indicated success AND returned an error!" - ); - Ok(()) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - - /// Add an integer array of 4 items to a larodMap object. - /// Example - /// ```rust - /// use larod::LarodMap; - /// - /// let map = LarodMap::new().expect("Error creating map"); - /// map.set_int_arr4("key", (45, 64, 36, 23)).expect("Error setting integer array for larodMap"); - /// ``` - pub fn set_int_arr4(&mut self, k: &str, v: (i64, i64, i64, i64)) -> Result<()> { - let Ok(key_cstr) = CString::new(k.as_bytes()) else { - return Err(Error::CStringAllocation); - }; - let (success, maybe_error): (bool, Option) = unsafe { - try_func!( - larodMapSetIntArr4, - self.raw, - key_cstr.as_ptr(), - v.0, - v.1, - v.2, - v.3 - ) - }; - - if success { - debug_assert!( - maybe_error.is_none(), - "larodMapSetIntArr4 indicated success AND returned an error!" - ); - Ok(()) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - - /// Get a string from a larodMap object. - /// Example - /// ```rust - /// use larod::LarodMap; - /// - /// let map = LarodMap::new().expect("Error creating map"); - /// map.set_string("key", "value").expect("Error setting string value for larodMap"); - /// let returned_string = map.get_string("key").expect("Unable to return value for \"key\""); - /// ``` - pub fn get_string(&self, k: &str) -> Result { - let Ok(key_cstr) = CString::new(k) else { - return Err(Error::CStringAllocation); - }; - let (c_str_ptr, maybe_error): (*const c_char, Option) = - unsafe { try_func!(larodMapGetStr, self.raw, key_cstr.as_ptr()) }; - let c_str = unsafe { CStr::from_ptr(c_str_ptr) }; - if let Ok(rs) = c_str.to_str() { - debug_assert!( - maybe_error.is_none(), - "larodMapGetStr returned a string AND returned an error!" - ); - Ok(String::from(rs)) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - - /// Get an integer array of 4 items from a larodMap object. - /// Example - /// ```rust - /// use larod::LarodMap; - /// - /// let map = LarodMap::new().expect("Error creating map"); - /// map.set_int("key", 45).expect("Error setting integer array for larodMap"); - /// let value = map.get_int("key").expect("Unable to get array values for \"key\""); - /// ``` - pub fn get_int(&self, k: &str) -> Result { - let Ok(key_cstr) = CString::new(k) else { - return Err(Error::CStringAllocation); - }; - let mut v: i64 = 0; - let (success, maybe_error): (bool, Option) = - unsafe { try_func!(larodMapGetInt, self.raw, key_cstr.as_ptr(), &mut v) }; - if success { - debug_assert!( - maybe_error.is_none(), - "larodMapGetInt indicated success AND returned an error!" - ); - Ok(v) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - - /// Get an integer array of 4 items from a larodMap object. - /// Example - /// ```rust - /// use larod::LarodMap; - /// - /// let map = LarodMap::new().expect("Error creating map"); - /// map.set_int_arr2("key", (45, 64)).expect("Error setting integer array for larodMap"); - /// let returned_array = map.get_int_arr2("key").expect("Unable to get array values for \"key\""); - /// ``` - pub fn get_int_arr2(&self, k: &str) -> Result<&[i64; 2]> { - let Ok(key_cstr) = CString::new(k) else { - return Err(Error::CStringAllocation); - }; - let (out_arr, maybe_error) = - unsafe { try_func!(larodMapGetIntArr2, self.raw, key_cstr.as_ptr()) }; - if !out_arr.is_null() { - debug_assert!( - maybe_error.is_none(), - "larodMapGetInt indicated success AND returned an error!" - ); - unsafe { - slice::from_raw_parts(out_arr, 2) - .try_into() - .or(Err(Error::PointerToInvalidData)) - } - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - - /// Get an integer array of 4 items from a larodMap object. - /// Example - /// ```rust - /// use larod::LarodMap; - /// - /// let map = LarodMap::new().expect("Error creating map"); - /// map.set_int_arr4("key", (45, 64, 36, 23)).expect("Error setting integer array for larodMap"); - /// let returned_array = map.get_int_arr4("key").expect("Unable to get array values for \"key\""); - /// ``` - pub fn get_int_arr4(&self, k: &str) -> Result<&[i64; 4]> { - let Ok(key_cstr) = CString::new(k) else { - return Err(Error::CStringAllocation); - }; - let (out_arr, maybe_error) = - unsafe { try_func!(larodMapGetIntArr4, self.raw, key_cstr.as_ptr()) }; - if !out_arr.is_null() { - debug_assert!( - maybe_error.is_none(), - "larodMapGetIntArr4 indicated success AND returned an error!" - ); - unsafe { - slice::from_raw_parts(out_arr, 4) - .try_into() - .or(Err(Error::PointerToInvalidData)) - } - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } -} - -impl std::ops::Drop for LarodMap { - fn drop(&mut self) { - unsafe { - larodDestroyMap(&mut self.raw); - } - } -} - -#[derive(Eq, PartialEq, Hash)] -pub struct Tensor<'a> { - ptr: *mut larodTensor, - phantom: PhantomData<&'a Session<'a>>, -} - -/// A structure representing a larodTensor. -impl<'a> Tensor<'a> { - fn as_ptr(&self) -> *const larodTensor { - self.ptr.cast_const() - } - pub fn name() {} - pub fn byte_size() {} - pub fn dims() {} - pub fn set_dims() {} - pub fn pitches() {} - pub fn set_pitches() {} - pub fn data_type() {} - pub fn set_data_type() {} - pub fn layout() {} - pub fn set_layout() {} - pub fn fd() {} - pub fn set_fd() {} - pub fn fd_size() {} - pub fn set_fd_size() {} - pub fn fd_offset() {} - pub fn set_fd_offset() {} - pub fn fd_props() {} - pub fn set_fd_props() {} -} - -/// A type representing a larodDevice. -/// The lifetime of LarodDevice is explicitly tied to the lifetime of a -/// [Session]. So using a LarodDevice after the Session it was acquired from -/// will cause compilation to fail. -/// ```compile_fail -/// use larod::Session; -/// let sess = Session::new(); -/// let first_device = sess -/// .devices() -/// .expect("unable to get devices") -/// .pop() -/// .expect("empty devices list!"); -/// drop(sess); -/// println!("{:?}", first_device.name()); -/// ``` -#[derive(Debug)] -pub struct LarodDevice<'a> { - // The caller does not get ownership of the returned pointer and must not - // attempt to free it. The lifetime of the memory pointed to expires when - // conn closes. - ptr: *const larodDevice, - phantom: PhantomData<&'a Session<'a>>, -} - -impl<'a> LarodDevice<'a> { - /// Get the name of a larodDevice. - pub fn name(&self) -> Result { - unsafe { - let (c_char_ptr, maybe_error) = try_func!(larodGetDeviceName, self.ptr); - if !c_char_ptr.is_null() { - debug_assert!( - maybe_error.is_none(), - "larodGetDeviceName returned an object pointer AND returned an error!" - ); - let c_name = CStr::from_ptr(c_char_ptr); - c_name - .to_str() - .map(String::from) - .map_err(|_e| Error::InvalidLarodMessage) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - } - - /// Get the instance of a larodDevice. - /// From the larod documentation - /// > *In case there are multiple identical devices that are available in the service, they are distinguished by an instance number, with the first instance starting from zero.* - pub fn instance(&self) -> Result { - unsafe { - let mut instance: u32 = 0; - let (success, maybe_error) = try_func!(larodGetDeviceInstance, self.ptr, &mut instance); - if success { - debug_assert!( - maybe_error.is_none(), - "larodGetDeviceInstance returned success AND returned an error!" - ); - Ok(instance) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - } -} - -pub struct LarodModel { - ptr: *mut larodModel, -} - -impl LarodModel { - pub fn id() -> Result<()> { - Ok(()) - } - pub fn chip() -> Result<()> { - Ok(()) - } - pub fn device() -> Result<()> { - Ok(()) - } - pub fn size() -> Result<()> { - Ok(()) - } - pub fn name() -> Result<()> { - Ok(()) - } - pub fn access() -> Result<()> { - Ok(()) - } - pub fn num_inputs() -> Result<()> { - Ok(()) - } - pub fn num_outputs() -> Result<()> { - Ok(()) - } - pub fn create_model_inputs() -> Result<()> { - Ok(()) - } - pub fn create_model_outputs() -> Result<()> { - Ok(()) - } -} - -impl Drop for LarodModel { - fn drop(&mut self) { - unsafe { larodDestroyModel(&mut self.ptr) }; - } -} - -pub struct SessionBuilder {} - -impl SessionBuilder { - pub fn new() -> SessionBuilder { - SessionBuilder {} - } - pub fn build(&self) -> Result> { - let mut conn: *mut larodConnection = ptr::null_mut(); - let (success, maybe_error): (bool, Option) = - unsafe { try_func!(larodConnect, &mut conn) }; - if success { - debug_assert!( - maybe_error.is_none(), - "larodConnect indicated success AND returned an error!" - ); - Ok(Session { - conn, - model_map: HashMap::new(), - phantom: PhantomData, - }) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } -} - -impl Default for SessionBuilder { - fn default() -> Self { - SessionBuilder::new() - } -} - -pub struct Session<'a> { - conn: *mut larodConnection, - model_map: HashMap, - phantom: PhantomData<&'a larodConnection>, -} - -// Using a session builder might not be necessary. -// There's little to configure when starting a session. -impl<'a> Session<'a> { - /// Constructs a new `Session`. - /// - /// # Panics - /// - /// Use `Session::builder()` if you wish to handle the failure as an `Error` - /// instead of panicking. - pub fn new() -> Session<'a> { - SessionBuilder::new().build().expect("Session::new()") - } - pub fn builder() -> SessionBuilder { - SessionBuilder::new() - } - pub fn disconnect(&mut self) -> Result<()> { - let (success, maybe_error): (bool, Option) = - unsafe { try_func!(larodDisconnect, &mut self.conn) }; - if success { - debug_assert!( - maybe_error.is_none(), - "larodDisconnect indicated success AND returned an error!" - ); - Ok(()) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - pub fn num_sessions() -> Result<()> { - Ok(()) - } - - /// Returns a reference to an available device - pub fn device(&self, name: &str, instance: u32) -> Result { - let Ok(name_cstr) = CString::new(name) else { - return Err(Error::CStringAllocation); - }; - let (device_ptr, maybe_error) = - unsafe { try_func!(larodGetDevice, self.conn, name_cstr.as_ptr(), instance) }; - if !device_ptr.is_null() { - debug_assert!( - maybe_error.is_none(), - "larodGetDevice indicated success AND returned an error!" - ); - Ok(LarodDevice { - ptr: device_ptr, - phantom: PhantomData, - }) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - - pub fn list_chips() -> Result<()> { - Ok(()) - } - - /// Get a reference to a HashMap of name LarodDevice pairs. - pub fn devices(&self) -> Result> { - let mut num_devices: usize = 0; - let (dev_ptr, maybe_error) = - unsafe { try_func!(larodListDevices, self.conn, &mut num_devices) }; - if dev_ptr.is_null() { - return Err(maybe_error.unwrap_or(Error::MissingLarodError)); - } - let raw_devices = - unsafe { slice::from_raw_parts::<'a, *const larodDevice>(dev_ptr, num_devices) }; - - let devices: Vec = raw_devices - .iter() - .map(|ptr| LarodDevice { - ptr: *ptr, - phantom: PhantomData, - }) - .collect(); - - Ok(devices) - } - - // Overloaded need to check that. - pub fn load_model>( - &self, - path: T, - name: &str, - device: &LarodDevice, - access: &LarodAccess, - params: &LarodMap, - ) -> Result { - let file = File::open(path).map_err(Error::IOError)?; - let name_cstr = CString::new(name).map_err(|_e| Error::CStringAllocation)?; - let (model_ptr, maybe_error) = unsafe { - try_func!( - larodLoadModel, - self.conn, - file.as_raw_fd(), - device.ptr, - *access, - name_cstr.as_ptr(), - params.raw - ) - }; - if !model_ptr.is_null() { - debug_assert!( - maybe_error.is_none(), - "larodLoadModel indicated success AND returned an error!" - ); - Ok(LarodModel { ptr: model_ptr }) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - pub fn get_model(&self, model_id: u64) -> Result { - let (model_ptr, maybe_error) = unsafe { try_func!(larodGetModel, self.conn, model_id) }; - if !model_ptr.is_null() { - debug_assert!( - maybe_error.is_none(), - "larodGetModel indicated success AND returned an error!" - ); - Ok(LarodModel { ptr: model_ptr }) - } else { - Err(maybe_error.unwrap_or(Error::MissingLarodError)) - } - } - pub fn models() -> Result<()> { - Ok(()) - } - pub fn delete_model(&self) -> Result<()> { - Ok(()) - } - pub fn alloc_model_inputs() -> Result<()> { - Ok(()) - } - pub fn alloc_model_outputs() -> Result<()> { - Ok(()) - } - pub fn destroy_tensors() -> Result<()> { - Ok(()) - } - pub fn track_tensor() -> Result<()> { - Ok(()) - } - pub fn run_job() -> Result<()> { - Ok(()) - } - pub fn run_inference() -> Result<()> { - Ok(()) - } - pub fn chip_id() -> Result<()> { - Ok(()) - } - pub fn chip_type() -> Result<()> { - Ok(()) - } -} - -impl<'a> Default for Session<'a> { - fn default() -> Self { - SessionBuilder::default() - .build() - .expect("Session::default()") - } -} - -impl<'a> std::ops::Drop for Session<'a> { - fn drop(&mut self) { - unsafe { - try_func!(larodDisconnect, &mut self.conn); - } - } -} - -#[cfg(all(test, target_arch = "aarch64", feature = "device-tests"))] -mod tests { - use super::*; - - #[test] - fn it_creates_larod_map() { - assert!(LarodMap::new().is_ok()); - } - - #[test] - fn it_drops_map() { - let map = LarodMap::new().unwrap(); - std::mem::drop(map); - } - - #[test] - fn larod_map_can_set_str() { - let mut map = LarodMap::new().unwrap(); - map.set_string("test_key", "test_value").unwrap(); - } - - #[test] - fn larod_map_can_get_str() { - let mut map = LarodMap::new().unwrap(); - map.set_string("test_key", "this_value").unwrap(); - let s = map.get_string("test_key").unwrap(); - assert_eq!(s, String::from("this_value")); - } - - #[test] - fn larod_map_can_set_int() { - let mut map = LarodMap::new().unwrap(); - map.set_int("test_key", 10).unwrap(); - } - - #[test] - fn larod_map_can_get_int() { - let mut map = LarodMap::new().unwrap(); - map.set_int("test_key", 9).unwrap(); - let i = map.get_int("test_key").unwrap(); - assert_eq!(i, 9); - } - - #[test] - fn larod_map_can_set_2_tuple() { - let mut map = LarodMap::new().unwrap(); - map.set_int_arr2("test_key", (1, 2)).unwrap(); - } - #[test] - fn larod_map_can_get_2_tuple() { - let mut map = LarodMap::new().unwrap(); - map.set_int_arr2("test_key", (5, 6)).unwrap(); - let arr = map.get_int_arr2("test_key").unwrap(); - assert_eq!(arr[0], 5); - assert_eq!(arr[1], 6); - } - - #[test] - fn larod_map_can_set_4_tuple() { - let mut map = LarodMap::new().unwrap(); - map.set_int_arr4("test_key", (1, 2, 3, 4)).unwrap(); - } - - #[test] - fn larod_map_can_get_4_tuple() { - let mut map = LarodMap::new().unwrap(); - map.set_int_arr4("test_key", (1, 2, 3, 4)).unwrap(); - let arr = map.get_int_arr4("test_key").unwrap(); - assert_eq!(arr[0], 1); - assert_eq!(arr[1], 2); - assert_eq!(arr[2], 3); - assert_eq!(arr[3], 4); - } - - #[test] - fn it_establishes_session() { - let sess = Session::new(); - } - - #[test] - fn it_lists_devices() { - let sess = Session::new(); - let devices = sess.devices().unwrap(); - for device in devices { - println!( - "device: {}, id: {}, addr: {:?}", - device.name().unwrap(), - device.instance().unwrap(), - unsafe { std::ptr::addr_of!(*device.ptr) }, - ); - } - } -} From bac69d720b93d3d9ebe88f8a4694256c3ce66b7e Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Tue, 17 Dec 2024 13:55:19 -0500 Subject: [PATCH 42/54] Combine height and width methods into single resolution method. --- crates/vdo/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index e8ab9ae6..0c84b3b3 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -268,11 +268,10 @@ impl StreamBuilder { self.format = format; self } - pub fn width(mut self, width: u32) -> Self { + + /// Set the resolution for the stream. + pub fn resolution(mut self, width: u32, height: u32) -> Self { self.width = width; - self - } - pub fn height(mut self, height: u32) -> Self { self.height = height; self } From 9b613ef2873fc3347ab85a0994538fd4d822b449 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 18 Dec 2024 06:38:40 -0500 Subject: [PATCH 43/54] Fix StreamBuilder.resolution() in a couple tests. --- crates/vdo/src/lib.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index 0c84b3b3..db54f218 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -572,12 +572,11 @@ mod tests { #[test] fn stream_error_with_no_buffers() { - env_logger::try_init(); + env_logger::builder().is_test(true).try_init(); let mut stream = Stream::builder() .channel(0) .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) - .width(1920) - .height(1080) + .resolution(1920, 1080) .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) .build() .expect("Unable to create stream"); @@ -594,12 +593,11 @@ mod tests { #[test] fn it_starts_stream() { - env_logger::try_init(); + env_logger::builder().is_test(true).try_init(); let mut stream = Stream::builder() .channel(0) .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) - .width(1920) - .height(1080) + .resolution(1920, 1080) .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) .build() .expect("Unable to create stream"); @@ -617,20 +615,19 @@ mod tests { } #[test] - fn stream_fetches_frames() { - env_logger::try_init(); + fn stream_fetches_frames_explicitly() { + env_logger::builder().is_test(true).try_init(); let mut stream = Stream::builder() .channel(0) .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) - .width(1920) - .height(1080) + .resolution(1920, 1080) .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) .build() .expect("Unable to create stream"); stream.allocate_buffers(5); let mut r = stream.start().expect("starting stream returned error"); - { + for _ in 0..3 { let buff = r.iter().next().expect("failed to fetch frame"); let size = buff .frame() From bac7fb2838e97b82588e95e31b96928e9a765225 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 18 Dec 2024 06:56:12 -0500 Subject: [PATCH 44/54] Abandon allowing other buffer strategies and only use VDO_BUFFER_STRATEGY_INFINITE. --- crates/vdo/src/lib.rs | 103 ++++++++++++------------------------------ 1 file changed, 30 insertions(+), 73 deletions(-) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index db54f218..f8fbdc77 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -220,12 +220,14 @@ pub struct StreamBuilder { } impl Default for StreamBuilder { + /// VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT only works for VdoFormat::VDO_FORMAT_YUV and RGB + /// VdoBufferStrategy::VDO_BUFFER_STRATEGY_INFINITE works for all VdoFormat's. fn default() -> Self { StreamBuilder { format: VdoFormat::VDO_FORMAT_H264, buffer_access: 0, buffer_count: 0, - buffer_strategy: VdoBufferStrategy::VDO_BUFFER_STRATEGY_NONE, + buffer_strategy: VdoBufferStrategy::VDO_BUFFER_STRATEGY_INFINITE, input: 0, channel: 0, width: 0, @@ -255,8 +257,11 @@ impl StreamBuilder { StreamBuilder::default() } - pub fn buffer_strategy(mut self, strategy: VdoBufferStrategy) -> Self { - self.buffer_strategy = strategy; + /// Set the number of buffers to use for the stream. + /// For VdoFormat::VDO_FORMAT_YUV and RGB, the default number of buffers is 3. + /// For VdoFormat::VDO_FORMAT_JPEG and h26x this number is ignored. + pub fn buffers(mut self, num_buffers: u32) -> Self { + self.buffer_count = num_buffers; self } @@ -282,6 +287,7 @@ impl StreamBuilder { map.set_u32("format", self.format as u32)?; map.set_u32("width", self.width)?; map.set_u32("height", self.height)?; + map.set_u32("buffer.count", self.buffer_count)?; map.set_u32("buffer.strategy", self.buffer_strategy as u32)?; let (stream_raw, maybe_error) = unsafe { try_func!(vdo_stream_new, map.raw, None) }; if !stream_raw.is_null() { @@ -438,56 +444,8 @@ impl Stream { } } - pub fn allocate_buffer(&mut self) -> Result<()> { - let (buffer_ptr, maybe_error) = - unsafe { try_func!(vdo_stream_buffer_alloc, self.raw.0, ptr::null_mut()) }; - if !buffer_ptr.is_null() { - debug_assert!( - maybe_error.is_none(), - "vdo_stream_buffer_alloc returned a buffer pointer AND returned an error!" - ); - self.buffers.push(buffer_ptr); - Ok(()) - } else { - Err(maybe_error.unwrap_or(Error::MissingVDOError)) - } - } - - pub fn allocate_buffers(&mut self, num: usize) -> Result<()> { - for _ in 0..num { - let (buffer_ptr, maybe_error) = - unsafe { try_func!(vdo_stream_buffer_alloc, self.raw.0, ptr::null_mut()) }; - if !buffer_ptr.is_null() { - debug_assert!( - maybe_error.is_none(), - "vdo_stream_buffer_alloc returned a buffer pointer AND returned an error!" - ); - self.buffers.push(buffer_ptr); - } else { - return Err(maybe_error.unwrap_or(Error::MissingVDOError)); - } - } - Ok(()) - } - /// Request the Larod service to start fetching frames and passing back buffers. pub fn start(&mut self) -> Result { - if self.buffers.is_empty() { - return Err(Error::NoBuffersAllocated); - } - for buffer in self.buffers.iter() { - let (success_enqueue, maybe_error) = - unsafe { try_func!(vdo_stream_buffer_enqueue, self.raw.0, *buffer) }; - if success_enqueue == GTRUE { - debug!("enqueued buffer to stream"); - debug_assert!( - maybe_error.is_none(), - "vdo_stream_buffer_enqueue indicated success AND returned an error!" - ); - } else { - return Err(maybe_error.unwrap_or(Error::MissingVDOError)); - } - } let (success_start, maybe_error) = unsafe { try_func!(vdo_stream_start, self.raw.0) }; if success_start == GTRUE { debug_assert!( @@ -569,74 +527,73 @@ impl Drop for Stream { #[cfg(all(test, target_arch = "aarch64", feature = "device-tests"))] mod tests { use super::*; + use anyhow::Context; #[test] - fn stream_error_with_no_buffers() { + fn stream_error_with_no_buffers() -> anyhow::Result<()> { env_logger::builder().is_test(true).try_init(); let mut stream = Stream::builder() .channel(0) .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) .resolution(1920, 1080) - .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) .build() - .expect("Unable to create stream"); + .context("Unable to create stream")?; // let settings = stream // .settings() // .expect("error while getting stream settings"); // settings.dump(); // let info = stream.info().expect("error while getting stream info"); // info.dump(); - let r = stream.start(); - assert!(r.is_err()); - assert!(matches!(r, Err(Error::NoBuffersAllocated))); + let mut r = stream.start().context("Unable to start stream")?; + r.stop()?; + Ok(()) } #[test] - fn it_starts_stream() { + fn it_starts_stream() -> anyhow::Result<()> { env_logger::builder().is_test(true).try_init(); let mut stream = Stream::builder() .channel(0) .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) .resolution(1920, 1080) - .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) .build() - .expect("Unable to create stream"); + .context("Unable to create stream")?; // let settings = stream // .settings() // .expect("error while getting stream settings"); // settings.dump(); // let info = stream.info().expect("error while getting stream info"); // info.dump(); - stream.allocate_buffers(5); - let mut r = stream.start().expect("starting stream returned error"); + // stream.allocate_buffers(5); + let mut r = stream.start().context("starting stream returned error")?; - let s = r.stop(); - assert!(s.is_ok()); + r.stop().context("Unable to stop stream")?; + Ok(()) } #[test] - fn stream_fetches_frames_explicitly() { + fn stream_fetches_frames_infinitely() -> anyhow::Result<()> { env_logger::builder().is_test(true).try_init(); let mut stream = Stream::builder() .channel(0) .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) .resolution(1920, 1080) - .buffer_strategy(VdoBufferStrategy::VDO_BUFFER_STRATEGY_EXPLICIT) + .buffers(5) .build() - .expect("Unable to create stream"); - stream.allocate_buffers(5); - let mut r = stream.start().expect("starting stream returned error"); + .context("Unable to create stream")?; + let mut r = stream.start().context("starting stream returned error")?; - for _ in 0..3 { - let buff = r.iter().next().expect("failed to fetch frame"); + for _ in 0..10 { + let buff = r.iter().next().context("failed to fetch frame")?; let size = buff .frame() - .expect("error fetching frame for buffer") + .context("error fetching frame for buffer")? .size(); info!("frame size: {}", size); assert!(size > 0); } - let s = r.stop(); + r.stop().context("Unable to stop stream")?; + Ok(()) } } From a096a3231f6e9bd40003d9a88bfffbd737be29dd Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 18 Dec 2024 07:35:42 -0500 Subject: [PATCH 45/54] Add tests for other VdoFormats. --- crates/vdo/src/lib.rs | 141 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 126 insertions(+), 15 deletions(-) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index f8fbdc77..d4aa4e6d 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -530,7 +530,7 @@ mod tests { use anyhow::Context; #[test] - fn stream_error_with_no_buffers() -> anyhow::Result<()> { + fn stream_starts_without_explicit_buffers() -> anyhow::Result<()> { env_logger::builder().is_test(true).try_init(); let mut stream = Stream::builder() .channel(0) @@ -538,35 +538,146 @@ mod tests { .resolution(1920, 1080) .build() .context("Unable to create stream")?; - // let settings = stream - // .settings() - // .expect("error while getting stream settings"); - // settings.dump(); - // let info = stream.info().expect("error while getting stream info"); - // info.dump(); let mut r = stream.start().context("Unable to start stream")?; r.stop()?; Ok(()) } #[test] - fn it_starts_stream() -> anyhow::Result<()> { + fn stream_starts_with_explicit_buffers() -> anyhow::Result<()> { env_logger::builder().is_test(true).try_init(); let mut stream = Stream::builder() .channel(0) .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) .resolution(1920, 1080) + .buffers(5) + .build() + .context("Unable to create stream")?; + let mut r = stream.start().context("Unable to start stream")?; + for _ in 0..10 { + let buff = r.iter().next().context("failed to fetch frame")?; + let size = buff + .frame() + .context("error fetching frame for buffer")? + .size(); + info!("frame size: {}", size); + assert!(size > 0); + } + r.stop()?; + Ok(()) + } + + #[test] + fn stream_starts_with_rgb() -> anyhow::Result<()> { + env_logger::builder().is_test(true).try_init(); + let mut stream = Stream::builder() + .channel(0) + .format(VdoFormat::VDO_FORMAT_PLANAR_RGB) + .resolution(1920, 1080) + .build() + .context("Unable to create stream")?; + let mut r = stream.start().context("starting stream returned error")?; + for _ in 0..10 { + let buff = r.iter().next().context("failed to fetch frame")?; + let size = buff + .frame() + .context("error fetching frame for buffer")? + .size(); + info!("frame size: {}", size); + assert!(size > 0); + } + r.stop().context("Unable to stop stream")?; + Ok(()) + } + + #[test] + fn stream_starts_with_jpeg() -> anyhow::Result<()> { + env_logger::builder().is_test(true).try_init(); + let mut stream = Stream::builder() + .channel(0) + .format(VdoFormat::VDO_FORMAT_JPEG) + .resolution(1920, 1080) .build() .context("Unable to create stream")?; - // let settings = stream - // .settings() - // .expect("error while getting stream settings"); - // settings.dump(); - // let info = stream.info().expect("error while getting stream info"); - // info.dump(); - // stream.allocate_buffers(5); let mut r = stream.start().context("starting stream returned error")?; + for _ in 0..10 { + let buff = r.iter().next().context("failed to fetch frame")?; + let size = buff + .frame() + .context("error fetching frame for buffer")? + .size(); + info!("frame size: {}", size); + assert!(size > 0); + } + r.stop().context("Unable to stop stream")?; + Ok(()) + } + #[test] + fn stream_starts_with_yuv() -> anyhow::Result<()> { + env_logger::builder().is_test(true).try_init(); + let mut stream = Stream::builder() + .channel(0) + .format(VdoFormat::VDO_FORMAT_YUV) + .resolution(1920, 1080) + .build() + .context("Unable to create stream")?; + let mut r = stream.start().context("starting stream returned error")?; + for _ in 0..10 { + let buff = r.iter().next().context("failed to fetch frame")?; + let size = buff + .frame() + .context("error fetching frame for buffer")? + .size(); + info!("frame size: {}", size); + assert!(size > 0); + } + r.stop().context("Unable to stop stream")?; + Ok(()) + } + + #[test] + fn stream_starts_with_h264() -> anyhow::Result<()> { + env_logger::builder().is_test(true).try_init(); + let mut stream = Stream::builder() + .channel(0) + .format(VdoFormat::VDO_FORMAT_H264) + .resolution(1920, 1080) + .build() + .context("Unable to create stream")?; + let mut r = stream.start().context("starting stream returned error")?; + for _ in 0..10 { + let buff = r.iter().next().context("failed to fetch frame")?; + let size = buff + .frame() + .context("error fetching frame for buffer")? + .size(); + info!("frame size: {}", size); + assert!(size > 0); + } + r.stop().context("Unable to stop stream")?; + Ok(()) + } + + #[test] + fn stream_starts_with_h265() -> anyhow::Result<()> { + env_logger::builder().is_test(true).try_init(); + let mut stream = Stream::builder() + .channel(0) + .format(VdoFormat::VDO_FORMAT_H265) + .resolution(1920, 1080) + .build() + .context("Unable to create stream")?; + let mut r = stream.start().context("starting stream returned error")?; + for _ in 0..10 { + let buff = r.iter().next().context("failed to fetch frame")?; + let size = buff + .frame() + .context("error fetching frame for buffer")? + .size(); + info!("frame size: {}", size); + assert!(size > 0); + } r.stop().context("Unable to stop stream")?; Ok(()) } From d7dba525532e83de666c95a51064035af2915877 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 2 Jan 2025 11:58:12 -0500 Subject: [PATCH 46/54] Add capacity, as_slice, and as_mut_slice methods to StreamBuffer. --- crates/vdo/src/lib.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index d4aa4e6d..374bcc4c 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -326,6 +326,32 @@ pub struct StreamBuffer<'a> { } impl<'a> StreamBuffer<'a> { + pub fn capacity(&self) -> usize { + unsafe { vdo_buffer_get_capacity(self.buffer.raw) } + } + pub fn as_slice(&self) -> Result<&[u8]> { + let buffer_data = unsafe { vdo_buffer_get_data(self.buffer.raw) }; + if !buffer_data.is_null() { + let slice = + unsafe { std::slice::from_raw_parts(buffer_data as *mut u8, self.capacity()) } as _; + Ok(slice) + } else { + Err(Error::NullPointer) + } + } + + pub fn as_mut_slice(&self) -> Result<&mut [u8]> { + let buffer_data = unsafe { vdo_buffer_get_data(self.buffer.raw) }; + if !buffer_data.is_null() { + let slice = + unsafe { std::slice::from_raw_parts_mut(buffer_data as *mut u8, self.capacity()) } + as _; + Ok(slice) + } else { + Err(Error::NullPointer) + } + } + pub fn frame(&self) -> Result { let frame = unsafe { vdo_buffer_get_frame(self.buffer.raw) }; Ok(Frame { From af80b950e2f7db70188c7ccbd8108d4b67ab28d9 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Thu, 2 Jan 2025 12:40:10 -0500 Subject: [PATCH 47/54] Synchronize gobject-sys version with workspace glib-sys version. --- Cargo.lock | 80 +++++++++------------------------------ crates/vdo-sys/Cargo.toml | 2 +- crates/vdo/Cargo.toml | 2 +- 3 files changed, 20 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20f9c8be..e999d5aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,7 +190,7 @@ version = "0.0.0" dependencies = [ "axevent-sys", "glib", - "glib-sys 0.19.8", + "glib-sys", "log", "thiserror", ] @@ -200,7 +200,7 @@ name = "axevent-sys" version = "0.0.0" dependencies = [ "bindgen", - "glib-sys 0.19.8", + "glib-sys", "pkg-config", ] @@ -240,7 +240,7 @@ version = "0.0.0" dependencies = [ "axstorage-sys", "glib", - "glib-sys 0.19.8", + "glib-sys", ] [[package]] @@ -248,7 +248,7 @@ name = "axstorage-sys" version = "0.0.0" dependencies = [ "bindgen", - "glib-sys 0.19.8", + "glib-sys", "pkg-config", ] @@ -496,16 +496,6 @@ dependencies = [ "target-lexicon", ] -[[package]] -name = "cfg-expr" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c360837f8f19e2e4468275138f1c0dec1647d1e17bb7c0215fe3cd7530e93c25" -dependencies = [ - "smallvec", - "target-lexicon", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -1006,10 +996,10 @@ version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cd743ba4714d671ad6b6234e8ab2a13b42304d0e13ab7eba1dcdd78a7d6d4ef" dependencies = [ - "glib-sys 0.19.8", - "gobject-sys 0.19.8", + "glib-sys", + "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", "windows-sys 0.52.0", ] @@ -1027,8 +1017,8 @@ dependencies = [ "futures-util", "gio-sys", "glib-macros", - "glib-sys 0.19.8", - "gobject-sys 0.19.8", + "glib-sys", + "gobject-sys", "libc", "memchr", "smallvec", @@ -1055,17 +1045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c2dc18d3a82b0006d470b13304fbbb3e0a9bd4884cf985a60a7ed733ac2c4a5" dependencies = [ "libc", - "system-deps 6.2.2", -] - -[[package]] -name = "glib-sys" -version = "0.20.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b1827e8621fc42c0dfb228e5d57ff6a71f9699e666ece8113f979ad87c2de" -dependencies = [ - "libc", - "system-deps 7.0.3", + "system-deps", ] [[package]] @@ -1080,20 +1060,9 @@ version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e697e252d6e0416fd1d9e169bda51c0f1c926026c39ca21fbe8b1bb5c3b8b9e" dependencies = [ - "glib-sys 0.19.8", - "libc", - "system-deps 6.2.2", -] - -[[package]] -name = "gobject-sys" -version = "0.20.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c674d2ff8478cf0ec29d2be730ed779fef54415a2fb4b565c52def62696462" -dependencies = [ - "glib-sys 0.20.6", + "glib-sys", "libc", - "system-deps 7.0.3", + "system-deps", ] [[package]] @@ -1399,7 +1368,7 @@ dependencies = [ name = "licensekey" version = "0.0.0" dependencies = [ - "glib-sys 0.19.8", + "glib-sys", "libc", "licensekey-sys", "thiserror", @@ -2356,20 +2325,7 @@ version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ - "cfg-expr 0.15.8", - "heck", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "system-deps" -version = "7.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" -dependencies = [ - "cfg-expr 0.17.1", + "cfg-expr", "heck", "pkg-config", "toml", @@ -2874,8 +2830,8 @@ dependencies = [ "anyhow", "env_logger", "glib", - "glib-sys 0.19.8", - "gobject-sys 0.20.4", + "glib-sys", + "gobject-sys", "log", "thiserror", "vdo-sys", @@ -2886,8 +2842,8 @@ name = "vdo-sys" version = "0.0.0" dependencies = [ "bindgen", - "glib-sys 0.19.8", - "gobject-sys 0.20.4", + "glib-sys", + "gobject-sys", "pkg-config", ] diff --git a/crates/vdo-sys/Cargo.toml b/crates/vdo-sys/Cargo.toml index 18511fd7..523270db 100644 --- a/crates/vdo-sys/Cargo.toml +++ b/crates/vdo-sys/Cargo.toml @@ -9,4 +9,4 @@ pkg-config = { workspace = true } [dependencies] glib-sys = { workspace = true } -gobject-sys = "0.20.4" +gobject-sys = "0.19.5" diff --git a/crates/vdo/Cargo.toml b/crates/vdo/Cargo.toml index 43a60934..c1bc3558 100644 --- a/crates/vdo/Cargo.toml +++ b/crates/vdo/Cargo.toml @@ -9,7 +9,7 @@ log.workspace = true anyhow.workspace = true glib.workspace = true glib-sys.workspace = true -gobject-sys = "0.20.4" +gobject-sys = "0.19.5" thiserror.workspace = true [features] From 441d1ce40568a775d4b327b78e99f6bb2e2b9068 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 12 Feb 2025 15:05:53 -0500 Subject: [PATCH 48/54] be able to set stream framerate --- crates/vdo/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index 374bcc4c..9f2dee9c 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -281,12 +281,18 @@ impl StreamBuilder { self } + pub fn framerate(mut self, framerate: u32) -> Self { + self.framerate = framerate; + self + } + pub fn build(self) -> Result { let map = Map::new()?; map.set_u32("channel", self.channel)?; map.set_u32("format", self.format as u32)?; map.set_u32("width", self.width)?; map.set_u32("height", self.height)?; + map.set_u32("framerate", self.framerate)?; map.set_u32("buffer.count", self.buffer_count)?; map.set_u32("buffer.strategy", self.buffer_strategy as u32)?; let (stream_raw, maybe_error) = unsafe { try_func!(vdo_stream_new, map.raw, None) }; From c9f29b0e810deabe83630601359758fe9a49a1fd Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 12 Feb 2025 15:06:19 -0500 Subject: [PATCH 49/54] be able to get frame time --- crates/vdo/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index 9f2dee9c..1fa94f96 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -320,6 +320,10 @@ impl<'a> Frame<'a> { pub fn size(&self) -> usize { unsafe { vdo_frame_get_size(self.raw) } } + + pub fn get_type(&self) -> VdoFrameType { + unsafe { vdo_frame_get_frame_type(self.raw) } + } } pub struct Buffer { From bcb98b61c1cc98400ef6b71b978be18b98075b29 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 14 Feb 2025 07:08:08 -0500 Subject: [PATCH 50/54] remove extra remote-test.sh --- remote-test.sh | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100755 remote-test.sh diff --git a/remote-test.sh b/remote-test.sh deleted file mode 100755 index 1f5cf009..00000000 --- a/remote-test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -set -e -if [ -n "${CARGO_TEST_CAMERA}" ]; then - f=`basename $1` - scp "$1" $CARGO_TEST_CAMERA:. - # echo $f - ssh $CARGO_TEST_CAMERA "chmod +x /root/$f" - shift - ssh $CARGO_TEST_CAMERA "/root/$f" "$@" -else - $1 -fi From bb2d7f22a84d6a1b7341123855ab87c182780532 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 14 Feb 2025 09:28:02 -0500 Subject: [PATCH 51/54] remove unused imports --- crates/vdo/src/lib.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index 1fa94f96..16d23c2c 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -4,17 +4,14 @@ //! frame data in them. //! -use glib::translate::from_glib_full; -use glib_sys::{gboolean, gpointer, GError, GTRUE}; +use glib_sys::{gpointer, GError, GTRUE}; use gobject_sys::{g_object_unref, GObject}; -use log::{debug, error, info}; +use log::error; use std::ffi::{CStr, CString}; use std::fmt::{Debug, Display}; use std::marker::PhantomData; use std::mem; -use std::sync::{mpsc, Arc, Mutex, PoisonError}; -use std::thread::JoinHandle; -use std::{ptr, thread}; +use std::ptr; use vdo_sys::*; pub use vdo_sys::{VdoBufferStrategy, VdoFormat}; From bae84d61af7c9ce5c2a14c24fb3785c31f48b376 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Fri, 14 Feb 2025 15:31:06 -0500 Subject: [PATCH 52/54] clean up unused variables --- crates/vdo/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index 16d23c2c..6a6061aa 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -4,7 +4,7 @@ //! frame data in them. //! -use glib_sys::{gpointer, GError, GTRUE}; +use glib_sys::{GError, GTRUE}; use gobject_sys::{g_object_unref, GObject}; use log::error; use std::ffi::{CStr, CString}; @@ -189,6 +189,7 @@ impl Drop for Map { } } +#[allow(dead_code)] pub struct StreamBuilder { format: VdoFormat, buffer_access: u32, @@ -370,7 +371,7 @@ impl<'a> StreamBuffer<'a> { impl<'a> Drop for StreamBuffer<'a> { fn drop(&mut self) { - let (success, maybe_error) = unsafe { + let (_success, _maybe_error) = unsafe { try_func!( vdo_stream_buffer_unref, self.stream.raw.0, @@ -551,7 +552,7 @@ impl Drop for Stream { vdo_stream_stop(self.raw.0); } for mut buffer in mem::take(&mut self.buffers).into_iter() { - let (success, maybe_error) = + let (_success, _maybe_error) = unsafe { try_func!(vdo_stream_buffer_unref, self.raw.0, &mut buffer) }; } } From 44191dfaf189f173c6caa76c58c55a728bf0cb8e Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Wed, 19 Feb 2025 13:55:02 +0000 Subject: [PATCH 53/54] add functions for Frame --- crates/vdo/src/lib.rs | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/crates/vdo/src/lib.rs b/crates/vdo/src/lib.rs index 6a6061aa..9b53dedb 100644 --- a/crates/vdo/src/lib.rs +++ b/crates/vdo/src/lib.rs @@ -315,12 +315,47 @@ pub struct Frame<'a> { } impl<'a> Frame<'a> { + /// Returns the type of the frame + pub fn get_type(&self) -> VdoFrameType { + unsafe { vdo_frame_get_frame_type(self.raw) } + } + + /// Returns the sequence number of the frame. + /// The sequence number starts at 0. The point at which the sequence number + /// wraps around is undefined. + pub fn sequence_number(&self) -> u32 { + unsafe { vdo_frame_get_sequence_nbr(self.raw) } + } + + /// Return the timestamp of the frame. The time stamp is the number of + /// microseconds since boot. + pub fn timestamp(&self) -> u64 { + unsafe { vdo_frame_get_timestamp(self.raw) } + } + + /// Return the custom timestamp of the frame. + pub fn custom_timestamp(&self) -> i64 { + unsafe { vdo_frame_get_custom_timestamp(self.raw) } + } + + /// Return the size of the frame in bytes. pub fn size(&self) -> usize { unsafe { vdo_frame_get_size(self.raw) } } - pub fn get_type(&self) -> VdoFrameType { - unsafe { vdo_frame_get_frame_type(self.raw) } + pub fn header_size(&self) -> isize { + unsafe { vdo_frame_get_header_size(self.raw) } + } + + pub fn file_descriptor(&self) -> std::os::fd::BorrowedFd { + unsafe { + let fd = vdo_frame_get_fd(self.raw); + std::os::fd::BorrowedFd::borrow_raw(fd) + } + } + + pub fn is_last_buffer(&self) -> bool { + unsafe { vdo_frame_get_is_last_buffer(self.raw) != 0 } } } @@ -405,7 +440,6 @@ impl<'a> Iterator for StreamIterator<'a> { maybe_error.is_none(), "vdo_stream_get_buffer returned an stream pointer AND returned an error!" ); - debug!("fetched buffer from vdo stream"); Some(StreamBuffer { buffer: Buffer { raw: buffer_ptr }, stream: self.stream, From 7615a3bb6b0788393b442f5d59a402f561f862c4 Mon Sep 17 00:00:00 2001 From: Jonathan Green Date: Tue, 22 Apr 2025 21:25:08 -0400 Subject: [PATCH 54/54] use NewType enum --- crates/vdo-sys/build.rs | 6 +- crates/vdo-sys/src/bindings.rs | 1464 ++++++++++++++++++++++++-------- 2 files changed, 1103 insertions(+), 367 deletions(-) diff --git a/crates/vdo-sys/build.rs b/crates/vdo-sys/build.rs index 6fbe6d15..9f450272 100644 --- a/crates/vdo-sys/build.rs +++ b/crates/vdo-sys/build.rs @@ -9,10 +9,10 @@ fn populated_bindings(dst: &path::PathBuf) { .allowlist_type("^(_?Vdo.*)$") .allowlist_var("^(VDO_ERROR.*)$") .blocklist_function("vdo_stream_enqueue_buffer") - .default_enum_style(bindgen::EnumVariation::Rust { - non_exhaustive: true, + .default_enum_style(bindgen::EnumVariation::NewType { + is_global: false, + is_bitfield: true, }) - // .constified_enum("^(VDO_ERROR.*)$") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .layout_tests(false); for path in library.include_paths { diff --git a/crates/vdo-sys/src/bindings.rs b/crates/vdo-sys/src/bindings.rs index ca9641ef..a92f253d 100644 --- a/crates/vdo-sys/src/bindings.rs +++ b/crates/vdo-sys/src/bindings.rs @@ -73,119 +73,540 @@ impl ::std::cmp::PartialEq for __BindgenUnionField { } } impl ::std::cmp::Eq for __BindgenUnionField {} -#[repr(i32)] -#[non_exhaustive] +impl VdoWdrMode { + pub const VDO_WDR_MODE_NONE: VdoWdrMode = VdoWdrMode(-1); +} +impl VdoWdrMode { + pub const VDO_WDR_MODE_LINEAR: VdoWdrMode = VdoWdrMode(0); +} +impl VdoWdrMode { + pub const VDO_WDR_MODE_2X: VdoWdrMode = VdoWdrMode(1); +} +impl VdoWdrMode { + pub const VDO_WDR_MODE_3X: VdoWdrMode = VdoWdrMode(2); +} +impl VdoWdrMode { + pub const VDO_WDR_MODE_4X: VdoWdrMode = VdoWdrMode(3); +} +impl VdoWdrMode { + pub const VDO_WDR_MODE_SENSOR: VdoWdrMode = VdoWdrMode(4); +} +impl VdoWdrMode { + pub const VDO_WDR_MODE_OFF: VdoWdrMode = VdoWdrMode(5); +} +impl ::std::ops::BitOr for VdoWdrMode { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoWdrMode(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoWdrMode { + #[inline] + fn bitor_assign(&mut self, rhs: VdoWdrMode) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoWdrMode { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoWdrMode(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoWdrMode { + #[inline] + fn bitand_assign(&mut self, rhs: VdoWdrMode) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoWdrMode { - VDO_WDR_MODE_NONE = -1, - VDO_WDR_MODE_LINEAR = 0, - VDO_WDR_MODE_2X = 1, - VDO_WDR_MODE_3X = 2, - VDO_WDR_MODE_4X = 3, - VDO_WDR_MODE_SENSOR = 4, -} -#[repr(i32)] -#[non_exhaustive] +pub struct VdoWdrMode(pub ::std::os::raw::c_int); +impl VdoFormat { + pub const VDO_FORMAT_NONE: VdoFormat = VdoFormat(-1); +} +impl VdoFormat { + pub const VDO_FORMAT_H264: VdoFormat = VdoFormat(0); +} +impl VdoFormat { + pub const VDO_FORMAT_H265: VdoFormat = VdoFormat(1); +} +impl VdoFormat { + pub const VDO_FORMAT_JPEG: VdoFormat = VdoFormat(2); +} +impl VdoFormat { + pub const VDO_FORMAT_YUV: VdoFormat = VdoFormat(3); +} +impl VdoFormat { + pub const VDO_FORMAT_BAYER: VdoFormat = VdoFormat(4); +} +impl VdoFormat { + pub const VDO_FORMAT_IVS: VdoFormat = VdoFormat(5); +} +impl VdoFormat { + pub const VDO_FORMAT_RAW: VdoFormat = VdoFormat(6); +} +impl VdoFormat { + pub const VDO_FORMAT_RGBA: VdoFormat = VdoFormat(7); +} +impl VdoFormat { + pub const VDO_FORMAT_RGB: VdoFormat = VdoFormat(8); +} +impl VdoFormat { + pub const VDO_FORMAT_PLANAR_RGB: VdoFormat = VdoFormat(9); +} +impl VdoFormat { + pub const VDO_FORMAT_AV1: VdoFormat = VdoFormat(10); +} +impl ::std::ops::BitOr for VdoFormat { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoFormat(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoFormat { + #[inline] + fn bitor_assign(&mut self, rhs: VdoFormat) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoFormat { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoFormat(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoFormat { + #[inline] + fn bitand_assign(&mut self, rhs: VdoFormat) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoFormat { - VDO_FORMAT_NONE = -1, - VDO_FORMAT_H264 = 0, - VDO_FORMAT_H265 = 1, - VDO_FORMAT_JPEG = 2, - VDO_FORMAT_YUV = 3, - VDO_FORMAT_BAYER = 4, - VDO_FORMAT_IVS = 5, - VDO_FORMAT_RAW = 6, - VDO_FORMAT_RGBA = 7, - VDO_FORMAT_RGB = 8, - VDO_FORMAT_PLANAR_RGB = 9, -} -#[repr(i32)] -#[non_exhaustive] +pub struct VdoFormat(pub ::std::os::raw::c_int); +impl VdoH264Profile { + pub const VDO_H264_PROFILE_NONE: VdoH264Profile = VdoH264Profile(-1); +} +impl VdoH264Profile { + pub const VDO_H264_PROFILE_BASELINE: VdoH264Profile = VdoH264Profile(0); +} +impl VdoH264Profile { + pub const VDO_H264_PROFILE_MAIN: VdoH264Profile = VdoH264Profile(1); +} +impl VdoH264Profile { + pub const VDO_H264_PROFILE_HIGH: VdoH264Profile = VdoH264Profile(2); +} +impl ::std::ops::BitOr for VdoH264Profile { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoH264Profile(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoH264Profile { + #[inline] + fn bitor_assign(&mut self, rhs: VdoH264Profile) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoH264Profile { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoH264Profile(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoH264Profile { + #[inline] + fn bitand_assign(&mut self, rhs: VdoH264Profile) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoH264Profile { - VDO_H264_PROFILE_NONE = -1, - VDO_H264_PROFILE_BASELINE = 0, - VDO_H264_PROFILE_MAIN = 1, - VDO_H264_PROFILE_HIGH = 2, -} -#[repr(i32)] -#[non_exhaustive] +pub struct VdoH264Profile(pub ::std::os::raw::c_int); +impl VdoH265Profile { + pub const VDO_H265_PROFILE_NONE: VdoH265Profile = VdoH265Profile(-1); +} +impl VdoH265Profile { + pub const VDO_H265_PROFILE_MAIN: VdoH265Profile = VdoH265Profile(0); +} +impl VdoH265Profile { + pub const VDO_H265_PROFILE_MAIN_10: VdoH265Profile = VdoH265Profile(1); +} +impl ::std::ops::BitOr for VdoH265Profile { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoH265Profile(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoH265Profile { + #[inline] + fn bitor_assign(&mut self, rhs: VdoH265Profile) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoH265Profile { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoH265Profile(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoH265Profile { + #[inline] + fn bitand_assign(&mut self, rhs: VdoH265Profile) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoH265Profile { - VDO_H265_PROFILE_NONE = -1, - VDO_H265_PROFILE_MAIN = 0, - VDO_H265_PROFILE_MAIN_10 = 1, +pub struct VdoH265Profile(pub ::std::os::raw::c_int); +impl VdoAV1Profile { + pub const VDO_AV1_PROFILE_NONE: VdoAV1Profile = VdoAV1Profile(-1); +} +impl VdoAV1Profile { + pub const VDO_AV1_PROFILE_MAIN: VdoAV1Profile = VdoAV1Profile(0); +} +impl VdoAV1Profile { + pub const VDO_AV1_PROFILE_MAIN_10: VdoAV1Profile = VdoAV1Profile(1); +} +impl ::std::ops::BitOr for VdoAV1Profile { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoAV1Profile(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoAV1Profile { + #[inline] + fn bitor_assign(&mut self, rhs: VdoAV1Profile) { + self.0 |= rhs.0; + } } -#[repr(i32)] -#[non_exhaustive] +impl ::std::ops::BitAnd for VdoAV1Profile { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoAV1Profile(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoAV1Profile { + #[inline] + fn bitand_assign(&mut self, rhs: VdoAV1Profile) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoRateControlMode { - VDO_RATE_CONTROL_MODE_NONE = -1, - VDO_RATE_CONTROL_MODE_CBR = 0, - VDO_RATE_CONTROL_MODE_VBR = 1, - VDO_RATE_CONTROL_MODE_MBR = 2, - VDO_RATE_CONTROL_MODE_ABR = 3, -} -#[repr(i32)] -#[non_exhaustive] +pub struct VdoAV1Profile(pub ::std::os::raw::c_int); +impl VdoRateControlMode { + pub const VDO_RATE_CONTROL_MODE_NONE: VdoRateControlMode = VdoRateControlMode(-1); +} +impl VdoRateControlMode { + pub const VDO_RATE_CONTROL_MODE_CBR: VdoRateControlMode = VdoRateControlMode(0); +} +impl VdoRateControlMode { + pub const VDO_RATE_CONTROL_MODE_VBR: VdoRateControlMode = VdoRateControlMode(1); +} +impl VdoRateControlMode { + pub const VDO_RATE_CONTROL_MODE_MBR: VdoRateControlMode = VdoRateControlMode(2); +} +impl VdoRateControlMode { + pub const VDO_RATE_CONTROL_MODE_ABR: VdoRateControlMode = VdoRateControlMode(3); +} +impl ::std::ops::BitOr for VdoRateControlMode { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoRateControlMode(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoRateControlMode { + #[inline] + fn bitor_assign(&mut self, rhs: VdoRateControlMode) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoRateControlMode { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoRateControlMode(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoRateControlMode { + #[inline] + fn bitand_assign(&mut self, rhs: VdoRateControlMode) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoRateControlPriority { - VDO_RATE_CONTROL_PRIORITY_NONE = -1, - VDO_RATE_CONTROL_PRIORITY_FRAMERATE = 0, - VDO_RATE_CONTROL_PRIORITY_QUALITY = 1, - VDO_RATE_CONTROL_PRIORITY_FULL_FRAMERATE = 2, -} -#[repr(u32)] -#[non_exhaustive] +pub struct VdoRateControlMode(pub ::std::os::raw::c_int); +impl VdoRateControlPriority { + pub const VDO_RATE_CONTROL_PRIORITY_NONE: VdoRateControlPriority = VdoRateControlPriority(-1); +} +impl VdoRateControlPriority { + pub const VDO_RATE_CONTROL_PRIORITY_FRAMERATE: VdoRateControlPriority = + VdoRateControlPriority(0); +} +impl VdoRateControlPriority { + pub const VDO_RATE_CONTROL_PRIORITY_QUALITY: VdoRateControlPriority = VdoRateControlPriority(1); +} +impl VdoRateControlPriority { + pub const VDO_RATE_CONTROL_PRIORITY_FULL_FRAMERATE: VdoRateControlPriority = + VdoRateControlPriority(2); +} +impl ::std::ops::BitOr for VdoRateControlPriority { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoRateControlPriority(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoRateControlPriority { + #[inline] + fn bitor_assign(&mut self, rhs: VdoRateControlPriority) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoRateControlPriority { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoRateControlPriority(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoRateControlPriority { + #[inline] + fn bitand_assign(&mut self, rhs: VdoRateControlPriority) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoFrameType { - VDO_FRAME_TYPE_NONE = 0, - VDO_FRAME_TYPE_H264_SPS = 1, - VDO_FRAME_TYPE_H264_PPS = 2, - VDO_FRAME_TYPE_H264_SEI = 3, - VDO_FRAME_TYPE_H264_IDR = 4, - VDO_FRAME_TYPE_H264_I = 5, - VDO_FRAME_TYPE_H264_P = 6, - VDO_FRAME_TYPE_H264_B = 7, - VDO_FRAME_TYPE_H265_SPS = 8, - VDO_FRAME_TYPE_H265_PPS = 9, - VDO_FRAME_TYPE_H265_VPS = 10, - VDO_FRAME_TYPE_H265_SEI = 11, - VDO_FRAME_TYPE_H265_IDR = 12, - VDO_FRAME_TYPE_H265_I = 13, - VDO_FRAME_TYPE_H265_P = 14, - VDO_FRAME_TYPE_H265_B = 15, - VDO_FRAME_TYPE_JPEG = 16, - VDO_FRAME_TYPE_YUV = 17, - VDO_FRAME_TYPE_RAW = 18, - VDO_FRAME_TYPE_RGBA = 19, - VDO_FRAME_TYPE_RGB = 20, - VDO_FRAME_TYPE_PLANAR_RGB = 21, -} -#[repr(i32)] -#[non_exhaustive] +pub struct VdoRateControlPriority(pub ::std::os::raw::c_int); +impl VdoFrameType { + pub const VDO_FRAME_TYPE_NONE: VdoFrameType = VdoFrameType(0); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H264_SPS: VdoFrameType = VdoFrameType(1); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H264_PPS: VdoFrameType = VdoFrameType(2); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H264_SEI: VdoFrameType = VdoFrameType(3); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H264_IDR: VdoFrameType = VdoFrameType(4); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H264_I: VdoFrameType = VdoFrameType(5); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H264_P: VdoFrameType = VdoFrameType(6); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H264_B: VdoFrameType = VdoFrameType(7); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H265_SPS: VdoFrameType = VdoFrameType(8); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H265_PPS: VdoFrameType = VdoFrameType(9); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H265_VPS: VdoFrameType = VdoFrameType(10); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H265_SEI: VdoFrameType = VdoFrameType(11); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H265_IDR: VdoFrameType = VdoFrameType(12); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H265_I: VdoFrameType = VdoFrameType(13); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H265_P: VdoFrameType = VdoFrameType(14); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_H265_B: VdoFrameType = VdoFrameType(15); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_JPEG: VdoFrameType = VdoFrameType(16); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_YUV: VdoFrameType = VdoFrameType(17); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_RAW: VdoFrameType = VdoFrameType(18); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_RGBA: VdoFrameType = VdoFrameType(19); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_RGB: VdoFrameType = VdoFrameType(20); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_PLANAR_RGB: VdoFrameType = VdoFrameType(21); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_AV1_KEY: VdoFrameType = VdoFrameType(22); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_AV1_INTER: VdoFrameType = VdoFrameType(23); +} +impl VdoFrameType { + pub const VDO_FRAME_TYPE_AV1_BIDI: VdoFrameType = VdoFrameType(24); +} +impl ::std::ops::BitOr for VdoFrameType { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoFrameType(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoFrameType { + #[inline] + fn bitor_assign(&mut self, rhs: VdoFrameType) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoFrameType { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoFrameType(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoFrameType { + #[inline] + fn bitand_assign(&mut self, rhs: VdoFrameType) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoZipStreamProfile { - VDO_ZIPSTREAM_PROFILE_NONE = -1, - VDO_ZIPSTREAM_PROFILE_CLASSIC = 0, - VDO_ZIPSTREAM_PROFILE_STORAGE = 1, - VDO_ZIPSTREAM_PROFILE_LIVE = 2, -} -#[repr(u32)] -#[non_exhaustive] +pub struct VdoFrameType(pub ::std::os::raw::c_uint); +impl VdoZipStreamProfile { + pub const VDO_ZIPSTREAM_PROFILE_NONE: VdoZipStreamProfile = VdoZipStreamProfile(-1); +} +impl VdoZipStreamProfile { + pub const VDO_ZIPSTREAM_PROFILE_CLASSIC: VdoZipStreamProfile = VdoZipStreamProfile(0); +} +impl VdoZipStreamProfile { + pub const VDO_ZIPSTREAM_PROFILE_STORAGE: VdoZipStreamProfile = VdoZipStreamProfile(1); +} +impl VdoZipStreamProfile { + pub const VDO_ZIPSTREAM_PROFILE_LIVE: VdoZipStreamProfile = VdoZipStreamProfile(2); +} +impl ::std::ops::BitOr for VdoZipStreamProfile { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoZipStreamProfile(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoZipStreamProfile { + #[inline] + fn bitor_assign(&mut self, rhs: VdoZipStreamProfile) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoZipStreamProfile { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoZipStreamProfile(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoZipStreamProfile { + #[inline] + fn bitand_assign(&mut self, rhs: VdoZipStreamProfile) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoChunkType { - VDO_CHUNK_NONE = 0, - VDO_CHUNK_ERROR = 2147483648, +pub struct VdoZipStreamProfile(pub ::std::os::raw::c_int); +impl VdoChunkType { + pub const VDO_CHUNK_NONE: VdoChunkType = VdoChunkType(0); +} +impl VdoChunkType { + pub const VDO_CHUNK_ERROR: VdoChunkType = VdoChunkType(2147483648); +} +impl ::std::ops::BitOr for VdoChunkType { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoChunkType(self.0 | other.0) + } } -#[repr(u32)] -#[non_exhaustive] +impl ::std::ops::BitOrAssign for VdoChunkType { + #[inline] + fn bitor_assign(&mut self, rhs: VdoChunkType) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoChunkType { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoChunkType(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoChunkType { + #[inline] + fn bitand_assign(&mut self, rhs: VdoChunkType) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoChunkOption { - VDO_CHUNK_OPTION_NONE = 0, - VDO_CHUNK_OPTION_MMAP = 2147483648, +pub struct VdoChunkType(pub ::std::os::raw::c_uint); +impl VdoChunkOption { + pub const VDO_CHUNK_OPTION_NONE: VdoChunkOption = VdoChunkOption(0); +} +impl VdoChunkOption { + pub const VDO_CHUNK_OPTION_MMAP: VdoChunkOption = VdoChunkOption(2147483648); +} +impl ::std::ops::BitOr for VdoChunkOption { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoChunkOption(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoChunkOption { + #[inline] + fn bitor_assign(&mut self, rhs: VdoChunkOption) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoChunkOption { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoChunkOption(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoChunkOption { + #[inline] + fn bitand_assign(&mut self, rhs: VdoChunkOption) { + self.0 &= rhs.0; + } } +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct VdoChunkOption(pub ::std::os::raw::c_uint); #[repr(C)] pub struct VdoChunk { pub data: gpointer, @@ -193,98 +614,403 @@ pub struct VdoChunk { pub type_: VdoChunkType, pub offset: gint64, } -#[repr(i32)] -#[non_exhaustive] -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoOverlayAlign { - VDO_OVERLAY_ALIGN_NONE = -1, - VDO_OVERLAY_ALIGN_TOP = 0, - VDO_OVERLAY_ALIGN_BOTTOM = 1, +impl VdoOverlayAlign { + pub const VDO_OVERLAY_ALIGN_NONE: VdoOverlayAlign = VdoOverlayAlign(-1); +} +impl VdoOverlayAlign { + pub const VDO_OVERLAY_ALIGN_TOP: VdoOverlayAlign = VdoOverlayAlign(0); +} +impl VdoOverlayAlign { + pub const VDO_OVERLAY_ALIGN_BOTTOM: VdoOverlayAlign = VdoOverlayAlign(1); +} +impl ::std::ops::BitOr for VdoOverlayAlign { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoOverlayAlign(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoOverlayAlign { + #[inline] + fn bitor_assign(&mut self, rhs: VdoOverlayAlign) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoOverlayAlign { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoOverlayAlign(self.0 & other.0) + } } -#[repr(u32)] -#[non_exhaustive] +impl ::std::ops::BitAndAssign for VdoOverlayAlign { + #[inline] + fn bitand_assign(&mut self, rhs: VdoOverlayAlign) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoOverlayColor { - VDO_OVERLAY_COLOR_TRANSPARENT = 0, - VDO_OVERLAY_COLOR_BLACK = 61440, - VDO_OVERLAY_COLOR_WHITE = 65535, +pub struct VdoOverlayAlign(pub ::std::os::raw::c_int); +impl VdoOverlayColor { + pub const VDO_OVERLAY_COLOR_TRANSPARENT: VdoOverlayColor = VdoOverlayColor(0); +} +impl VdoOverlayColor { + pub const VDO_OVERLAY_COLOR_BLACK: VdoOverlayColor = VdoOverlayColor(61440); +} +impl VdoOverlayColor { + pub const VDO_OVERLAY_COLOR_WHITE: VdoOverlayColor = VdoOverlayColor(65535); +} +impl ::std::ops::BitOr for VdoOverlayColor { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoOverlayColor(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoOverlayColor { + #[inline] + fn bitor_assign(&mut self, rhs: VdoOverlayColor) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoOverlayColor { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoOverlayColor(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoOverlayColor { + #[inline] + fn bitand_assign(&mut self, rhs: VdoOverlayColor) { + self.0 &= rhs.0; + } } -#[repr(u32)] -#[non_exhaustive] +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoOverlayTextSize { - VDO_OVERLAY_TEXT_SIZE_SMALL = 16, - VDO_OVERLAY_TEXT_SIZE_MEDIUM = 32, - VDO_OVERLAY_TEXT_SIZE_LARGE = 48, +pub struct VdoOverlayColor(pub ::std::os::raw::c_uint); +impl VdoOverlayTextSize { + pub const VDO_OVERLAY_TEXT_SIZE_SMALL: VdoOverlayTextSize = VdoOverlayTextSize(16); +} +impl VdoOverlayTextSize { + pub const VDO_OVERLAY_TEXT_SIZE_MEDIUM: VdoOverlayTextSize = VdoOverlayTextSize(32); } -#[repr(u32)] -#[non_exhaustive] +impl VdoOverlayTextSize { + pub const VDO_OVERLAY_TEXT_SIZE_LARGE: VdoOverlayTextSize = VdoOverlayTextSize(48); +} +impl ::std::ops::BitOr for VdoOverlayTextSize { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoOverlayTextSize(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoOverlayTextSize { + #[inline] + fn bitor_assign(&mut self, rhs: VdoOverlayTextSize) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoOverlayTextSize { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoOverlayTextSize(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoOverlayTextSize { + #[inline] + fn bitand_assign(&mut self, rhs: VdoOverlayTextSize) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoStreamTimestamp { - VDO_TIMESTAMP_NONE = 0, - VDO_TIMESTAMP_UTC = 1, - VDO_TIMESTAMP_ZIPSTREAM = 2, - VDO_TIMESTAMP_DIFF = 4, - VDO_TIMESTAMP_MONO_CAPTURE = 8, - VDO_TIMESTAMP_MONO_SERVER = 16, - VDO_TIMESTAMP_MONO_CLIENT = 32, - VDO_TIMESTAMP_MONO_CLIENT_SERVER_DIFF = 52, - VDO_TIMESTAMP_MONO_CLIENT_CAPTURE_DIFF = 44, -} -#[repr(u32)] -#[non_exhaustive] +pub struct VdoOverlayTextSize(pub ::std::os::raw::c_uint); +impl VdoStreamTimestamp { + pub const VDO_TIMESTAMP_NONE: VdoStreamTimestamp = VdoStreamTimestamp(0); +} +impl VdoStreamTimestamp { + pub const VDO_TIMESTAMP_UTC: VdoStreamTimestamp = VdoStreamTimestamp(1); +} +impl VdoStreamTimestamp { + pub const VDO_TIMESTAMP_ZIPSTREAM: VdoStreamTimestamp = VdoStreamTimestamp(2); +} +impl VdoStreamTimestamp { + pub const VDO_TIMESTAMP_DIFF: VdoStreamTimestamp = VdoStreamTimestamp(4); +} +impl VdoStreamTimestamp { + pub const VDO_TIMESTAMP_MONO_CAPTURE: VdoStreamTimestamp = VdoStreamTimestamp(8); +} +impl VdoStreamTimestamp { + pub const VDO_TIMESTAMP_MONO_SERVER: VdoStreamTimestamp = VdoStreamTimestamp(16); +} +impl VdoStreamTimestamp { + pub const VDO_TIMESTAMP_MONO_CLIENT: VdoStreamTimestamp = VdoStreamTimestamp(32); +} +impl VdoStreamTimestamp { + pub const VDO_TIMESTAMP_MONO_CLIENT_SERVER_DIFF: VdoStreamTimestamp = VdoStreamTimestamp(52); +} +impl VdoStreamTimestamp { + pub const VDO_TIMESTAMP_MONO_CLIENT_CAPTURE_DIFF: VdoStreamTimestamp = VdoStreamTimestamp(44); +} +impl ::std::ops::BitOr for VdoStreamTimestamp { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoStreamTimestamp(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoStreamTimestamp { + #[inline] + fn bitor_assign(&mut self, rhs: VdoStreamTimestamp) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoStreamTimestamp { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoStreamTimestamp(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoStreamTimestamp { + #[inline] + fn bitand_assign(&mut self, rhs: VdoStreamTimestamp) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoIntent { - VDO_INTENT_NONE = 0, - VDO_INTENT_CONTROL = 1, - VDO_INTENT_MONITOR = 2, - VDO_INTENT_CONSUME = 4, - VDO_INTENT_PRODUCE = 8, - VDO_INTENT_DEFAULT = 5, - VDO_INTENT_EVENTFD = 16, - VDO_INTENT_UNIVERSE = 4294967295, -} -#[repr(u32)] -#[non_exhaustive] +pub struct VdoStreamTimestamp(pub ::std::os::raw::c_uint); +impl VdoIntent { + pub const VDO_INTENT_NONE: VdoIntent = VdoIntent(0); +} +impl VdoIntent { + pub const VDO_INTENT_CONTROL: VdoIntent = VdoIntent(1); +} +impl VdoIntent { + pub const VDO_INTENT_MONITOR: VdoIntent = VdoIntent(2); +} +impl VdoIntent { + pub const VDO_INTENT_CONSUME: VdoIntent = VdoIntent(4); +} +impl VdoIntent { + pub const VDO_INTENT_PRODUCE: VdoIntent = VdoIntent(8); +} +impl VdoIntent { + pub const VDO_INTENT_DEFAULT: VdoIntent = VdoIntent(5); +} +impl VdoIntent { + pub const VDO_INTENT_EVENTFD: VdoIntent = VdoIntent(16); +} +impl VdoIntent { + pub const VDO_INTENT_UNIVERSE: VdoIntent = VdoIntent(4294967295); +} +impl ::std::ops::BitOr for VdoIntent { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoIntent(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoIntent { + #[inline] + fn bitor_assign(&mut self, rhs: VdoIntent) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoIntent { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoIntent(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoIntent { + #[inline] + fn bitand_assign(&mut self, rhs: VdoIntent) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoStreamEvent { - VDO_STREAM_EVENT_NONE = 0, - VDO_STREAM_EVENT_STARTED = 1, - VDO_STREAM_EVENT_STOPPED = 2, - VDO_STREAM_EVENT_RESOURCE = 16, - VDO_STREAM_EVENT_QUOTA_SOFT = 17, - VDO_STREAM_EVENT_QUOTA_HARD = 18, - VDO_STREAM_EVENT_ZIPSTREAM = 32, - VDO_STREAM_EVENT_BUFFERING = 64, - VDO_STREAM_EVENT_BUFFERING_WARN = 65, - VDO_STREAM_EVENT_BUFFERING_FAIL = 66, - VDO_STREAM_EVENT_INVALID = 4294967295, -} -#[repr(u32)] -#[non_exhaustive] +pub struct VdoIntent(pub ::std::os::raw::c_uint); +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_NONE: VdoStreamEvent = VdoStreamEvent(0); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_STARTED: VdoStreamEvent = VdoStreamEvent(1); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_STOPPED: VdoStreamEvent = VdoStreamEvent(2); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_RESOURCE: VdoStreamEvent = VdoStreamEvent(16); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_QUOTA_SOFT: VdoStreamEvent = VdoStreamEvent(17); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_QUOTA_HARD: VdoStreamEvent = VdoStreamEvent(18); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_ZIPSTREAM: VdoStreamEvent = VdoStreamEvent(32); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_BUFFERING: VdoStreamEvent = VdoStreamEvent(64); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_BUFFERING_WARN: VdoStreamEvent = VdoStreamEvent(65); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_BUFFERING_FAIL: VdoStreamEvent = VdoStreamEvent(66); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_CREATED: VdoStreamEvent = VdoStreamEvent(80); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_CLOSED: VdoStreamEvent = VdoStreamEvent(81); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_EXISTING: VdoStreamEvent = VdoStreamEvent(82); +} +impl VdoStreamEvent { + pub const VDO_STREAM_EVENT_INVALID: VdoStreamEvent = VdoStreamEvent(4294967295); +} +impl ::std::ops::BitOr for VdoStreamEvent { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoStreamEvent(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoStreamEvent { + #[inline] + fn bitor_assign(&mut self, rhs: VdoStreamEvent) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoStreamEvent { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoStreamEvent(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoStreamEvent { + #[inline] + fn bitand_assign(&mut self, rhs: VdoStreamEvent) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoBufferAccess { - VDO_BUFFER_ACCESS_NONE = 0, - VDO_BUFFER_ACCESS_CPU_RD = 1, - VDO_BUFFER_ACCESS_DEV_RD = 2, - VDO_BUFFER_ACCESS_ANY_RD = 3, - VDO_BUFFER_ACCESS_CPU_WR = 256, - VDO_BUFFER_ACCESS_DEV_WR = 512, - VDO_BUFFER_ACCESS_ANY_WR = 768, - VDO_BUFFER_ACCESS_CPU_RW = 257, - VDO_BUFFER_ACCESS_DEV_RW = 514, - VDO_BUFFER_ACCESS_ANY_RW = 771, -} -#[repr(u32)] -#[non_exhaustive] +pub struct VdoStreamEvent(pub ::std::os::raw::c_uint); +impl VdoBufferAccess { + pub const VDO_BUFFER_ACCESS_NONE: VdoBufferAccess = VdoBufferAccess(0); +} +impl VdoBufferAccess { + pub const VDO_BUFFER_ACCESS_CPU_RD: VdoBufferAccess = VdoBufferAccess(1); +} +impl VdoBufferAccess { + pub const VDO_BUFFER_ACCESS_DEV_RD: VdoBufferAccess = VdoBufferAccess(2); +} +impl VdoBufferAccess { + pub const VDO_BUFFER_ACCESS_ANY_RD: VdoBufferAccess = VdoBufferAccess(3); +} +impl VdoBufferAccess { + pub const VDO_BUFFER_ACCESS_CPU_WR: VdoBufferAccess = VdoBufferAccess(256); +} +impl VdoBufferAccess { + pub const VDO_BUFFER_ACCESS_DEV_WR: VdoBufferAccess = VdoBufferAccess(512); +} +impl VdoBufferAccess { + pub const VDO_BUFFER_ACCESS_ANY_WR: VdoBufferAccess = VdoBufferAccess(768); +} +impl VdoBufferAccess { + pub const VDO_BUFFER_ACCESS_CPU_RW: VdoBufferAccess = VdoBufferAccess(257); +} +impl VdoBufferAccess { + pub const VDO_BUFFER_ACCESS_DEV_RW: VdoBufferAccess = VdoBufferAccess(514); +} +impl VdoBufferAccess { + pub const VDO_BUFFER_ACCESS_ANY_RW: VdoBufferAccess = VdoBufferAccess(771); +} +impl ::std::ops::BitOr for VdoBufferAccess { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoBufferAccess(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoBufferAccess { + #[inline] + fn bitor_assign(&mut self, rhs: VdoBufferAccess) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for VdoBufferAccess { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoBufferAccess(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoBufferAccess { + #[inline] + fn bitand_assign(&mut self, rhs: VdoBufferAccess) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum VdoBufferStrategy { - VDO_BUFFER_STRATEGY_NONE = 0, - VDO_BUFFER_STRATEGY_INPUT = 1, - VDO_BUFFER_STRATEGY_EXTERNAL = 2, - VDO_BUFFER_STRATEGY_EXPLICIT = 3, - VDO_BUFFER_STRATEGY_INFINITE = 4, +pub struct VdoBufferAccess(pub ::std::os::raw::c_uint); +impl VdoBufferStrategy { + pub const VDO_BUFFER_STRATEGY_NONE: VdoBufferStrategy = VdoBufferStrategy(0); +} +impl VdoBufferStrategy { + pub const VDO_BUFFER_STRATEGY_INPUT: VdoBufferStrategy = VdoBufferStrategy(1); +} +impl VdoBufferStrategy { + pub const VDO_BUFFER_STRATEGY_EXTERNAL: VdoBufferStrategy = VdoBufferStrategy(2); +} +impl VdoBufferStrategy { + pub const VDO_BUFFER_STRATEGY_EXPLICIT: VdoBufferStrategy = VdoBufferStrategy(3); +} +impl VdoBufferStrategy { + pub const VDO_BUFFER_STRATEGY_INFINITE: VdoBufferStrategy = VdoBufferStrategy(4); +} +impl ::std::ops::BitOr for VdoBufferStrategy { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + VdoBufferStrategy(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for VdoBufferStrategy { + #[inline] + fn bitor_assign(&mut self, rhs: VdoBufferStrategy) { + self.0 |= rhs.0; + } } +impl ::std::ops::BitAnd for VdoBufferStrategy { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + VdoBufferStrategy(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for VdoBufferStrategy { + #[inline] + fn bitand_assign(&mut self, rhs: VdoBufferStrategy) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct VdoBufferStrategy(pub ::std::os::raw::c_uint); #[repr(C)] pub struct VdoMemChunk { pub data: gpointer, @@ -335,6 +1061,7 @@ pub struct VdoPair32u { pub __bindgen_anon_1: __BindgenUnionField, pub __bindgen_anon_2: __BindgenUnionField, pub __bindgen_anon_3: __BindgenUnionField, + pub res: __BindgenUnionField, pub val: __BindgenUnionField<[guint32; 2usize]>, pub bindgen_union_field: [u32; 2usize], } @@ -365,6 +1092,9 @@ extern "C" { extern "C" { pub fn vdo_h265_profile_get_type() -> GType; } +extern "C" { + pub fn vdo_av1_profile_get_type() -> GType; +} extern "C" { pub fn vdo_zipstream_profile_get_type() -> GType; } @@ -498,7 +1228,37 @@ extern "C" { pub fn vdo_map_to_variant(self_: *const VdoMap) -> *mut GVariant; } extern "C" { - pub fn vdo_map_dump(self_: *const VdoMap); + pub fn vdo_map_dump(self_: *const VdoMap); +} +extern "C" { + pub fn vdo_map_get_byte(self_: *const VdoMap, name: *const gchar, def: guchar) -> guchar; +} +extern "C" { + pub fn vdo_map_get_int16(self_: *const VdoMap, name: *const gchar, def: gint16) -> gint16; +} +extern "C" { + pub fn vdo_map_get_uint16(self_: *const VdoMap, name: *const gchar, def: guint16) -> guint16; +} +extern "C" { + pub fn vdo_map_get_uint32x2( + self_: *const VdoMap, + name: *const gchar, + def: *mut guint32, + ) -> *mut guint32; +} +extern "C" { + pub fn vdo_map_get_uint32x4( + self_: *const VdoMap, + name: *const gchar, + def: *mut guint32, + ) -> *mut guint32; +} +extern "C" { + pub fn vdo_map_get_doublex4( + self_: *const VdoMap, + name: *const gchar, + def: *mut gdouble, + ) -> *mut gdouble; } extern "C" { pub fn vdo_map_get_variant( @@ -511,15 +1271,6 @@ extern "C" { pub fn vdo_map_get_boolean(self_: *const VdoMap, name: *const gchar, def: gboolean) -> gboolean; } -extern "C" { - pub fn vdo_map_get_byte(self_: *const VdoMap, name: *const gchar, def: guchar) -> guchar; -} -extern "C" { - pub fn vdo_map_get_int16(self_: *const VdoMap, name: *const gchar, def: gint16) -> gint16; -} -extern "C" { - pub fn vdo_map_get_uint16(self_: *const VdoMap, name: *const gchar, def: guint16) -> guint16; -} extern "C" { pub fn vdo_map_get_int32(self_: *const VdoMap, name: *const gchar, def: gint32) -> gint32; } @@ -550,27 +1301,6 @@ extern "C" { def: *const gchar, ) -> *mut gchar; } -extern "C" { - pub fn vdo_map_get_uint32x2( - self_: *const VdoMap, - name: *const gchar, - def: *mut guint32, - ) -> *mut guint32; -} -extern "C" { - pub fn vdo_map_get_uint32x4( - self_: *const VdoMap, - name: *const gchar, - def: *mut guint32, - ) -> *mut guint32; -} -extern "C" { - pub fn vdo_map_get_doublex4( - self_: *const VdoMap, - name: *const gchar, - def: *mut gdouble, - ) -> *mut gdouble; -} extern "C" { pub fn vdo_map_get_pair32i( self_: *const VdoMap, @@ -585,9 +1315,6 @@ extern "C" { def: VdoPair32u, ) -> VdoPair32u; } -extern "C" { - pub fn vdo_map_set_boolean(self_: *mut VdoMap, name: *const gchar, value: gboolean); -} extern "C" { pub fn vdo_map_set_byte(self_: *mut VdoMap, name: *const gchar, value: guchar); } @@ -597,24 +1324,6 @@ extern "C" { extern "C" { pub fn vdo_map_set_uint16(self_: *mut VdoMap, name: *const gchar, value: guint16); } -extern "C" { - pub fn vdo_map_set_int32(self_: *mut VdoMap, name: *const gchar, value: gint32); -} -extern "C" { - pub fn vdo_map_set_uint32(self_: *mut VdoMap, name: *const gchar, value: guint32); -} -extern "C" { - pub fn vdo_map_set_int64(self_: *mut VdoMap, name: *const gchar, value: gint64); -} -extern "C" { - pub fn vdo_map_set_uint64(self_: *mut VdoMap, name: *const gchar, value: guint64); -} -extern "C" { - pub fn vdo_map_set_double(self_: *mut VdoMap, name: *const gchar, value: gdouble); -} -extern "C" { - pub fn vdo_map_set_string(self_: *mut VdoMap, name: *const gchar, value: *const gchar); -} extern "C" { pub fn vdo_map_set_uint32x2(self_: *mut VdoMap, name: *const gchar, value: *const guint32); } @@ -625,102 +1334,31 @@ extern "C" { pub fn vdo_map_set_doublex4(self_: *mut VdoMap, name: *const gchar, value: *const gdouble); } extern "C" { - pub fn vdo_map_set_pair32i(self_: *mut VdoMap, name: *const gchar, value: VdoPair32i); -} -extern "C" { - pub fn vdo_map_set_pair32u(self_: *mut VdoMap, name: *const gchar, value: VdoPair32u); -} -extern "C" { - pub fn vdo_frame_get_type() -> GType; -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _VdoFrame { - _unused: [u8; 0], -} -pub type VdoFrame = _VdoFrame; -#[repr(C)] -pub struct VdoFrameClass { - pub parent_class: GObjectClass, -} -pub type VdoFrame_autoptr = *mut VdoFrame; -pub type VdoFrame_listautoptr = *mut GList; -pub type VdoFrame_slistautoptr = *mut GSList; -pub type VdoFrame_queueautoptr = *mut GQueue; -pub type VdoFrameClass_autoptr = *mut VdoFrameClass; -pub type VdoFrameClass_listautoptr = *mut GList; -pub type VdoFrameClass_slistautoptr = *mut GSList; -pub type VdoFrameClass_queueautoptr = *mut GQueue; -pub type VdoFrameFinalizer = ::std::option::Option; -extern "C" { - pub fn vdo_frame_get_frame_type(self_: *mut VdoFrame) -> VdoFrameType; -} -extern "C" { - pub fn vdo_frame_get_sequence_nbr(self_: *mut VdoFrame) -> guint; -} -extern "C" { - pub fn vdo_frame_get_timestamp(self_: *mut VdoFrame) -> guint64; -} -extern "C" { - pub fn vdo_frame_get_custom_timestamp(self_: *mut VdoFrame) -> gint64; -} -extern "C" { - pub fn vdo_frame_get_size(self_: *mut VdoFrame) -> gsize; -} -extern "C" { - pub fn vdo_frame_get_header_size(self_: *mut VdoFrame) -> gssize; -} -extern "C" { - pub fn vdo_frame_get_fd(self_: *mut VdoFrame) -> gint; -} -extern "C" { - pub fn vdo_frame_get_extra_info(self_: *mut VdoFrame) -> *mut VdoMap; -} -extern "C" { - pub fn vdo_frame_get_opaque(self_: *mut VdoFrame) -> gpointer; -} -extern "C" { - pub fn vdo_frame_get_is_last_buffer(self_: *mut VdoFrame) -> gboolean; -} -extern "C" { - pub fn vdo_frame_set_size(self_: *mut VdoFrame, size: gsize); -} -extern "C" { - pub fn vdo_frame_set_frame_type(self_: *mut VdoFrame, type_: VdoFrameType); -} -extern "C" { - pub fn vdo_frame_set_sequence_nbr(self_: *mut VdoFrame, seqnum: guint); -} -extern "C" { - pub fn vdo_frame_set_timestamp(self_: *mut VdoFrame, timestamp: guint64); + pub fn vdo_map_set_boolean(self_: *mut VdoMap, name: *const gchar, value: gboolean); } extern "C" { - pub fn vdo_frame_set_custom_timestamp(self_: *mut VdoFrame, timestamp: gint64); + pub fn vdo_map_set_int32(self_: *mut VdoMap, name: *const gchar, value: gint32); } extern "C" { - pub fn vdo_frame_set_is_last_buffer(self_: *mut VdoFrame, is_last_buffer: gboolean); + pub fn vdo_map_set_uint32(self_: *mut VdoMap, name: *const gchar, value: guint32); } extern "C" { - pub fn vdo_frame_set_extra_info(self_: *mut VdoFrame, extra_info: *mut VdoMap); + pub fn vdo_map_set_int64(self_: *mut VdoMap, name: *const gchar, value: gint64); } extern "C" { - pub fn vdo_frame_set_header_size(self_: *mut VdoFrame, size: gssize); + pub fn vdo_map_set_uint64(self_: *mut VdoMap, name: *const gchar, value: guint64); } extern "C" { - pub fn vdo_frame_memmap(self_: *mut VdoFrame) -> gpointer; + pub fn vdo_map_set_double(self_: *mut VdoMap, name: *const gchar, value: gdouble); } extern "C" { - pub fn vdo_frame_unmap(self_: *mut VdoFrame); + pub fn vdo_map_set_string(self_: *mut VdoMap, name: *const gchar, value: *const gchar); } extern "C" { - pub fn vdo_frame_take_chunk(self_: *mut VdoFrame, error: *mut *mut GError) -> VdoChunk; + pub fn vdo_map_set_pair32i(self_: *mut VdoMap, name: *const gchar, value: VdoPair32i); } extern "C" { - pub fn vdo_frame_take_chunk_ex( - self_: *mut VdoFrame, - options: VdoChunkOption, - error: *mut *mut GError, - ) -> VdoChunk; + pub fn vdo_map_set_pair32u(self_: *mut VdoMap, name: *const gchar, value: VdoPair32u); } extern "C" { pub fn vdo_channel_get_type() -> GType; @@ -746,6 +1384,9 @@ pub type VdoChannelClass_queueautoptr = *mut GQueue; extern "C" { pub fn vdo_channel_get(channel_nbr: guint, error: *mut *mut GError) -> *mut VdoChannel; } +extern "C" { + pub fn vdo_channel_get_ex(desc: *mut VdoMap, error: *mut *mut GError) -> *mut VdoChannel; +} extern "C" { pub fn vdo_channel_get_all(error: *mut *mut GError) -> *mut GList; } @@ -818,15 +1459,102 @@ extern "C" { ) -> gboolean; } extern "C" { - pub fn vdo_channel_new( - id: guint, - resolution_set: *mut VdoResolutionSet, - info: *mut VdoMap, - error: *mut *mut GError, - ) -> *mut VdoChannel; + pub fn vdo_frame_get_type() -> GType; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _VdoFrame { + _unused: [u8; 0], +} +pub type VdoFrame = _VdoFrame; +#[repr(C)] +pub struct VdoFrameClass { + pub parent_class: GObjectClass, +} +pub type VdoFrame_autoptr = *mut VdoFrame; +pub type VdoFrame_listautoptr = *mut GList; +pub type VdoFrame_slistautoptr = *mut GSList; +pub type VdoFrame_queueautoptr = *mut GQueue; +pub type VdoFrameClass_autoptr = *mut VdoFrameClass; +pub type VdoFrameClass_listautoptr = *mut GList; +pub type VdoFrameClass_slistautoptr = *mut GSList; +pub type VdoFrameClass_queueautoptr = *mut GQueue; +pub type VdoFrameFinalizer = ::std::option::Option; +extern "C" { + pub fn vdo_frame_get_frame_type(self_: *mut VdoFrame) -> VdoFrameType; +} +extern "C" { + pub fn vdo_frame_is_key(self_: *mut VdoFrame) -> gboolean; +} +extern "C" { + pub fn vdo_frame_shown(self_: *mut VdoFrame) -> gboolean; +} +extern "C" { + pub fn vdo_frame_get_sequence_nbr(self_: *mut VdoFrame) -> guint; +} +extern "C" { + pub fn vdo_frame_get_timestamp(self_: *mut VdoFrame) -> guint64; +} +extern "C" { + pub fn vdo_frame_get_custom_timestamp(self_: *mut VdoFrame) -> gint64; +} +extern "C" { + pub fn vdo_frame_get_size(self_: *mut VdoFrame) -> gsize; +} +extern "C" { + pub fn vdo_frame_get_header_size(self_: *mut VdoFrame) -> gssize; +} +extern "C" { + pub fn vdo_frame_get_fd(self_: *mut VdoFrame) -> gint; +} +extern "C" { + pub fn vdo_frame_get_extra_info(self_: *mut VdoFrame) -> *mut VdoMap; +} +extern "C" { + pub fn vdo_frame_get_opaque(self_: *mut VdoFrame) -> gpointer; +} +extern "C" { + pub fn vdo_frame_get_is_last_buffer(self_: *mut VdoFrame) -> gboolean; +} +extern "C" { + pub fn vdo_frame_set_size(self_: *mut VdoFrame, size: gsize); +} +extern "C" { + pub fn vdo_frame_set_frame_type(self_: *mut VdoFrame, type_: VdoFrameType); +} +extern "C" { + pub fn vdo_frame_set_sequence_nbr(self_: *mut VdoFrame, seqnum: guint); +} +extern "C" { + pub fn vdo_frame_set_timestamp(self_: *mut VdoFrame, timestamp: guint64); +} +extern "C" { + pub fn vdo_frame_set_custom_timestamp(self_: *mut VdoFrame, timestamp: gint64); +} +extern "C" { + pub fn vdo_frame_set_is_last_buffer(self_: *mut VdoFrame, is_last_buffer: gboolean); +} +extern "C" { + pub fn vdo_frame_set_extra_info(self_: *mut VdoFrame, extra_info: *mut VdoMap); } extern "C" { - pub fn vdo_channel_destroy(self_: *mut VdoChannel, error: *mut *mut GError) -> gboolean; + pub fn vdo_frame_set_header_size(self_: *mut VdoFrame, size: gssize); +} +extern "C" { + pub fn vdo_frame_memmap(self_: *mut VdoFrame) -> gpointer; +} +extern "C" { + pub fn vdo_frame_unmap(self_: *mut VdoFrame); +} +extern "C" { + pub fn vdo_frame_take_chunk(self_: *mut VdoFrame, error: *mut *mut GError) -> VdoChunk; +} +extern "C" { + pub fn vdo_frame_take_chunk_ex( + self_: *mut VdoFrame, + options: VdoChunkOption, + error: *mut *mut GError, + ) -> VdoChunk; } pub type VdoBuffer = VdoFrame; pub type VdoBuffer_autoptr = *mut VdoBuffer; @@ -1007,54 +1735,62 @@ extern "C" { extern "C" { pub fn vdo_stream_get_event(self_: *mut VdoStream, error: *mut *mut GError) -> *mut VdoMap; } -pub const VDO_ERROR_NOT_FOUND: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NOT_FOUND; -pub const VDO_ERROR_EXISTS: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_EXISTS; -pub const VDO_ERROR_INVALID_ARGUMENT: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_INVALID_ARGUMENT; -pub const VDO_ERROR_PERMISSION_DENIED: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_PERMISSION_DENIED; -pub const VDO_ERROR_NOT_SUPPORTED: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NOT_SUPPORTED; -pub const VDO_ERROR_CLOSED: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_CLOSED; -pub const VDO_ERROR_BUSY: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_BUSY; -pub const VDO_ERROR_IO: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_IO; -pub const VDO_ERROR_HAL: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_HAL; -pub const VDO_ERROR_DBUS: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_DBUS; -pub const VDO_ERROR_OOM: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_OOM; -pub const VDO_ERROR_IDLE: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_IDLE; -pub const VDO_ERROR_NO_DATA: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NO_DATA; -pub const VDO_ERROR_NO_BUFFER_SPACE: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NO_BUFFER_SPACE; -pub const VDO_ERROR_BUFFER_FAILURE: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_BUFFER_FAILURE; -pub const VDO_ERROR_INTERFACE_DOWN: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_INTERFACE_DOWN; -pub const VDO_ERROR_FAILED: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_FAILED; -pub const VDO_ERROR_FATAL: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_FATAL; -pub const VDO_ERROR_NOT_CONTROLLED: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NOT_CONTROLLED; -pub const VDO_ERROR_NO_EVENT: _bindgen_ty_24 = _bindgen_ty_24::VDO_ERROR_NO_EVENT; -#[repr(u32)] -#[non_exhaustive] -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum _bindgen_ty_24 { - VDO_ERROR_NOT_FOUND = 1, - VDO_ERROR_EXISTS = 2, - VDO_ERROR_INVALID_ARGUMENT = 3, - VDO_ERROR_PERMISSION_DENIED = 4, - VDO_ERROR_NOT_SUPPORTED = 5, - VDO_ERROR_CLOSED = 6, - VDO_ERROR_BUSY = 7, - VDO_ERROR_IO = 8, - VDO_ERROR_HAL = 9, - VDO_ERROR_DBUS = 10, - VDO_ERROR_OOM = 11, - VDO_ERROR_IDLE = 12, - VDO_ERROR_NO_DATA = 13, - VDO_ERROR_NO_BUFFER_SPACE = 14, - VDO_ERROR_BUFFER_FAILURE = 15, - VDO_ERROR_INTERFACE_DOWN = 16, - VDO_ERROR_FAILED = 17, - VDO_ERROR_FATAL = 18, - VDO_ERROR_NOT_CONTROLLED = 19, - VDO_ERROR_NO_EVENT = 20, +pub const VDO_ERROR_NOT_FOUND: _bindgen_ty_24 = _bindgen_ty_24(1); +pub const VDO_ERROR_EXISTS: _bindgen_ty_24 = _bindgen_ty_24(2); +pub const VDO_ERROR_INVALID_ARGUMENT: _bindgen_ty_24 = _bindgen_ty_24(3); +pub const VDO_ERROR_PERMISSION_DENIED: _bindgen_ty_24 = _bindgen_ty_24(4); +pub const VDO_ERROR_NOT_SUPPORTED: _bindgen_ty_24 = _bindgen_ty_24(5); +pub const VDO_ERROR_CLOSED: _bindgen_ty_24 = _bindgen_ty_24(6); +pub const VDO_ERROR_BUSY: _bindgen_ty_24 = _bindgen_ty_24(7); +pub const VDO_ERROR_IO: _bindgen_ty_24 = _bindgen_ty_24(8); +pub const VDO_ERROR_HAL: _bindgen_ty_24 = _bindgen_ty_24(9); +pub const VDO_ERROR_DBUS: _bindgen_ty_24 = _bindgen_ty_24(10); +pub const VDO_ERROR_OOM: _bindgen_ty_24 = _bindgen_ty_24(11); +pub const VDO_ERROR_IDLE: _bindgen_ty_24 = _bindgen_ty_24(12); +pub const VDO_ERROR_NO_DATA: _bindgen_ty_24 = _bindgen_ty_24(13); +pub const VDO_ERROR_NO_BUFFER_SPACE: _bindgen_ty_24 = _bindgen_ty_24(14); +pub const VDO_ERROR_BUFFER_FAILURE: _bindgen_ty_24 = _bindgen_ty_24(15); +pub const VDO_ERROR_INTERFACE_DOWN: _bindgen_ty_24 = _bindgen_ty_24(16); +pub const VDO_ERROR_FAILED: _bindgen_ty_24 = _bindgen_ty_24(17); +pub const VDO_ERROR_FATAL: _bindgen_ty_24 = _bindgen_ty_24(18); +pub const VDO_ERROR_NOT_CONTROLLED: _bindgen_ty_24 = _bindgen_ty_24(19); +pub const VDO_ERROR_NO_EVENT: _bindgen_ty_24 = _bindgen_ty_24(20); +pub const VDO_ERROR_NO_VIDEO: _bindgen_ty_24 = _bindgen_ty_24(21); +impl ::std::ops::BitOr<_bindgen_ty_24> for _bindgen_ty_24 { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + _bindgen_ty_24(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for _bindgen_ty_24 { + #[inline] + fn bitor_assign(&mut self, rhs: _bindgen_ty_24) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd<_bindgen_ty_24> for _bindgen_ty_24 { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + _bindgen_ty_24(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for _bindgen_ty_24 { + #[inline] + fn bitand_assign(&mut self, rhs: _bindgen_ty_24) { + self.0 &= rhs.0; + } } +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct _bindgen_ty_24(pub ::std::os::raw::c_uint); extern "C" { pub fn vdo_error_quark() -> GQuark; } extern "C" { pub fn vdo_error_is_expected(error: *mut *mut GError) -> gboolean; } +extern "C" { + pub fn vdo_error_is_resource_limitation(error: *mut *mut GError) -> gboolean; +}