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/rtpav1",
|
||||
"net/webrtc-http",
|
||||
"net/ndi",
|
||||
"text/ahead",
|
||||
"text/json",
|
||||
"text/regex",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -69,6 +69,7 @@ plugins = {
|
|||
'gst-plugin-tracers': 'libgstrstracers',
|
||||
'gst-plugin-webrtchttp': 'libgstwebrtchttp',
|
||||
'gst-plugin-rtpav1': 'libgstrtpav1',
|
||||
'gst-plugin-ndi': 'libgstndi',
|
||||
}
|
||||
|
||||
extra_env = {}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue