1use crate::sys::{NDIlib_find_create_t, NDIlib_find_instance_t};
2use crate::{NDIError, NDIResult, Source, HANDLE};
3use std::net::IpAddr;
4use std::ptr;
5
6#[derive(Default, Debug, Clone)]
7pub struct FindSettings<'a> {
8 show_local_sources: bool,
9 groups: Vec<&'a str>,
10 extra_ips: Vec<&'a IpAddr>,
11}
12
13impl<'a> FindSettings<'a> {
14 pub fn new() -> Self {
15 Self::default()
16 }
17
18 pub fn show_local_sources(mut self, show: bool) -> Self {
19 self.show_local_sources = show;
20 self
21 }
22
23 pub fn add_group(mut self, group: &'a str) -> Self {
24 self.groups.push(group);
25 self
26 }
27
28 pub fn add_extra_ip(mut self, addr: &'a IpAddr) -> Self {
29 self.extra_ips.push(addr);
30 self
31 }
32
33 pub fn build(self) -> NDIResult<NDIlib_find_create_t> {
34 let groups = self.groups.join(",");
35 let ips = self
36 .groups
37 .iter()
38 .map(|ip| ip.to_string())
39 .collect::<Vec<_>>()
40 .join(",");
41
42 Ok(NDIlib_find_create_t {
43 show_local_sources: self.show_local_sources,
44 p_groups: if self.groups.is_empty() {
45 ptr::null()
46 } else {
47 groups.as_ptr() as *const i8
48 },
49 p_extra_ips: if self.extra_ips.is_empty() {
50 ptr::null()
51 } else {
52 ips.as_ptr() as *const i8
53 },
54 })
55 }
56}
57
58pub struct FindInstance {
59 inner: NDIlib_find_instance_t,
60}
61unsafe impl Send for FindInstance {}
62
63impl Drop for FindInstance {
64 fn drop(&mut self) {
65 if let Some(destroy_fn) = unsafe { (*HANDLE.lib).find_destroy } {
66 unsafe { destroy_fn(self.inner) };
67 }
68 }
69}
70
71impl FindInstance {
72 pub fn create(settings: Option<&NDIlib_find_create_t>) -> NDIResult<FindInstance> {
73 let create = match settings {
74 Some(settings) => settings,
75 None => ptr::null(),
76 };
77 let Some(create_fn) = (unsafe { (*HANDLE.lib).find_create_v2 }) else {
78 return Err(NDIError::MissingSymbolV5("find_create_v2"));
79 };
80
81 let instance_ptr = unsafe { create_fn(create) };
82 if instance_ptr.is_null() {
83 return Err(NDIError::UnexpectedNullPointer("find_create_v2"));
84 }
85 Ok(FindInstance {
86 inner: instance_ptr,
87 })
88 }
89
90 pub fn get_current_sources(&mut self) -> NDIResult<Vec<Source>> {
91 let Some(get_current_fn) = (unsafe { (*HANDLE.lib).find_get_current_sources }) else {
92 return Err(NDIError::MissingSymbolV5("find_get_current_sources"));
93 };
94
95 let mut num_sources: u32 = 0;
96 let sources_ptr = unsafe { get_current_fn(self.inner, &mut num_sources) };
97 if num_sources == 0 {
98 return Ok(Vec::new());
99 }
100 if sources_ptr.is_null() {
101 return Err(NDIError::UnexpectedNullPointer("find_get_current_sources"));
102 }
103 let mut sources = Vec::new();
104 for idx in 0..num_sources as usize {
105 let source = unsafe { &*sources_ptr.add(idx) };
106 sources.push(source.into());
107 }
108 Ok(sources)
109 }
110
111 pub fn wait_for_sources(&mut self, timeout_ms: u32) -> NDIResult<bool> {
112 let Some(wait_fn) = (unsafe { (*HANDLE.lib).find_wait_for_sources }) else {
113 return Err(NDIError::MissingSymbolV5("find_wait_for_sources"));
114 };
115 Ok(unsafe { wait_fn(self.inner, timeout_ms) })
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_find() -> NDIResult<()> {
125 let settings = FindSettings::new().show_local_sources(true).build()?;
126 let mut inst = FindInstance::create(Some(&settings))?;
127 let mut desired_sources = Vec::new();
128 for _ in 0..3 {
129 if !inst.wait_for_sources(5000)? {
130 println!("No sources found!");
131 } else {
132 let sources = inst.get_current_sources()?;
133 println!("Number of NDI Sources: {}", sources.len());
134 for (idx, source) in sources.iter().enumerate() {
135 desired_sources.push(source);
136 println!("\tSource index: {}", idx);
137 println!("\t\tName: {}", source.ndi_name);
138 println!("\t\tURL: {}", source.url_address);
139 }
140 break;
142 }
143 }
144 Ok(())
145 }
146}