ndi_sdk_sys/
router.rs

1//! NDI Router
2//!
3//! Using NDI routing, you can create an output on a machine that looks just like a ‘real’ video source to all remote systems.
4//! However, rather than producing actual video frames, it directs sources watching this output to receive video from a different location.
5//!
6//! NDI routing does not actually transfer any data through the computer hosting the routing source; it merely instructs receivers to look at another location when they wish to receive data from the router.
7//! Thus, a computer can act as a router exposing potentially hundreds of routing sources to the network – without any bandwidth overhead.
8//! This facility can be used for large-scale dynamic switching of sources at a network level.
9//!
10//! <https://docs.ndi.video/all/developing-with-ndi/sdk/ndi-routing>
11
12use std::{ffi::CString, fmt::Debug};
13
14use static_assertions::assert_impl_all;
15
16use crate::{
17    bindings,
18    source::{NDISourceLike, NDISourceRef},
19    util::{SourceNameError, validate_source_name},
20};
21
22/// Builder for [NDIRouter]
23#[derive(Debug, Clone)]
24pub struct NDIRouterBuilder {
25    name: CString,
26}
27assert_impl_all!(NDIRouterBuilder: Send, Sync);
28
29impl NDIRouterBuilder {
30    /// Creates a new [NDIRouterBuilder] with the given source name
31    ///
32    /// The total length of an NDI source name should be limited to 253 characters. The following characters
33    /// are considered invalid: \ / : * ? " < > |. If any of these characters are found in the name, they will
34    /// be replaced with a space. These characters are reserved according to Windows file system naming conventions
35    pub fn new(name: &str) -> Result<Self, SourceNameError> {
36        Ok(Self {
37            name: validate_source_name(name)?,
38        })
39    }
40
41    pub fn build(self) -> Result<NDIRouter, NDIRouterBuilderError> {
42        let options = bindings::NDIlib_routing_create_t {
43            // We only need the name for the constructor
44            p_ndi_name: self.name.as_ptr(),
45            p_groups: std::ptr::null(),
46        };
47        let handle = unsafe { bindings::NDIlib_routing_create(&options) };
48
49        if handle.is_null() {
50            return Err(NDIRouterBuilderError::CreationFailed);
51        }
52
53        Ok(NDIRouter { handle })
54    }
55}
56
57#[non_exhaustive]
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum NDIRouterBuilderError {
60    CreationFailed,
61}
62
63/// NDI Router
64///
65/// For more information see module docs
66#[derive(Debug)]
67pub struct NDIRouter {
68    handle: bindings::NDIlib_routing_instance_t,
69}
70unsafe impl Send for NDIRouter {}
71unsafe impl Sync for NDIRouter {}
72
73impl NDIRouter {
74    pub fn switch(&mut self, source: &impl NDISourceLike) -> Option<()> {
75        let mut result = false;
76        source.with_descriptor(|source_ptr| {
77            result = unsafe { bindings::NDIlib_routing_change(self.handle, source_ptr) };
78        });
79        if result { Some(()) } else { None }
80    }
81
82    pub fn switch_clear(&mut self) -> Option<()> {
83        let result = unsafe { bindings::NDIlib_routing_clear(self.handle) };
84        if result { Some(()) } else { None }
85    }
86
87    /// Get the source descriptor that is needed to connect to this router source
88    pub fn get_source(&self) -> NDISourceRef<'_> {
89        // SDK Docs: Retrieve the source information for the given router instance. This pointer is valid until NDIlib_routing_destroy is called.
90        let source = unsafe { bindings::NDIlib_routing_get_source_name(self.handle) };
91
92        unsafe {
93            NDISourceRef::from(
94                *source
95                    .as_ref()
96                    .expect("[Fatal FFI Error] NDI SDK returned nullptr for source descriptor"),
97            )
98        }
99    }
100}
101
102impl Drop for NDIRouter {
103    fn drop(&mut self) {
104        unsafe { bindings::NDIlib_routing_destroy(self.handle) };
105    }
106}