Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 14 additions & 1 deletion Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,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
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this example program equivalent to it's namesake from acap-native-sdk-examples?

It's OK if it is not, but then we should rename the example since the current name reflects the intention that it is a port of the C example (#183), like many other examples are ports of their namesake C example.

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, Stream, VdoFormat};

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

let stream = Stream::builder()
.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);
}
}
}

running.stop();
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!");
}
23 changes: 23 additions & 0 deletions crates/vdo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[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-sys = { workspace = true }
gobject-sys = { workspace = true }
thiserror = { workspace = true }

[features]
device-tests = []

[dev-dependencies]
anyhow = { workspace = true }
env_logger = { 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, Stream, 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 = Stream::builder()
.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...");
running.stop();

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