mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-26 13:31:00 +00:00
ndi: Implement dynamic loading of the NDI SDK
And build the plugin on the CI and via meson.
This commit is contained in:
parent
16c036e2cc
commit
3fe9e4a207
11 changed files with 321 additions and 127 deletions
|
@ -57,6 +57,7 @@ default-members = [
|
||||||
"net/reqwest",
|
"net/reqwest",
|
||||||
"net/rtpav1",
|
"net/rtpav1",
|
||||||
"net/webrtc-http",
|
"net/webrtc-http",
|
||||||
|
"net/ndi",
|
||||||
"text/ahead",
|
"text/ahead",
|
||||||
"text/json",
|
"text/json",
|
||||||
"text/regex",
|
"text/regex",
|
||||||
|
|
|
@ -32,6 +32,7 @@ You will find the following plugins in this repository:
|
||||||
- `aws_transcriber`: an element wrapping the AWS Transcriber service.
|
- `aws_transcriber`: an element wrapping the AWS Transcriber service.
|
||||||
|
|
||||||
- `raptorq`: Encoder/decoder element for RaptorQ RTP FEC mechanism.
|
- `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`
|
* `audio`
|
||||||
- `audiofx`: Elements to apply audio effects to a stream
|
- `audiofx`: Elements to apply audio effects to a stream
|
||||||
|
|
|
@ -69,6 +69,7 @@ plugins = {
|
||||||
'gst-plugin-tracers': 'libgstrstracers',
|
'gst-plugin-tracers': 'libgstrstracers',
|
||||||
'gst-plugin-webrtchttp': 'libgstwebrtchttp',
|
'gst-plugin-webrtchttp': 'libgstwebrtchttp',
|
||||||
'gst-plugin-rtpav1': 'libgstrtpav1',
|
'gst-plugin-rtpav1': 'libgstrtpav1',
|
||||||
|
'gst-plugin-ndi': 'libgstndi',
|
||||||
}
|
}
|
||||||
|
|
||||||
extra_env = {}
|
extra_env = {}
|
||||||
|
|
|
@ -18,6 +18,7 @@ byte-slice-cast = "1"
|
||||||
once_cell = "1.0"
|
once_cell = "1.0"
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
atomic_refcell = "0.1"
|
atomic_refcell = "0.1"
|
||||||
|
libloading = "0.7"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gst-plugin-version-helper = { path = "../../version-helper" }
|
gst-plugin-version-helper = { path = "../../version-helper" }
|
||||||
|
@ -47,4 +48,4 @@ install_subdir = "gstreamer-1.0"
|
||||||
versioning = false
|
versioning = false
|
||||||
|
|
||||||
[package.metadata.capi.pkg_config]
|
[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"
|
||||||
|
|
|
@ -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:
|
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:
|
Feel free to contribute to this project. Some ways you can contribute are:
|
||||||
|
|
||||||
* Testing with more hardware and software and reporting bugs
|
* Testing with more hardware and software and reporting bugs
|
||||||
* Doing pull requests.
|
* 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
|
License
|
||||||
-------
|
-------
|
||||||
This plugin is licensed under the MPL-2 - see the [LICENSE](LICENSE-MPL-2.0) file for details
|
This plugin is licensed under the MPL-2 - see the [LICENSE](LICENSE-MPL-2.0) file for details
|
||||||
|
|
||||||
|
|
||||||
Acknowledgments
|
Acknowledgments
|
||||||
-------
|
-------
|
||||||
* University of the Arts London and The University of Manchester.
|
* University of the Arts London and The University of Manchester.
|
||||||
|
|
|
@ -72,6 +72,10 @@ impl DeviceProviderImpl for DeviceProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&self) -> Result<(), gst::LoggableError> {
|
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 mut thread_guard = self.thread.lock().unwrap();
|
||||||
let device_provider = self.instance();
|
let device_provider = self.instance();
|
||||||
if thread_guard.is_some() {
|
if thread_guard.is_some() {
|
||||||
|
|
|
@ -118,10 +118,6 @@ impl From<RecvColorFormat> for NDIlib_recv_color_format_e {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
if !ndi::initialize() {
|
|
||||||
return Err(glib::bool_error!("Cannot initialize NDI"));
|
|
||||||
}
|
|
||||||
|
|
||||||
device_provider::register(plugin)?;
|
device_provider::register(plugin)?;
|
||||||
|
|
||||||
ndisrc::register(plugin)?;
|
ndisrc::register(plugin)?;
|
||||||
|
|
|
@ -9,8 +9,8 @@ use std::ptr;
|
||||||
|
|
||||||
use byte_slice_cast::*;
|
use byte_slice_cast::*;
|
||||||
|
|
||||||
pub fn initialize() -> bool {
|
pub fn load() -> Result<(), glib::BoolError> {
|
||||||
unsafe { NDIlib_initialize() }
|
ndisys::load()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -167,6 +167,23 @@ impl ElementImpl for NdiSink {
|
||||||
|
|
||||||
PAD_TEMPLATES.as_ref()
|
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 {
|
impl BaseSinkImpl for NdiSink {
|
||||||
|
|
|
@ -370,6 +370,12 @@ impl ElementImpl for NdiSrc {
|
||||||
transition: gst::StateChange,
|
transition: gst::StateChange,
|
||||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
match transition {
|
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 => {
|
gst::StateChange::PausedToPlaying => {
|
||||||
if let Some(ref controller) = *self.receiver_controller.lock().unwrap() {
|
if let Some(ref controller) = *self.receiver_controller.lock().unwrap() {
|
||||||
controller.set_playing(true);
|
controller.set_playing(true);
|
||||||
|
|
|
@ -2,80 +2,66 @@
|
||||||
|
|
||||||
#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]
|
#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg(unix)]
|
||||||
all(target_arch = "x86_64", target_os = "windows"),
|
use libloading::os::unix::{Library, Symbol};
|
||||||
link(name = "Processing.NDI.Lib.x64")
|
#[cfg(windows)]
|
||||||
)]
|
use libloading::os::windows::{Library, Symbol};
|
||||||
#[cfg_attr(
|
|
||||||
all(target_arch = "x86", target_os = "windows"),
|
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
|
||||||
link(name = "Processing.NDI.Lib.x86")
|
const LIBRARY_NAME: &str = "Processing.NDI.Lib.x64.dll";
|
||||||
)]
|
#[cfg(all(target_arch = "x86", target_os = "windows"))]
|
||||||
#[cfg_attr(
|
const LIBRARY_NAME: &str = "Processing.NDI.Lib.x86.dll";
|
||||||
not(any(target_os = "windows", target_os = "macos")),
|
#[cfg(target_os = "linux")]
|
||||||
link(name = "ndi")
|
const LIBRARY_NAME: &str = "libndi.so.5";
|
||||||
)]
|
#[cfg(target_os = "macos")]
|
||||||
extern "C" {
|
const LIBRARY_NAME: &str = "libndi.dylib";
|
||||||
pub fn NDIlib_initialize() -> bool;
|
|
||||||
pub fn NDIlib_destroy();
|
struct FFI {
|
||||||
pub fn NDIlib_find_create_v2(
|
_library: Library,
|
||||||
p_create_settings: *const NDIlib_find_create_t,
|
initialize: Symbol<fn() -> bool>,
|
||||||
) -> NDIlib_find_instance_t;
|
destroy: Symbol<fn()>,
|
||||||
pub fn NDIlib_find_destroy(p_instance: NDIlib_find_instance_t);
|
find_create_v2:
|
||||||
pub fn NDIlib_find_wait_for_sources(
|
Symbol<fn(p_create_settings: *const NDIlib_find_create_t) -> NDIlib_find_instance_t>,
|
||||||
p_instance: NDIlib_find_instance_t,
|
find_destroy: Symbol<fn(p_instance: NDIlib_find_instance_t)>,
|
||||||
timeout_in_ms: u32,
|
find_wait_for_sources:
|
||||||
) -> bool;
|
Symbol<fn(p_instance: NDIlib_find_instance_t, timeout_in_ms: u32) -> bool>,
|
||||||
pub fn NDIlib_find_get_current_sources(
|
find_get_current_sources: Symbol<
|
||||||
p_instance: NDIlib_find_instance_t,
|
fn(p_instance: NDIlib_find_instance_t, p_no_sources: *mut u32) -> *const NDIlib_source_t,
|
||||||
p_no_sources: *mut u32,
|
>,
|
||||||
) -> *const NDIlib_source_t;
|
recv_create_v3:
|
||||||
pub fn NDIlib_recv_create_v3(
|
Symbol<fn(p_create_settings: *const NDIlib_recv_create_v3_t) -> NDIlib_recv_instance_t>,
|
||||||
p_create_settings: *const NDIlib_recv_create_v3_t,
|
recv_destroy: Symbol<fn(p_instance: NDIlib_recv_instance_t)>,
|
||||||
) -> NDIlib_recv_instance_t;
|
recv_set_tally:
|
||||||
pub fn NDIlib_recv_destroy(p_instance: NDIlib_recv_instance_t);
|
Symbol<fn(p_instance: NDIlib_recv_instance_t, p_tally: *const NDIlib_tally_t) -> bool>,
|
||||||
pub fn NDIlib_recv_set_tally(
|
recv_send_metadata: Symbol<
|
||||||
p_instance: NDIlib_recv_instance_t,
|
fn(p_instance: NDIlib_recv_instance_t, p_metadata: *const NDIlib_metadata_frame_t) -> bool,
|
||||||
p_tally: *const NDIlib_tally_t,
|
>,
|
||||||
) -> bool;
|
recv_capture_v3: Symbol<
|
||||||
pub fn NDIlib_recv_send_metadata(
|
fn(
|
||||||
p_instance: NDIlib_recv_instance_t,
|
p_instance: NDIlib_recv_instance_t,
|
||||||
p_metadata: *const NDIlib_metadata_frame_t,
|
p_video_data: *mut NDIlib_video_frame_v2_t,
|
||||||
) -> bool;
|
p_audio_data: *mut NDIlib_audio_frame_v3_t,
|
||||||
pub fn NDIlib_recv_capture_v3(
|
p_metadata: *mut NDIlib_metadata_frame_t,
|
||||||
p_instance: NDIlib_recv_instance_t,
|
timeout_in_ms: u32,
|
||||||
p_video_data: *mut NDIlib_video_frame_v2_t,
|
) -> NDIlib_frame_type_e,
|
||||||
p_audio_data: *mut NDIlib_audio_frame_v3_t,
|
>,
|
||||||
p_metadata: *mut NDIlib_metadata_frame_t,
|
recv_free_video_v2:
|
||||||
timeout_in_ms: u32,
|
Symbol<fn(p_instance: NDIlib_recv_instance_t, p_video_data: *mut NDIlib_video_frame_v2_t)>,
|
||||||
) -> NDIlib_frame_type_e;
|
recv_free_audio_v3:
|
||||||
pub fn NDIlib_recv_free_video_v2(
|
Symbol<fn(p_instance: NDIlib_recv_instance_t, p_audio_data: *mut NDIlib_audio_frame_v3_t)>,
|
||||||
p_instance: NDIlib_recv_instance_t,
|
recv_free_metadata:
|
||||||
p_video_data: *mut NDIlib_video_frame_v2_t,
|
Symbol<fn(p_instance: NDIlib_recv_instance_t, p_metadata: *mut NDIlib_metadata_frame_t)>,
|
||||||
);
|
recv_get_queue:
|
||||||
pub fn NDIlib_recv_free_audio_v3(
|
Symbol<fn(p_instance: NDIlib_recv_instance_t, p_total: *mut NDIlib_recv_queue_t)>,
|
||||||
p_instance: NDIlib_recv_instance_t,
|
send_create:
|
||||||
p_audio_data: *mut NDIlib_audio_frame_v3_t,
|
Symbol<fn(p_create_settings: *const NDIlib_send_create_t) -> NDIlib_send_instance_t>,
|
||||||
);
|
send_destroy: Symbol<fn(p_instance: NDIlib_send_instance_t)>,
|
||||||
pub fn NDIlib_recv_free_metadata(
|
send_send_video_v2: Symbol<
|
||||||
p_instance: NDIlib_recv_instance_t,
|
fn(p_instance: NDIlib_send_instance_t, p_video_data: *const NDIlib_video_frame_v2_t),
|
||||||
p_metadata: *mut NDIlib_metadata_frame_t,
|
>,
|
||||||
);
|
send_send_audio_v3: Symbol<
|
||||||
pub fn NDIlib_recv_get_queue(
|
fn(p_instance: NDIlib_send_instance_t, p_audio_data: *const NDIlib_audio_frame_v3_t),
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type NDIlib_find_instance_t = *mut ::std::os::raw::c_void;
|
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")]
|
#[cfg(feature = "advanced-sdk")]
|
||||||
pub const NDIlib_compressed_packet_version_0: u32 = 44;
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue