Skip to content

Commit

Permalink
Improved memory allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
GrantSparks committed Jul 22, 2024
1 parent 9633b29 commit 2579a03
Show file tree
Hide file tree
Showing 6 changed files with 360 additions and 195 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ exclude = ["target/", ".gitignore", ".github/", "NDI_6_SDK.zip"]

[dependencies]
png = "0.17.13"
thiserror = "1.0.61"

[build-dependencies]
bindgen = "0.69.4"
Expand Down
38 changes: 25 additions & 13 deletions examples/NDIlib_Find.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,50 @@
use grafton_ndi::{Error, Find, Finder, NDI};
use std::ffi::CString;
use std::time::{Duration, Instant};

use grafton_ndi::{Find, Finder, NDI};

fn main() -> Result<(), &'static str> {
fn main() -> Result<(), Error> {
// Initialize the NDI library and ensure it's properly cleaned up
if let Ok(_ndi) = NDI::new() {
if let Ok(ndi) = NDI::new() {
// Create a CString for the IP address
let ip_address = CString::new("192.168.0.110").expect("CString::new failed");

// Convert the CString to &str
let ip_str = ip_address
.to_str()
.expect("CString to str conversion failed");

// Create an NDI finder to locate sources on the network
// let finder = Finder::default();
let finder = Finder::new(false, None, Some("192.168.0.110"));
let ndi_find = Find::new(finder)?;
let finder = Finder::new(false, None, Some(ip_str));
let ndi_find = Find::new(&ndi, finder)?;

// Run for one minute
// Run for 15 seconds
let start = Instant::now();
while start.elapsed() < Duration::from_secs(60) {
while start.elapsed() < Duration::from_secs(15) {
// Wait up to 5 seconds to check for new sources to be added or removed
println!("Waiting for sources...");
if !ndi_find.wait_for_sources(5000) {
println!("No change to the sources found.");
continue;
}

// Get the updated list of sources
let sources = ndi_find.get_sources(5000);
println!("Getting sources...");
let sources = ndi_find.get_sources(5000)?;
println!("Sources retrieved.");

// Display all the sources
println!("Network sources ({} found).", sources.len());
for (i, source) in sources.iter().enumerate() {
println!("{}. {}", i + 1, source.name);
println!("{}. {}", i + 1, source);
}
}

// The NDI finder will be destroyed automatically when it goes out of scope
// The NDI library will be destroyed automatically when `_ndi` goes out of scope
// The NDI library will be destroyed automatically when `ndi` goes out of scope
} else {
return Err("Failed to initialize NDI library");
return Err(Error::InitializationFailed(
"Failed to initialize NDI library".into(),
));
}

Ok(())
Expand Down
67 changes: 35 additions & 32 deletions examples/NDIlib_Recv_PNG.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
use std::fs::File;

use grafton_ndi::{
Find, Finder, FrameType, Receiver, Recv, RecvBandwidth, RecvColorFormat, VideoFrame, NDI,
Error, Find, Finder, FrameType, Receiver, Recv, RecvBandwidth, RecvColorFormat, VideoFrame, NDI,
};
use std::ffi::CString;
use std::fs::File;

fn main() -> Result<(), &'static str> {
fn main() -> Result<(), Error> {
// Initialize the NDI library and ensure it's properly cleaned up
if let Ok(_ndi) = NDI::new() {
if let Ok(ndi) = NDI::new() {
// Create a CString for the IP address
let ip_address =
CString::new("192.168.0.110").map_err(Error::InvalidCString)?;

// Convert the CString to &str
let ip_str = ip_address
.to_str()
.map_err(|_| Error::InvalidUtf8("CString to str conversion failed".into()))?;

// Create an NDI finder to locate sources on the network
// let finder = Finder::default();
let finder = Finder::new(false, None, Some("192.168.0.110"));
let ndi_find = Find::new(finder)?;
let finder = Finder::new(false, None, Some(ip_str));
let ndi_find = Find::new(&ndi, finder)?;

// Wait until we find a source named "CAMERA4"
let source_name = "CAMERA4";
Expand All @@ -20,7 +28,7 @@ fn main() -> Result<(), &'static str> {
// Wait until the sources on the network have changed
println!("Looking for sources ...");
ndi_find.wait_for_sources(5000);
let sources = ndi_find.get_sources(5000);
let sources = ndi_find.get_sources(5000)?;

for source in &sources {
if source.name.contains(source_name) {
Expand All @@ -30,9 +38,9 @@ fn main() -> Result<(), &'static str> {
}
}

let source =
found_source.unwrap_or_else(|| panic!("Failed to find source {}", source_name));

let source = found_source.ok_or_else(|| {
Error::InitializationFailed(format!("Failed to find source {}", source_name))
})?;
println!("Found source: {:?}", source);

// We now have the desired source, so we create a receiver to look at it.
Expand All @@ -43,12 +51,12 @@ fn main() -> Result<(), &'static str> {
false,
None,
);
let ndi_recv = Recv::new(receiver)?;
let ndi_recv = Recv::new(&ndi, receiver)?;

// Wait until we have a video frame
let mut video_frame: Option<VideoFrame> = None;
while video_frame.is_none() {
// Sleep for 3 seconds
// Sleep for 5 seconds
std::thread::sleep(std::time::Duration::from_secs(5));

println!("Waiting for video frame ...");
Expand All @@ -74,23 +82,23 @@ fn main() -> Result<(), &'static str> {
if let Err(e) = save_frame_as_png(&frame) {
eprintln!("Failed to save frame as PNG: {}", e);
}

// Free the data
ndi_recv.free_video(&frame);
}

// The NDI receiver will be destroyed automatically when it goes out of scope
// The NDI library will be destroyed automatically when `_ndi` goes out of scope
// The NDI library will be destroyed automatically when `ndi` goes out of scope
} else {
return Err("Failed to initialize NDI library");
return Err(Error::InitializationFailed(
"Failed to initialize NDI library".into(),
));
}

Ok(())
}

fn save_frame_as_png(video_frame: &VideoFrame) -> Result<(), &'static str> {
fn save_frame_as_png(video_frame: &VideoFrame) -> Result<(), Error> {
let path = "CoolNDIImage.png";
let file = File::create(path).map_err(|_| "Failed to create file")?;
let file = File::create(path)
.map_err(|_| Error::InitializationFailed("Failed to create file".into()))?;
let mut encoder = png::Encoder::new(file, video_frame.xres as u32, video_frame.yres as u32);
encoder.set_color(png::ColorType::Rgba);
encoder.set_depth(png::BitDepth::Eight);
Expand All @@ -103,21 +111,16 @@ fn save_frame_as_png(video_frame: &VideoFrame) -> Result<(), &'static str> {
unsafe { video_frame.line_stride_or_size.line_stride_in_bytes }
);

// Ensure the p_data pointer is valid
if video_frame.p_data.is_null() {
return Err("Frame data pointer is null");
// Ensure the data is not empty
if video_frame.data.is_empty() {
return Err(Error::InitializationFailed("Frame data is empty".into()));
}

let mut writer = encoder
.write_header()
.map_err(|_| "Failed to write PNG header")?;
.map_err(|_| Error::InitializationFailed("Failed to write PNG header".into()))?;
writer
.write_image_data(unsafe {
let data_len =
(video_frame.yres * video_frame.line_stride_or_size.line_stride_in_bytes) as usize;
println!("Data length: {}", data_len);
std::slice::from_raw_parts(video_frame.p_data, data_len)
})
.map_err(|_| "Failed to write PNG data")?;
.write_image_data(&video_frame.data)
.map_err(|_| Error::InitializationFailed("Failed to write PNG data".into()))?;
Ok(())
}
27 changes: 17 additions & 10 deletions examples/NDIlib_Recv_PTZ.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
use std::time::{Duration, Instant};
use std::{
ffi::CString,
time::{Duration, Instant},
};

use grafton_ndi::*;

fn main() {
if let Ok(_ndi) = NDI::new() {
if let Ok(ndi) = NDI::new() {
println!("NDI initialized successfully.");

// We first need to look for a source on the network
// let finder = Finder::default();
let finder = Finder::new(false, None, Some("192.168.0.110"));
let ndi_find = Find::new(finder).expect("Failed to create NDI find instance");
// Create a CString for the IP address and convert to &str
let ip_address = CString::new("192.168.0.110").expect("CString::new failed");
let ip_str = ip_address
.to_str()
.expect("CString to str conversion failed");

// Create an NDI finder to locate sources on the network
let finder = Finder::new(false, None, Some(ip_str));
let ndi_find = Find::new(&ndi, finder).expect("Failed to create NDI find instance");

// We wait until there is at least one source on the network
let mut sources = vec![];
while sources.is_empty() {
if ndi_find.wait_for_sources(1000) {
sources = ndi_find.get_sources(1000);
sources = ndi_find.get_sources(1000).expect("Failed to get sources");
}
}

Expand All @@ -37,7 +45,7 @@ fn main() {
Some("Example PTZ Receiver".to_string()),
);

let ndi_recv = Recv::new(receiver).expect("Failed to create NDI recv instance");
let ndi_recv = Recv::new(&ndi, receiver).expect("Failed to create NDI recv instance");

// Run for 5 seconds
let start = Instant::now();
Expand Down Expand Up @@ -66,8 +74,7 @@ fn main() {
}
}

// Destroy the receiver
drop(ndi_recv);
// The NDI receiver will be destroyed automatically when it goes out of scope
} else {
println!("Cannot run NDI. Most likely because the CPU is not sufficient (see SDK documentation).");
}
Expand Down
14 changes: 14 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use std::ffi::NulError;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
#[error("Failed to initialize the NDI runtime: {0}")]
InitializationFailed(String),
#[error("Encountered a null pointer in function: {0}")]
NullPointer(String),
#[error("Invalid UTF-8 string in data: {0}")]
InvalidUtf8(String),
#[error("Invalid CString: {0}")]
InvalidCString(#[from] NulError),
}
Loading

0 comments on commit 2579a03

Please sign in to comment.