#![warn(missing_docs)]
#![warn(unsafe_op_in_unsafe_fn)]
use internal::{bindings::*, OnDrop};
use std::{
convert::TryFrom,
ffi::{CStr, CString},
fmt::{Debug, Display},
ptr::{null, null_mut},
sync::Arc,
};
pub mod error;
pub mod find;
#[doc(hidden)]
pub mod internal;
pub mod recv;
pub mod send;
#[doc(hidden)]
pub use error::*;
#[doc(hidden)]
pub use find::*;
#[doc(hidden)]
pub use recv::*;
#[doc(hidden)]
pub use send::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameType {
None = NDIlib_frame_type_e_NDIlib_frame_type_none as _,
Video = NDIlib_frame_type_e_NDIlib_frame_type_video as _,
Audio = NDIlib_frame_type_e_NDIlib_frame_type_audio as _,
Metadata = NDIlib_frame_type_e_NDIlib_frame_type_metadata as _,
StatusChange = NDIlib_frame_type_e_NDIlib_frame_type_status_change as _,
ErrorFrame = NDIlib_frame_type_e_NDIlib_frame_type_error as _,
}
impl TryFrom<NDIlib_frame_type_e> for FrameType {
type Error = InvalidEnum;
fn try_from(value: NDIlib_frame_type_e) -> Result<Self, Self::Error> {
#[allow(non_upper_case_globals)]
match value {
NDIlib_frame_type_e_NDIlib_frame_type_audio => Ok(FrameType::Audio),
NDIlib_frame_type_e_NDIlib_frame_type_video => Ok(FrameType::Video),
NDIlib_frame_type_e_NDIlib_frame_type_none => Ok(FrameType::None),
NDIlib_frame_type_e_NDIlib_frame_type_status_change => Ok(FrameType::StatusChange),
NDIlib_frame_type_e_NDIlib_frame_type_error => Ok(FrameType::ErrorFrame),
NDIlib_frame_type_e_NDIlib_frame_type_metadata => Ok(FrameType::Metadata),
x => Err(InvalidEnum(x as _, "FrameType")),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameFormatType {
Progressive = NDIlib_frame_format_type_e_NDIlib_frame_format_type_progressive as _,
Interleaved = NDIlib_frame_format_type_e_NDIlib_frame_format_type_interleaved as _,
Field0 = NDIlib_frame_format_type_e_NDIlib_frame_format_type_field_0 as _,
Field1 = NDIlib_frame_format_type_e_NDIlib_frame_format_type_field_1 as _,
}
impl TryFrom<NDIlib_frame_format_type_e> for FrameFormatType {
type Error = InvalidEnum;
fn try_from(value: NDIlib_frame_format_type_e) -> Result<Self, Self::Error> {
#[allow(non_upper_case_globals)]
match value {
NDIlib_frame_format_type_e_NDIlib_frame_format_type_progressive => {
Ok(FrameFormatType::Progressive)
}
NDIlib_frame_format_type_e_NDIlib_frame_format_type_interleaved => {
Ok(FrameFormatType::Interleaved)
}
NDIlib_frame_format_type_e_NDIlib_frame_format_type_field_0 => {
Ok(FrameFormatType::Field0)
}
NDIlib_frame_format_type_e_NDIlib_frame_format_type_field_1 => {
Ok(FrameFormatType::Field1)
}
x => Err(InvalidEnum(x as _, "FrameFormatType")),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FourCCVideoType {
UYVY = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_UYVY as _,
UYVA = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_UYVA as _,
P216 = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_P216 as _,
PA16 = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_PA16 as _,
YV12 = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_YV12 as _,
I420 = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_I420 as _,
NV12 = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_NV12 as _,
BGRA = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_BGRA as _,
BGRX = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_BGRX as _,
RGBA = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_RGBA as _,
RGBX = NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_RGBX as _,
}
impl TryFrom<NDIlib_FourCC_video_type_e> for FourCCVideoType {
type Error = InvalidEnum;
fn try_from(value: NDIlib_FourCC_video_type_e) -> Result<Self, Self::Error> {
#[allow(non_upper_case_globals)]
match value {
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_UYVY => Ok(FourCCVideoType::UYVY),
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_UYVA => Ok(FourCCVideoType::UYVA),
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_P216 => Ok(FourCCVideoType::P216),
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_PA16 => Ok(FourCCVideoType::PA16),
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_YV12 => Ok(FourCCVideoType::YV12),
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_I420 => Ok(FourCCVideoType::I420),
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_NV12 => Ok(FourCCVideoType::NV12),
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_BGRA => Ok(FourCCVideoType::BGRA),
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_RGBA => Ok(FourCCVideoType::RGBA),
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_BGRX => Ok(FourCCVideoType::BGRX),
NDIlib_FourCC_video_type_e_NDIlib_FourCC_type_RGBX => Ok(FourCCVideoType::RGBX),
x => Err(InvalidEnum(x as _, "FourCCVideoType")),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum FourCCAudioType {
FLTP = NDIlib_FourCC_audio_type_e_NDIlib_FourCC_type_FLTP as _,
}
impl TryFrom<NDIlib_FourCC_audio_type_e> for FourCCAudioType {
type Error = InvalidEnum;
fn try_from(value: NDIlib_FourCC_audio_type_e) -> Result<Self, Self::Error> {
#[allow(non_upper_case_globals)]
match value {
NDIlib_FourCC_audio_type_e_NDIlib_FourCC_type_FLTP => Ok(FourCCAudioType::FLTP),
x => Err(InvalidEnum(x as _, "FourCCAudioType")),
}
}
}
#[derive(Clone)]
enum SourceParent {
Find(Arc<OnDrop<NDIlib_find_instance_t>>),
Send(Arc<OnDrop<NDIlib_send_instance_t>>),
None,
}
#[derive(Clone)]
pub struct Source {
p_instance: NDIlib_source_t,
parent: SourceParent,
}
impl Debug for Source {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ndi::Source")
.field("name", &self.get_name())
.finish()
}
}
impl Source {
fn from_binding(parent: SourceParent, source: NDIlib_source_t) -> Self {
Self {
parent,
p_instance: source,
}
}
fn new() -> Self {
let p_instance = NDIlib_source_t {
p_ndi_name: null(),
__bindgen_anon_1: NDIlib_source_t__bindgen_ty_1 {
p_ip_address: null(),
},
};
Self {
parent: SourceParent::None,
p_instance,
}
}
pub fn get_name(&self) -> String {
let name_char_ptr: *mut std::os::raw::c_char = self.p_instance.p_ndi_name as _;
if name_char_ptr.is_null() {
return String::new();
}
let name = unsafe {
CStr::from_ptr(name_char_ptr)
.to_owned()
.to_string_lossy()
.to_string()
};
name
}
}
unsafe impl core::marker::Send for Source {}
unsafe impl core::marker::Sync for Source {}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Tally {
pub on_program: bool,
pub on_preview: bool,
}
impl Tally {
pub fn new() -> Self {
Self {
on_program: false,
on_preview: false,
}
}
}
impl Default for Tally {
fn default() -> Self {
Self::new()
}
}
impl From<NDIlib_tally_t> for Tally {
fn from(x: NDIlib_tally_t) -> Self {
Self {
on_preview: x.on_preview,
on_program: x.on_program,
}
}
}
impl Into<NDIlib_tally_t> for Tally {
fn into(self) -> NDIlib_tally_t {
NDIlib_tally_t {
on_preview: self.on_preview,
on_program: self.on_program,
}
}
}
enum VideoParent {
Recv(Arc<OnDrop<NDIlib_recv_instance_t>>),
Owned,
}
pub struct VideoData {
p_instance: NDIlib_video_frame_v2_t,
parent: VideoParent,
}
unsafe impl core::marker::Send for VideoData {}
unsafe impl core::marker::Sync for VideoData {}
impl Debug for VideoData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VideoData")
.field("width", &self.width())
.field("height", &self.height())
.field("line_stride_in_bytes", &self.line_stride_in_bytes())
.field("data_size", &self.data_size_in_bytes())
.field("fourcc", &self.four_cc())
.field("frame_format_type", &self.frame_format_type())
.field(
"frame_rate",
&format!(
"{}/{} = {}",
self.frame_rate_n(),
self.frame_rate_d(),
self.frame_rate_n() as f32 / self.frame_rate_d() as f32
),
)
.field("timestamp", &self.timestamp())
.field("timecode", &self.timecode())
.field("metadata", &self.metadata())
.finish()
}
}
impl VideoData {
fn from_binding_recv(
recv: Arc<OnDrop<NDIlib_recv_instance_t>>,
p_instance: NDIlib_video_frame_v2_t,
) -> Self {
Self {
p_instance,
parent: VideoParent::Recv(recv),
}
}
pub fn new() -> Self {
Self {
p_instance: NDIlib_video_frame_v2_t {
xres: 0,
yres: 0,
FourCC: FourCCVideoType::UYVY as _,
frame_rate_N: 60,
frame_rate_D: 0,
picture_aspect_ratio: 0f32,
frame_format_type: FrameFormatType::Progressive as _,
timecode: 0,
p_data: null_mut(),
__bindgen_anon_1: NDIlib_video_frame_v2_t__bindgen_ty_1 {
line_stride_in_bytes: 0,
},
p_metadata: null(),
timestamp: 0,
},
parent: VideoParent::Owned,
}
}
pub fn from_buffer(
width: i32,
height: i32,
fourcc: FourCCVideoType,
framerate_numerator: i32,
framerate_denominator: i32,
frame_format: FrameFormatType,
timecode: i64,
stride: i32,
metadata: Option<&CStr>,
buffer: &mut [u8],
) -> Self {
Self {
p_instance: NDIlib_video_frame_v2_t {
xres: width,
yres: height,
FourCC: fourcc as _,
frame_rate_N: framerate_numerator,
frame_rate_D: framerate_denominator,
picture_aspect_ratio: width as f32 / height as f32,
frame_format_type: frame_format as _,
timecode,
p_data: buffer.as_mut_ptr(),
__bindgen_anon_1: NDIlib_video_frame_v2_t__bindgen_ty_1 {
line_stride_in_bytes: stride,
},
p_metadata: if let Some(metadata) = metadata {
metadata.as_ptr()
} else {
null()
},
timestamp: NDIlib_recv_timestamp_undefined,
},
parent: VideoParent::Owned,
}
}
pub fn width(&self) -> u32 {
self.p_instance.xres as _
}
pub fn height(&self) -> u32 {
self.p_instance.yres as _
}
pub fn four_cc(&self) -> FourCCVideoType {
FourCCVideoType::try_from(self.p_instance.FourCC).unwrap()
}
pub fn frame_rate_n(&self) -> u32 {
self.p_instance.frame_rate_N as _
}
pub fn frame_rate_d(&self) -> u32 {
self.p_instance.frame_rate_D as _
}
pub fn frame_rate(&self) -> f32 {
self.p_instance.frame_rate_N as f32 / self.p_instance.frame_rate_D as f32
}
pub fn picture_aspect_ratio(&self) -> f32 {
self.p_instance.picture_aspect_ratio
}
pub fn frame_format_type(&self) -> FrameFormatType {
FrameFormatType::try_from(self.p_instance.frame_format_type).unwrap()
}
pub fn timecode(&self) -> i64 {
self.p_instance.timecode
}
pub fn p_data(&self) -> *mut u8 {
self.p_instance.p_data
}
pub fn line_stride_in_bytes(&self) -> Option<u32> {
unsafe { Some(self.p_instance.__bindgen_anon_1.line_stride_in_bytes as _) }
}
pub fn data_size_in_bytes(&self) -> Option<u32> {
unsafe { Some(self.p_instance.__bindgen_anon_1.data_size_in_bytes as _) }
}
pub fn metadata(&self) -> String {
let metadata_char_ptr = self.p_instance.p_metadata;
if metadata_char_ptr.is_null() {
return String::new();
}
let metadata = unsafe { CStr::from_ptr(metadata_char_ptr) }
.to_owned()
.to_string_lossy()
.to_string();
metadata
}
pub fn timestamp(&self) -> Option<i64> {
let timestamp = self.p_instance.timestamp;
if timestamp == NDIlib_recv_timestamp_undefined {
None
} else {
Some(timestamp)
}
}
}
impl Drop for VideoData {
fn drop(&mut self) {
match &self.parent {
VideoParent::Recv(recv) => unsafe {
NDIlib_recv_free_video_v2(***recv, &mut self.p_instance);
},
VideoParent::Owned => {}
}
}
}
enum AudioParent {
Recv(Arc<OnDrop<NDIlib_recv_instance_t>>),
Owned,
}
pub struct AudioData {
p_instance: NDIlib_audio_frame_v3_t,
parent: AudioParent,
}
unsafe impl core::marker::Send for AudioData {}
unsafe impl core::marker::Sync for AudioData {}
impl Debug for AudioData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AudioData")
.field("sample_rate", &self.sample_rate())
.field("no_samples", &self.no_samples())
.field("no_channels", &self.no_channels())
.field("timestamp", &self.timestamp())
.field("timecode", &self.timecode())
.field("fourcc", &self.four_cc())
.field("metadata", &self.metadata())
.finish()
}
}
impl AudioData {
fn from_binding_recv(
recv: Arc<OnDrop<NDIlib_recv_instance_t>>,
p_instance: NDIlib_audio_frame_v3_t,
) -> Self {
Self {
p_instance,
parent: AudioParent::Recv(recv),
}
}
pub fn new() -> Self {
Self {
p_instance: NDIlib_audio_frame_v3_t {
sample_rate: 0,
no_channels: 0,
no_samples: 0,
timecode: 0,
FourCC: FourCCAudioType::FLTP as _,
p_data: null_mut(),
__bindgen_anon_1: NDIlib_audio_frame_v3_t__bindgen_ty_1 {
channel_stride_in_bytes: 0,
},
p_metadata: "".as_ptr() as _,
timestamp: 0,
},
parent: AudioParent::Owned,
}
}
pub fn sample_rate(&self) -> u32 {
self.p_instance.sample_rate as _
}
pub fn no_channels(&self) -> u32 {
self.p_instance.no_channels as _
}
pub fn no_samples(&self) -> u32 {
self.p_instance.no_samples as _
}
pub fn timecode(&self) -> i64 {
self.p_instance.timecode
}
pub fn timestamp(&self) -> Option<i64> {
let timestamp = self.p_instance.timestamp;
if timestamp == NDIlib_recv_timestamp_undefined {
None
} else {
Some(timestamp)
}
}
pub fn p_data(&self) -> *mut u8 {
self.p_instance.p_data
}
pub fn four_cc(&self) -> FourCCAudioType {
#[allow(non_upper_case_globals)]
match self.p_instance.FourCC {
NDIlib_FourCC_audio_type_e_NDIlib_FourCC_type_FLTP => FourCCAudioType::FLTP,
x => panic!("Unknown NDI FourCC Audio type encountered: {}", x),
}
}
pub fn channel_stride_in_bytes(&self) -> u32 {
match self.four_cc() {
FourCCAudioType::FLTP => unsafe {
self.p_instance.__bindgen_anon_1.channel_stride_in_bytes as _
},
}
}
pub fn metadata(&self) -> String {
let metadata_char_ptr = self.p_instance.p_metadata;
if metadata_char_ptr.is_null() {
return String::new();
}
let metadata = unsafe { CStr::from_ptr(metadata_char_ptr) }
.to_owned()
.to_string_lossy()
.to_string();
metadata
}
}
impl Drop for AudioData {
fn drop(&mut self) {
match &self.parent {
AudioParent::Recv(recv) => unsafe {
NDIlib_recv_free_audio_v3(***recv, &self.p_instance);
},
AudioParent::Owned => {}
}
}
}
enum MetaDataParent {
Recv(Arc<OnDrop<NDIlib_recv_instance_t>>),
Send(Arc<OnDrop<NDIlib_send_instance_t>>),
Owned,
}
pub struct MetaData {
p_instance: NDIlib_metadata_frame_t,
parent: MetaDataParent,
}
unsafe impl core::marker::Send for MetaData {}
unsafe impl core::marker::Sync for MetaData {}
impl Debug for MetaData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MetaData")
.field("length", &self.length())
.field("data", &self.data())
.field("timecode", &self.timecode())
.finish()
}
}
impl MetaData {
fn from_binding_recv(
recv: Arc<OnDrop<NDIlib_recv_instance_t>>,
p_instance: NDIlib_metadata_frame_t,
) -> Self {
Self {
p_instance,
parent: MetaDataParent::Recv(recv),
}
}
fn from_binding_send(
send: Arc<OnDrop<NDIlib_send_instance_t>>,
p_instance: NDIlib_metadata_frame_t,
) -> Self {
Self {
p_instance,
parent: MetaDataParent::Send(send),
}
}
pub fn new(length: u32, timecode: i64, data: String) -> Self {
let p_data = CString::new(data).unwrap().into_raw();
let p_instance = NDIlib_metadata_frame_t {
length: length as _,
timecode,
p_data,
};
Self {
p_instance,
parent: MetaDataParent::Owned,
}
}
pub fn length(&self) -> u32 {
self.p_instance.length as _
}
pub fn timecode(&self) -> i64 {
self.p_instance.timecode
}
pub fn data(&self) -> String {
let char_ptr = self.p_instance.p_data;
let data = unsafe { CStr::from_ptr(char_ptr).to_string_lossy().to_string() };
data
}
}
impl Drop for MetaData {
fn drop(&mut self) {
match &self.parent {
MetaDataParent::Recv(recv) => unsafe {
NDIlib_recv_free_metadata(***recv, &mut self.p_instance);
},
MetaDataParent::Send(send) => unsafe {
NDIlib_send_free_metadata(***send, &mut self.p_instance);
},
MetaDataParent::Owned => {}
}
}
}
pub fn initialize() -> Result<(), NotSupported> {
if !unsafe { NDIlib_initialize() } {
return Err(NotSupported);
};
Ok(())
}
pub unsafe fn cleanup() {
unsafe { NDIlib_destroy() };
}
#[allow(non_snake_case)]
pub fn is_supported_CPU() -> bool {
unsafe { NDIlib_is_supported_CPU() }
}