Skip to main content

ndi_sdk/
find.rs

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                // We'll just bail out early.
141                break;
142            }
143        }
144        Ok(())
145    }
146}