Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2326701
feat(vdo): Add safe Rust bindings for VDO API
vsem-azamat Dec 28, 2025
864abce
Merge remote-tracking branch 'upstream/main' into HEAD
apljungquist Feb 12, 2026
2ca25d9
fix(vdo): Address all PR #223 review feedback
vsem-azamat Feb 10, 2026
05638be
refactor(vdo): Replace glob imports with qualified paths and add expe…
vsem-azamat Feb 21, 2026
b89acc0
fix(vdo): Address code review and security audit findings
vsem-azamat Feb 21, 2026
87a708d
style(vdo): Apply rustfmt
vsem-azamat Feb 21, 2026
9f98b9e
fix(vdo): Fix soundness hole in file_descriptor() API
vsem-azamat Mar 1, 2026
76ed6e4
docs(vdo): Separate Safety and Panics sections in unsafe fn docs
vsem-azamat Mar 1, 2026
b616613
fix(vdo): Rewrite Send safety comments without GLib assumption
vsem-azamat Mar 1, 2026
e5bdfa9
refactor(vdo): Remove redundant RunningStream::stop() method
vsem-azamat Mar 1, 2026
adb1eb9
refactor(vdo): Use NonNull::new instead of assert + new_unchecked
vsem-azamat Mar 1, 2026
6a5579c
fix(vdo): Match error codes as i32 to avoid wrapping negative GError …
vsem-azamat Mar 1, 2026
6274dae
fix(vdo): Use == GFALSE for gboolean checks instead of != GTRUE
vsem-azamat Mar 1, 2026
e8500d3
fix(vdo): Track stream started state to avoid stopping unstarted streams
vsem-azamat Mar 1, 2026
a617cd7
fix(vdo): Fix buffer leak in StreamBuffer::unref() on error
vsem-azamat Mar 1, 2026
756af0d
fix(vdo): Log StreamBuffer Drop failures even without GError
vsem-azamat Mar 1, 2026
35ae417
refactor(vdo): Minor code review cleanups
vsem-azamat Mar 2, 2026
a6ba015
fix(vdo): Use correct types for VdoFormat and fix Send safety comments
vsem-azamat Mar 2, 2026
824eeff
fix(vdo): Revert format to set_u32 - VDO API expects uint32
vsem-azamat Mar 2, 2026
f4337d8
f: consistent cfg
apljungquist Mar 7, 2026
8618568
Proper tests module and cfg
apljungquist Mar 7, 2026
fdf2314
update checksums
apljungquist Mar 7, 2026
52edcd3
fix `make check_docs`
apljungquist Mar 7, 2026
5f9a469
don't attempt to run vdo tests on host
apljungquist Mar 8, 2026
73cf845
Merge branch 'main' into feature/vdo-completion
vsem-azamat Jun 9, 2026
7489918
address review comments
vsem-azamat Jun 9, 2026
b2ccc73
update checksums
vsem-azamat Jun 9, 2026
ef3ca6a
document fd lifetime assumption
vsem-azamat Jun 15, 2026
17f0d26
remove file_descriptor until there is a consumer
vsem-azamat Jun 15, 2026
f4c9497
update checksums
vsem-azamat Jun 15, 2026
db02c34
update checksums
vsem-azamat Jul 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ diqwest = "3.1.0"
digest_auth = "0.3.1"
dirs = "5.0.1"
env_logger = { version = "0.11.3", default-features = false }
expect-test = "1.5.1"
Comment thread
apljungquist marked this conversation as resolved.
futures = "0.3.30"
futures-lite = "2.6.0"
futures-util = "0.3.30"
Expand Down Expand Up @@ -64,6 +65,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]
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ check_tests:
--exclude bbox \
--exclude licensekey \
--exclude mdb \
--exclude vdo \
--locked \
--workspace
.PHONY: check_tests
Expand Down
2 changes: 1 addition & 1 deletion apps-aarch64.checksum
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ d2365ffe408d5a77b326589d664d3647f4e99ac8 target-aarch64/acap/send_event_1_0_0_a
abfdb79508c4395f124c9cbe622e695515a8054f target-aarch64/acap/subscribe_to_event_1_0_0_aarch64.eap
a6e0ee05e0ea5b0e76308b6d91b06b61fb2d9029 target-aarch64/acap/using_a_build_script_0_0_0_aarch64.eap
61c0f96d5388ebb6a46a80f0fd2f57b86143a2f6 target-aarch64/acap/vapix_access_0_0_0_aarch64.eap
8f9dea1da7a39c1c37b99d6ab1a33d0abe0d643f target-aarch64/acap/vdoencodeclient_1_0_0_aarch64.eap
a69abf2f8bdb3201f75a0cdcb52b44121f171f53 target-aarch64/acap/vdoencodeclient_1_0_0_aarch64.eap
2 changes: 1 addition & 1 deletion apps-aarch64.filesize
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
3418 target-aarch64/acap/subscribe_to_event_1_0_0_aarch64.eap
899 target-aarch64/acap/using_a_build_script_0_0_0_aarch64.eap
11330 target-aarch64/acap/vapix_access_0_0_0_aarch64.eap
1407 target-aarch64/acap/vdoencodeclient_1_0_0_aarch64.eap
2837 target-aarch64/acap/vdoencodeclient_1_0_0_aarch64.eap
2 changes: 1 addition & 1 deletion apps/vdo_encode_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ publish = false
log = { workspace = true }

