Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sdroege/gst-plugin-rs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Dröge <sebastian@centricular.com>2022-10-12 21:14:45 +0300
committerSebastian Dröge <sebastian@centricular.com>2022-10-12 22:09:53 +0300
commit3fe9e4a207f8da5d5f3996160a09a12ed19a2a3c (patch)
treeb0196ac4c62180e51ce18dde4c6cdd33aa873785
parent16c036e2ccc3bbb30e541c4be44cb16ea9093192 (diff)
ndi: Implement dynamic loading of the NDI SDK
And build the plugin on the CI and via meson.
-rw-r--r--Cargo.toml1
-rw-r--r--README.md1
-rw-r--r--meson.build1
-rw-r--r--net/ndi/Cargo.toml3
-rw-r--r--net/ndi/README.md63
-rw-r--r--net/ndi/src/device_provider/imp.rs4
-rw-r--r--net/ndi/src/lib.rs4
-rw-r--r--net/ndi/src/ndi.rs4
-rw-r--r--net/ndi/src/ndisink/imp.rs17
-rw-r--r--net/ndi/src/ndisrc/imp.rs6
-rw-r--r--net/ndi/src/ndisys.rs344
11 files changed, 321 insertions, 127 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 78574627..711e9c4e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -57,6 +57,7 @@ default-members = [
"net/reqwest",
"net/rtpav1",
"net/webrtc-http",
+ "net/ndi",
"text/ahead",
"text/json",
"text/regex",
diff --git a/README.md b/README.md
index aea88723..8a341e8d 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,7 @@ You will find the following plugins in this repository:
- `aws_transcriber`: an element wrapping the AWS Transcriber service.
- `raptorq`: Encoder/decoder element for RaptorQ RTP FEC mechanism.
+ - `ndi`: An [NDI](https://www.newtek.com/ndi/) plugin containing a source, sink and device provider.
* `audio`
- `audiofx`: Elements to apply audio effects to a stream
diff --git a/meson.build b/meson.build
index 3d8ac24f..c9d10bf5 100644
--- a/meson.build
+++ b/meson.build
@@ -69,6 +69,7 @@ plugins = {
'gst-plugin-tracers': 'libgstrstracers',
'gst-plugin-webrtchttp': 'libgstwebrtchttp',
'gst-plugin-rtpav1': 'libgstrtpav1',
+ 'gst-plugin-ndi': 'libgstndi',
}
extra_env = {}
diff --git a/net/ndi/Cargo.toml b/net/ndi/Cargo.toml
index d0b1c39e..c9a20441 100644
--- a/net/ndi/Cargo.toml
+++ b/net/ndi/Cargo.toml
@@ -18,6 +18,7 @@ byte-slice-cast = "1"
once_cell = "1.0"
byteorder = "1.0"
atomic_refcell = "0.1"
+libloading = "0.7"
[build-dependencies]
gst-plugin-version-helper = { path = "../../version-helper" }
@@ -47,4 +48,4 @@ install_subdir = "gstreamer-1.0"
versioning = false
[package.metadata.capi.pkg_config]
-requires_private = "gstreamer-1.0, gstreamer-base-1.0, gstreamer-audio-1.0, gstreamer-video-1.0, gobject-2.0, glib-2.0, gmodule-2.0, ndi"
+requires_private = "gstreamer-1.0, gstreamer-base-1.0, gstreamer-audio-1.0, gstreamer-video-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
diff --git a/net/ndi/README.md b/net/ndi/README.md
index 2446cae3..c783e336 100644
--- a/net/ndi/README.md
+++ b/net/ndi/README.md
@@ -1,11 +1,23 @@
-GStreamer NDI Plugin for Linux
+GStreamer NDI Plugin
====================
-*Compiled and tested with NDI SDK 4.0, 4.1 and 5.0*
+*Compatible with NDI SDK 5.x*
-This is a plugin for the [GStreamer](https://gstreamer.freedesktop.org/) multimedia framework that allows GStreamer to receive a stream from a [NDI](https://www.newtek.com/ndi/) source. This plugin has been developed by [Teltek](http://teltek.es/) and was funded by the [University of the Arts London](https://www.arts.ac.uk/) and [The University of Manchester](https://www.manchester.ac.uk/).
+This is a plugin for the [GStreamer](https://gstreamer.freedesktop.org/)
+multimedia framework that allows GStreamer to receive or send an
+[NDI](https://www.newtek.com/ndi/) stream.
-Currently the plugin has a source element for receiving from NDI sources, a sink element to provide an NDI source and a device provider for discovering NDI sources on the network.
+This plugin has been initially developed by [Teltek](http://teltek.es/) and
+was funded by the [University of the Arts London](https://www.arts.ac.uk/) and
+[The University of Manchester](https://www.manchester.ac.uk/).
+
+Currently the plugin has a source element for receiving from NDI sources, a
+sink element to provide an NDI source and a device provider for discovering
+NDI sources on the network.
+
+The plugin is loading the NDI SDK at runtime, either from the default library
+path or, if set, from the directory given by the `NDI_RUNTIME_DIR_V5`
+environment variable.
Some examples of how to use these elements from the command line:
@@ -26,55 +38,14 @@ $ gst-launch-1.0 videotestsrc is-live=true ! video/x-raw,format=UYVY ! ndisinkco
```
Feel free to contribute to this project. Some ways you can contribute are:
+
* Testing with more hardware and software and reporting bugs
* Doing pull requests.
-Compilation of the NDI element
--------
-To compile the NDI element it's necessary to install Rust, the NDI SDK and the following packages for gstreamer:
-
-```console
-$ apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
- gstreamer1.0-plugins-base
-
-```
-To install the required NDI library there are two options:
-1. Download NDI SDK from NDI website and move the library to the correct location.
-2. Use a [deb package](https://github.com/Palakis/obs-ndi/releases/download/4.5.2/libndi3_3.5.1-1_amd64.deb) made by the community. Thanks to [NDI plugin for OBS](https://github.com/Palakis/obs-ndi).
-
-To install Rust, you can follow their documentation: https://www.rust-lang.org/en-US/install.html
-
-Once all requirements are met, you can build the plugin by executing the following command from the project root folder:
-
-```
-cargo build
-export GST_PLUGIN_PATH=`pwd`/target/debug
-gst-inspect-1.0 ndi
-```
-
-By default GStreamer 1.18 is required, to use an older version. You can build with `$ cargo build --no-default-features --features whatever_you_want_to_enable_of_the_above_features`
-
-
-If all went ok, you should see info related to the NDI element. To make the plugin available without using `GST_PLUGIN_PATH` it's necessary to copy the plugin to the gstreamer plugins folder.
-
-```console
-$ cargo build --release
-$ sudo install -o root -g root -m 644 target/release/libgstndi.so /usr/lib/x86_64-linux-gnu/gstreamer-1.0/
-$ sudo ldconfig
-$ gst-inspect-1.0 ndi
-```
-
-More info about GStreamer plugins written in Rust:
-----------------------------------
-https://gitlab.freedesktop.org/gstreamer/gstreamer-rs
-https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs
-
-
License
-------
This plugin is licensed under the MPL-2 - see the [LICENSE](LICENSE-MPL-2.0) file for details
-
Acknowledgments
-------
* University of the Arts London and The University of Manchester.
diff --git a/net/ndi/src/device_provider/imp.rs b/net/ndi/src/device_provider/imp.rs
index 6fba8c73..7cc4bd69 100644
--- a/net/ndi/src/device_provider/imp.rs
+++ b/net/ndi/src/device_provider/imp.rs
@@ -72,6 +72,10 @@ impl DeviceProviderImpl for DeviceProvider {
}
fn start(&self) -> Result<(), gst::LoggableError> {
+ if let Err(err) = crate::ndi::load() {
+ return Err(gst::loggable_error!(CAT, "{}", err));
+ }
+
let mut thread_guard = self.thread.lock().unwrap();
let device_provider = self.instance();
if thread_guard.is_some() {
diff --git a/net/ndi/src/lib.rs b/net/ndi/src/lib.rs
index 2a168b8f..cf4ce9bb 100644
--- a/net/ndi/src/lib.rs
+++ b/net/ndi/src/lib.rs
@@ -118,10 +118,6 @@ impl From<RecvColorFormat> for NDIlib_recv_color_format_e {
}
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
- if !ndi::initialize() {
- return Err(glib::bool_error!("Cannot initialize NDI"));
- }
-
device_provider::register(plugin)?;
ndisrc::register(plugin)?;
diff --git a/net/ndi/src/ndi.rs b/net/ndi/src/ndi.rs
index b9fe48bd..ecbc648c 100644
--- a/net/ndi/src/ndi.rs
+++ b/net/ndi/src/ndi.rs
@@ -9,8 +9,8 @@ use std::ptr;
use byte_slice_cast::*;
-pub fn initialize() -> bool {
- unsafe { NDIlib_initialize() }
+pub fn load() -> Result<(), glib::BoolError> {
+ ndisys::load()
}
#[derive(Debug)]
diff --git a/net/ndi/src/ndisink/imp.rs b/net/ndi/src/ndisink/imp.rs
index eb3c7dc1..24eb1694 100644
--- a/net/ndi/src/ndisink/imp.rs
+++ b/net/ndi/src/ndisink/imp.rs
@@ -167,6 +167,23 @@ impl ElementImpl for NdiSink {
PAD_TEMPLATES.as_ref()
}
+
+ fn change_state(
+ &self,
+ transition: gst::StateChange,
+ ) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
+ match transition {
+ gst::StateChange::NullToReady => {
+ if let Err(err) = crate::ndi::load() {
+ gst::element_imp_error!(self, gst::LibraryError::Init, ("{}", err));
+ return Err(gst::StateChangeError);
+ }
+ }
+ _ => (),
+ }
+
+ self.parent_change_state(transition)
+ }
}
impl BaseSinkImpl for NdiSink {
diff --git a/net/ndi/src/ndisrc/imp.rs b/net/ndi/src/ndisrc/imp.rs
index d19c049d..5bd90dfc 100644
--- a/net/ndi/src/ndisrc/imp.rs
+++ b/net/ndi/src/ndisrc/imp.rs
@@ -370,6 +370,12 @@ impl ElementImpl for NdiSrc {
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
match transition {
+ gst::StateChange::NullToReady => {
+ if let Err(err) = crate::ndi::load() {
+ gst::element_imp_error!(self, gst::LibraryError::Init, ("{}", err));
+ return Err(gst::StateChangeError);
+ }
+ }
gst::StateChange::PausedToPlaying => {
if let Some(ref controller) = *self.receiver_controller.lock().unwrap() {
controller.set_playing(true);
diff --git a/net/ndi/src/ndisys.rs b/net/ndi/src/ndisys.rs
index 86053abd..b5c813fe 100644
--- a/net/ndi/src/ndisys.rs
+++ b/net/ndi/src/ndisys.rs
@@ -2,80 +2,66 @@
#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]
-#[cfg_attr(
- all(target_arch = "x86_64", target_os = "windows"),
- link(name = "Processing.NDI.Lib.x64")
-)]
-#[cfg_attr(
- all(target_arch = "x86", target_os = "windows"),
- link(name = "Processing.NDI.Lib.x86")
-)]
-#[cfg_attr(
- not(any(target_os = "windows", target_os = "macos")),
- link(name = "ndi")
-)]
-extern "C" {
- pub fn NDIlib_initialize() -> bool;
- pub fn NDIlib_destroy();
- pub fn NDIlib_find_create_v2(
- p_create_settings: *const NDIlib_find_create_t,
- ) -> NDIlib_find_instance_t;
- pub fn NDIlib_find_destroy(p_instance: NDIlib_find_instance_t);
- pub fn NDIlib_find_wait_for_sources(
- p_instance: NDIlib_find_instance_t,
- timeout_in_ms: u32,
- ) -> bool;
- pub fn NDIlib_find_get_current_sources(
- p_instance: NDIlib_find_instance_t,
- p_no_sources: *mut u32,
- ) -> *const NDIlib_source_t;
- pub fn NDIlib_recv_create_v3(
- p_create_settings: *const NDIlib_recv_create_v3_t,
- ) -> NDIlib_recv_instance_t;
- pub fn NDIlib_recv_destroy(p_instance: NDIlib_recv_instance_t);
- pub fn NDIlib_recv_set_tally(
- p_instance: NDIlib_recv_instance_t,
- p_tally: *const NDIlib_tally_t,
- ) -> bool;
- pub fn NDIlib_recv_send_metadata(
- p_instance: NDIlib_recv_instance_t,
- p_metadata: *const NDIlib_metadata_frame_t,
- ) -> bool;
- pub fn NDIlib_recv_capture_v3(
- p_instance: NDIlib_recv_instance_t,
- p_video_data: *mut NDIlib_video_frame_v2_t,
- p_audio_data: *mut NDIlib_audio_frame_v3_t,
- p_metadata: *mut NDIlib_metadata_frame_t,
- timeout_in_ms: u32,
- ) -> NDIlib_frame_type_e;
- pub fn NDIlib_recv_free_video_v2(
- p_instance: NDIlib_recv_instance_t,
- p_video_data: *mut NDIlib_video_frame_v2_t,
- );
- pub fn NDIlib_recv_free_audio_v3(
- p_instance: NDIlib_recv_instance_t,
- p_audio_data: *mut NDIlib_audio_frame_v3_t,
- );
- pub fn NDIlib_recv_free_metadata(
- p_instance: NDIlib_recv_instance_t,
- p_metadata: *mut NDIlib_metadata_frame_t,
- );
- pub fn NDIlib_recv_get_queue(
- p_instance: NDIlib_recv_instance_t,
- p_total: *mut NDIlib_recv_queue_t,
- );
- pub fn NDIlib_send_create(
- p_create_settings: *const NDIlib_send_create_t,
- ) -> NDIlib_send_instance_t;
- pub fn NDIlib_send_destroy(p_instance: NDIlib_send_instance_t);
- pub fn NDIlib_send_send_video_v2(
- p_instance: NDIlib_send_instance_t,
- p_video_data: *const NDIlib_video_frame_v2_t,
- );
- pub fn NDIlib_send_send_audio_v3(
- p_instance: NDIlib_send_instance_t,
- p_audio_data: *const NDIlib_audio_frame_v3_t,
- );
+#[cfg(unix)]
+use libloading::os::unix::{Library, Symbol};
+#[cfg(windows)]
+use libloading::os::windows::{Library, Symbol};
+
+#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
+const LIBRARY_NAME: &str = "Processing.NDI.Lib.x64.dll";
+#[cfg(all(target_arch = "x86", target_os = "windows"))]
+const LIBRARY_NAME: &str = "Processing.NDI.Lib.x86.dll";
+#[cfg(target_os = "linux")]
+const LIBRARY_NAME: &str = "libndi.so.5";
+#[cfg(target_os = "macos")]
+const LIBRARY_NAME: &str = "libndi.dylib";
+
+struct FFI {
+ _library: Library,
+ initialize: Symbol<fn() -> bool>,
+ destroy: Symbol<fn()>,
+ find_create_v2:
+ Symbol<fn(p_create_settings: *const NDIlib_find_create_t) -> NDIlib_find_instance_t>,
+ find_destroy: Symbol<fn(p_instance: NDIlib_find_instance_t)>,
+ find_wait_for_sources:
+ Symbol<fn(p_instance: NDIlib_find_instance_t, timeout_in_ms: u32) -> bool>,
+ find_get_current_sources: Symbol<
+ fn(p_instance: NDIlib_find_instance_t, p_no_sources: *mut u32) -> *const NDIlib_source_t,
+ >,
+ recv_create_v3:
+ Symbol<fn(p_create_settings: *const NDIlib_recv_create_v3_t) -> NDIlib_recv_instance_t>,
+ recv_destroy: Symbol<fn(p_instance: NDIlib_recv_instance_t)>,
+ recv_set_tally:
+ Symbol<fn(p_instance: NDIlib_recv_instance_t, p_tally: *const NDIlib_tally_t) -> bool>,
+ recv_send_metadata: Symbol<
+ fn(p_instance: NDIlib_recv_instance_t, p_metadata: *const NDIlib_metadata_frame_t) -> bool,
+ >,
+ recv_capture_v3: Symbol<
+ fn(
+ p_instance: NDIlib_recv_instance_t,
+ p_video_data: *mut NDIlib_video_frame_v2_t,
+ p_audio_data: *mut NDIlib_audio_frame_v3_t,
+ p_metadata: *mut NDIlib_metadata_frame_t,
+ timeout_in_ms: u32,
+ ) -> NDIlib_frame_type_e,
+ >,
+ recv_free_video_v2:
+ Symbol<fn(p_instance: NDIlib_recv_instance_t, p_video_data: *mut NDIlib_video_frame_v2_t)>,
+ recv_free_audio_v3:
+ Symbol<fn(p_instance: NDIlib_recv_instance_t, p_audio_data: *mut NDIlib_audio_frame_v3_t)>,
+ recv_free_metadata:
+ Symbol<fn(p_instance: NDIlib_recv_instance_t, p_metadata: *mut NDIlib_metadata_frame_t)>,
+ recv_get_queue:
+ Symbol<fn(p_instance: NDIlib_recv_instance_t, p_total: *mut NDIlib_recv_queue_t)>,
+ send_create:
+ Symbol<fn(p_create_settings: *const NDIlib_send_create_t) -> NDIlib_send_instance_t>,
+ send_destroy: Symbol<fn(p_instance: NDIlib_send_instance_t)>,
+ send_send_video_v2: Symbol<
+ fn(p_instance: NDIlib_send_instance_t, p_video_data: *const NDIlib_video_frame_v2_t),
+ >,
+ send_send_audio_v3: Symbol<
+ fn(p_instance: NDIlib_send_instance_t, p_audio_data: *const NDIlib_audio_frame_v3_t),
+ >,
}
pub type NDIlib_find_instance_t = *mut ::std::os::raw::c_void;
@@ -326,3 +312,213 @@ pub const NDIlib_compressed_packet_flags_keyframe: u32 = 1;
#[cfg(feature = "advanced-sdk")]
pub const NDIlib_compressed_packet_version_0: u32 = 44;
+
+static FFI: once_cell::sync::OnceCell<FFI> = once_cell::sync::OnceCell::new();
+
+pub fn load() -> Result<(), glib::BoolError> {
+ static ERR: once_cell::sync::OnceCell<Result<(), glib::BoolError>> =
+ once_cell::sync::OnceCell::new();
+
+ ERR.get_or_init(|| unsafe {
+ use std::env;
+ use std::path;
+
+ let library_directory = env::var_os("NDI_RUNTIME_DIR_V5");
+ let library_path = if let Some(library_directory) = library_directory {
+ let mut path = path::PathBuf::from(library_directory);
+ path.push(LIBRARY_NAME);
+ path
+ } else {
+ path::PathBuf::from(LIBRARY_NAME)
+ };
+
+ let library = Library::new(library_path)
+ .map_err(|err| glib::bool_error!("Failed to load NDI SDK: {}", err))?;
+
+ macro_rules! load_symbol {
+ ($name:ident) => {{
+ #[cfg(unix)]
+ {
+ library
+ .get_singlethreaded(stringify!($name).as_bytes())
+ .map_err(|err| {
+ glib::bool_error!(
+ concat!(
+ "Failed to load function '",
+ stringify!($name),
+ "' from NDI SDK: {}"
+ ),
+ err
+ )
+ })?
+ }
+ #[cfg(windows)]
+ {
+ library.get(stringify!($name).as_bytes()).map_err(|err| {
+ glib::bool_error!(
+ concat!(
+ "Failed to load function '",
+ stringify!($name),
+ "' from NDI SDK: {}"
+ ),
+ err
+ )
+ })?
+ }
+ }};
+ }
+
+ let ffi = FFI {
+ initialize: load_symbol!(NDIlib_initialize),
+ destroy: load_symbol!(NDIlib_destroy),
+ find_create_v2: load_symbol!(NDIlib_find_create_v2),
+ find_destroy: load_symbol!(NDIlib_find_destroy),
+ find_wait_for_sources: load_symbol!(NDIlib_find_wait_for_sources),
+ find_get_current_sources: load_symbol!(NDIlib_find_get_current_sources),
+ recv_create_v3: load_symbol!(NDIlib_recv_create_v3),
+ recv_destroy: load_symbol!(NDIlib_recv_destroy),
+ recv_set_tally: load_symbol!(NDIlib_recv_set_tally),
+ recv_send_metadata: load_symbol!(NDIlib_recv_send_metadata),
+ recv_capture_v3: load_symbol!(NDIlib_recv_capture_v3),
+ recv_free_video_v2: load_symbol!(NDIlib_recv_free_video_v2),
+ recv_free_audio_v3: load_symbol!(NDIlib_recv_free_audio_v3),
+ recv_free_metadata: load_symbol!(NDIlib_recv_free_metadata),
+ recv_get_queue: load_symbol!(NDIlib_recv_get_queue),
+ send_create: load_symbol!(NDIlib_send_create),
+ send_destroy: load_symbol!(NDIlib_send_destroy),
+ send_send_video_v2: load_symbol!(NDIlib_send_send_video_v2),
+ send_send_audio_v3: load_symbol!(NDIlib_send_send_audio_v3),
+ _library: library,
+ };
+
+ if FFI.set(ffi).is_err() {
+ unreachable!("NDI SDK loaded twice");
+ }
+
+ Ok(())
+ })
+ .to_owned()
+}
+
+pub unsafe fn NDIlib_initialize() -> bool {
+ (FFI.get_unchecked().initialize)()
+}
+
+pub unsafe fn NDIlib_destroy() {
+ (FFI.get_unchecked().destroy)()
+}
+
+pub unsafe fn NDIlib_find_create_v2(
+ p_create_settings: *const NDIlib_find_create_t,
+) -> NDIlib_find_instance_t {
+ (FFI.get_unchecked().find_create_v2)(p_create_settings)
+}
+pub unsafe fn NDIlib_find_destroy(p_instance: NDIlib_find_instance_t) {
+ (FFI.get_unchecked().find_destroy)(p_instance)
+}
+
+pub unsafe fn NDIlib_find_wait_for_sources(
+ p_instance: NDIlib_find_instance_t,
+ timeout_in_ms: u32,
+) -> bool {
+ (FFI.get_unchecked().find_wait_for_sources)(p_instance, timeout_in_ms)
+}
+
+pub unsafe fn NDIlib_find_get_current_sources(
+ p_instance: NDIlib_find_instance_t,
+ p_no_sources: *mut u32,
+) -> *const NDIlib_source_t {
+ (FFI.get_unchecked().find_get_current_sources)(p_instance, p_no_sources)
+}
+
+pub unsafe fn NDIlib_recv_create_v3(
+ p_create_settings: *const NDIlib_recv_create_v3_t,
+) -> NDIlib_recv_instance_t {
+ (FFI.get_unchecked().recv_create_v3)(p_create_settings)
+}
+
+pub unsafe fn NDIlib_recv_destroy(p_instance: NDIlib_recv_instance_t) {
+ (FFI.get_unchecked().recv_destroy)(p_instance)
+}
+
+pub unsafe fn NDIlib_recv_set_tally(
+ p_instance: NDIlib_recv_instance_t,
+ p_tally: *const NDIlib_tally_t,
+) -> bool {
+ (FFI.get_unchecked().recv_set_tally)(p_instance, p_tally)
+}
+
+pub unsafe fn NDIlib_recv_send_metadata(
+ p_instance: NDIlib_recv_instance_t,
+ p_metadata: *const NDIlib_metadata_frame_t,
+) -> bool {
+ (FFI.get_unchecked().recv_send_metadata)(p_instance, p_metadata)
+}
+
+pub unsafe fn NDIlib_recv_capture_v3(
+ p_instance: NDIlib_recv_instance_t,
+ p_video_data: *mut NDIlib_video_frame_v2_t,
+ p_audio_data: *mut NDIlib_audio_frame_v3_t,
+ p_metadata: *mut NDIlib_metadata_frame_t,
+ timeout_in_ms: u32,
+) -> NDIlib_frame_type_e {
+ (FFI.get_unchecked().recv_capture_v3)(
+ p_instance,
+ p_video_data,
+ p_audio_data,
+ p_metadata,
+ timeout_in_ms,
+ )
+}
+
+pub unsafe fn NDIlib_recv_free_video_v2(
+ p_instance: NDIlib_recv_instance_t,
+ p_video_data: *mut NDIlib_video_frame_v2_t,
+) {
+ (FFI.get_unchecked().recv_free_video_v2)(p_instance, p_video_data)
+}
+
+pub unsafe fn NDIlib_recv_free_audio_v3(
+ p_instance: NDIlib_recv_instance_t,
+ p_audio_data: *mut NDIlib_audio_frame_v3_t,
+) {
+ (FFI.get_unchecked().recv_free_audio_v3)(p_instance, p_audio_data)
+}
+
+pub unsafe fn NDIlib_recv_free_metadata(
+ p_instance: NDIlib_recv_instance_t,
+ p_metadata: *mut NDIlib_metadata_frame_t,
+) {
+ (FFI.get_unchecked().recv_free_metadata)(p_instance, p_metadata)
+}
+
+pub unsafe fn NDIlib_recv_get_queue(
+ p_instance: NDIlib_recv_instance_t,
+ p_total: *mut NDIlib_recv_queue_t,
+) {
+ (FFI.get_unchecked().recv_get_queue)(p_instance, p_total)
+}
+
+pub unsafe fn NDIlib_send_create(
+ p_create_settings: *const NDIlib_send_create_t,
+) -> NDIlib_send_instance_t {
+ (FFI.get_unchecked().send_create)(p_create_settings)
+}
+
+pub unsafe fn NDIlib_send_destroy(p_instance: NDIlib_send_instance_t) {
+ (FFI.get_unchecked().send_destroy)(p_instance)
+}
+
+pub unsafe fn NDIlib_send_send_video_v2(
+ p_instance: NDIlib_send_instance_t,
+ p_video_data: *const NDIlib_video_frame_v2_t,
+) {
+ (FFI.get_unchecked().send_send_video_v2)(p_instance, p_video_data)
+}
+
+pub unsafe fn NDIlib_send_send_audio_v3(
+ p_instance: NDIlib_send_instance_t,
+ p_audio_data: *const NDIlib_audio_frame_v3_t,
+) {
+ (FFI.get_unchecked().send_send_audio_v3)(p_instance, p_audio_data)
+}