acap-logging = { workspace = true }
vdo-sys = { workspace = true }
vdo = { workspace = true }
121 changes: 107 additions & 14 deletions apps/vdo_encode_client/src/main.rs
Comment thread
apljungquist marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,20 +1,113 @@
//! This application is a basic VDO type of application.
//! VDO Example Application
//!
//! The application starts a VDO stream and illustrates how to continuously capture frames from the
//! VDO service, access the received buffer contents, as well as the frame metadata.
//! This application demonstrates the VDO (Video Capture) API by capturing
//! frames from the camera in various formats.
//!
//! # Arguments
//!
//! - `format`: A string describing the video compression format.
//! Possible values are `h264` (default), `h265`, `jpeg`, `nv12`, and `y800`.
//! - `frames`: An integer specifying the number of captured frames.
//! - `output`: The output filename.
//!
use log::info;
//! It tests:
//! - Stream creation with different formats (YUV, JPEG, H.264)
//! - Frame capture and metadata access
//! - Proper resource cleanup

// These format tests run on the device as an ACAP application rather than as
// unit tests because they require access to actual camera hardware via the VDO API.

use log::{error, info};
use vdo::{Error, Resolution, StreamBuilder, VdoFormat};

fn capture_format(name: &str, format: VdoFormat, num_frames: usize) -> Result<(), Error> {
info!("=== Testing {} format ===", name);

let stream = StreamBuilder::new()
.channel(0)
.format(format)
.resolution(Resolution::Exact {
width: 640,
height: 480,
})
.framerate(15)
.build()?;

info!("{}: Stream created successfully", name);

// Get stream info
if let Ok(stream_info) = stream.info() {
info!("{}: Stream info:", name);
stream_info.dump();
}

let running = stream.start()?;
info!("{}: Stream started", name);

for i in 0..num_frames {
let buffer = running.next_buffer()?;
let size = buffer.size();
let seq = buffer.sequence_number();
let ts = buffer.timestamp();

info!(
"{}: Frame {}: {} bytes, seq={}, timestamp={}us",
name, i, size, seq, ts
);

// For JPEG, verify magic bytes
if format == VdoFormat::VDO_FORMAT_JPEG {
let data = buffer.as_slice()?;
if data.len() >= 2 && data[0] == 0xFF && data[1] == 0xD8 {
info!("{}: Frame {} has valid JPEG header", name, i);
} else {
error!("{}: Frame {} has INVALID JPEG header!", name, i);
}
}
}

drop(running);
info!("{}: Stream stopped successfully", name);
info!("");

Ok(())
}

fn main() {
acap_logging::init_logger();
unsafe { assert!(!vdo_sys::vdo_map_new().is_null()) };
info!("vdo map created");
todo!("Implement the real example")

info!("VDO Example Application starting...");
info!("Testing VDO safe Rust bindings");
info!("");

// Test YUV (most portable format)
match capture_format("YUV", VdoFormat::VDO_FORMAT_YUV, 5) {
Ok(()) => info!("YUV test: PASSED"),
Err(e) => error!("YUV test: FAILED - {}", e),
}

// Test JPEG
match capture_format("JPEG", VdoFormat::VDO_FORMAT_JPEG, 5) {
Ok(()) => info!("JPEG test: PASSED"),
Err(e) => error!("JPEG test: FAILED - {}", e),
}

// Test H.264
match capture_format("H.264", VdoFormat::VDO_FORMAT_H264, 10) {
Ok(()) => info!("H.264 test: PASSED"),
Err(e) => error!("H.264 test: FAILED - {}", e),
}

// Test H.265 (might not be supported on all platforms)
match capture_format("H.265", VdoFormat::VDO_FORMAT_H265, 5) {
Ok(()) => info!("H.265 test: PASSED"),
Err(e) => {
if let Error::Vdo(ref vdo_err) = e {
if vdo_err.code_name() == "VDO_ERROR_NOT_SUPPORTED" {
info!("H.265 test: SKIPPED (not supported on this platform)");
} else {
error!("H.265 test: FAILED - {}", e);
}
} else {
error!("H.265 test: FAILED - {}", e);
}
}
}

info!("");
info!("VDO Example Application completed!");
}
25 changes: 25 additions & 0 deletions crates/vdo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "vdo"
version = "0.0.0"
edition.workspace = true
license = "MIT"
description = "Safe Rust bindings for the VDO (Video Capture) API"

[dependencies]
vdo-sys = { workspace = true }
log = { workspace = true }
glib = { workspace = true }
glib-sys = { workspace = true }
gobject-sys = { workspace = true }
thiserror = { workspace = true }

[features]
device-tests = []

[dev-dependencies]
anyhow = { workspace = true }
env_logger = { workspace = true }
expect-test = { workspace = true }

[[example]]
name = "basic"
45 changes: 45 additions & 0 deletions crates/vdo/examples/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! Basic example of using VDO to capture video frames.
//!
//! This example creates a video stream, captures a few frames, and prints
//! information about each frame.

use vdo::{Resolution, StreamBuilder, VdoFormat};

fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize logging (optional)
env_logger::init();

println!("Creating video stream...");

// Create a stream with YUV format (most portable across platforms)
let stream = StreamBuilder::new()
.channel(0)
.format(VdoFormat::VDO_FORMAT_YUV)
.resolution(Resolution::Exact {
width: 640,
height: 480,
})
.framerate(15)
.build()?;

println!("Starting stream...");
let running = stream.start()?;

println!("Capturing frames...");
for i in 0..10 {
let buffer = running.next_buffer()?;
println!(
"Frame {}: {} bytes, seq={}, timestamp={}us",
i,
buffer.size(),
buffer.sequence_number(),
buffer.timestamp()
);
}

println!("Stopping stream...");
drop(running);

println!("Done!");
Ok(())
}
Loading
Loading