use num::ToPrimitive;
use std::error::Error;
use std::fmt::Debug;
use std::{ffi::CStr, sync::Arc};
use num::Rational32;
pub(crate) use crate::bindings::NDIlib_video_frame_v2_t as NDIRawVideoFrame;
use crate::receiver::RawReceiver;
use crate::{
bindings,
buffer_info::BufferInfo,
enums::NDIFieldedFrameMode,
four_cc::{BufferInfoError, FourCC, FourCCVideo},
resolution::Resolution,
sender::RawSender,
timecode::NDITime,
};
use super::{NDIFrame, RawBufferManagement, RawFrame, drop_guard::FrameDataDropGuard};
impl RawBufferManagement for NDIRawVideoFrame {
#[inline]
unsafe fn drop_with_recv(&mut self, recv: &Arc<RawReceiver>) {
unsafe { bindings::NDIlib_recv_free_video_v2(recv.raw_ptr(), self) }
}
#[inline]
unsafe fn drop_with_sender(&mut self, _sender: &Arc<RawSender>) {
panic!(
"NDIRawVideoFrame cannot be dropped with a sender as it cannot be received by the sender."
)
}
fn assert_unwritten(&self) {
assert!(
self.p_data.is_null(),
"[Fatal FFI Error] NDIRawVideoFrame data is not null, but should be."
);
assert!(
self.p_metadata.is_null(),
"[Fatal FFI Error] NDIRawVideoFrame metadata is not null, but should be."
);
}
}
unsafe impl Send for NDIRawVideoFrame {}
unsafe impl Sync for NDIRawVideoFrame {}
impl RawFrame for NDIRawVideoFrame {}
pub type VideoFrame = NDIFrame<NDIRawVideoFrame>;
impl Default for VideoFrame {
fn default() -> Self {
Self::new()
}
}
impl VideoFrame {
pub fn new() -> Self {
let raw = NDIRawVideoFrame {
xres: 0,
yres: 0,
FourCC: FourCCVideo::UYVY.to_ffi(),
frame_rate_N: 30_000,
frame_rate_D: 1001,
picture_aspect_ratio: 0.0,
frame_format_type: NDIFieldedFrameMode::Progressive.to_ffi(),
timecode: bindings::NDIlib_send_timecode_synthesize,
p_metadata: std::ptr::null_mut(),
p_data: std::ptr::null_mut(),
__bindgen_anon_1: bindings::NDIlib_video_frame_v2_t__bindgen_ty_1 {
line_stride_in_bytes: 0,
},
timestamp: 0,
};
Self {
raw,
alloc: FrameDataDropGuard::NullPtr,
custom_state: (),
}
}
pub fn buffer_info(&self) -> Result<BufferInfo, BufferInfoError> {
if let Some(cc) = self.four_cc() {
cc.buffer_info(self.resolution(), self.field_mode())
} else {
Err(BufferInfoError::UnspecifiedFourCC)
}
}
pub fn try_alloc(&mut self) -> Result<(), VideoFrameAllocationError> {
if self.is_allocated() {
Err(VideoFrameAllocationError::AlreadyAllocated)?;
}
let info = self
.buffer_info()
.map_err(VideoFrameAllocationError::BufferInfoError)?;
let (alloc, ptr) = FrameDataDropGuard::new_boxed(info.size);
self.alloc = alloc;
self.raw.p_data = ptr;
self.raw.__bindgen_anon_1.line_stride_in_bytes = info.line_stride as i32;
Ok(())
}
pub fn alloc(&mut self) {
self.try_alloc().unwrap();
}
pub fn dealloc(&mut self) {
let drops_metadata = self.alloc.is_from_sdk();
unsafe { self.alloc.drop_buffer(&mut self.raw) };
self.raw.p_data = std::ptr::null_mut();
self.raw.__bindgen_anon_1.line_stride_in_bytes = -1;
if drops_metadata {
self.raw.p_metadata = std::ptr::null_mut();
}
}
pub fn video_data(&self) -> Result<(&[u8], BufferInfo), VideoFrameAccessError> {
if !self.is_allocated() {
Err(VideoFrameAccessError::NotAllocated)?;
}
let info = self
.buffer_info()
.map_err(VideoFrameAccessError::BufferInfoError)?;
assert_eq!(
info.line_stride,
self.lib_stride() as usize,
"[Fatal FFI Error] Stride mismatch"
);
assert!(
!self.raw.p_data.is_null(),
"[Invariant Error] data pointer does not match allocation"
);
Ok((
unsafe { std::slice::from_raw_parts(self.raw.p_data, info.size) },
info,
))
}
pub fn video_data_mut(&mut self) -> Result<(&mut [u8], BufferInfo), VideoFrameAccessError> {
if !self.is_allocated() {
Err(VideoFrameAccessError::NotAllocated)?;
}
if !self.alloc.is_mut() {
Err(VideoFrameAccessError::Readonly)?;
}
let info = self
.buffer_info()
.map_err(VideoFrameAccessError::BufferInfoError)?;
assert_eq!(
info.line_stride,
self.lib_stride() as usize,
"[Fatal FFI Error] Stride mismatch"
);
assert!(
!self.raw.p_data.is_null(),
"[Invariant Error] data pointer does not match allocation"
);
Ok((
unsafe { std::slice::from_raw_parts_mut(self.raw.p_data, info.size) },
info,
))
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VideoFrameAllocationError {
AlreadyAllocated,
BufferInfoError(BufferInfoError),
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VideoFrameAccessError {
NotAllocated,
Readonly,
BufferInfoError(BufferInfoError),
}
impl std::fmt::Display for VideoFrameAllocationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::AlreadyAllocated => f.write_str("Frame already allocated"),
Self::BufferInfoError(buffer_info_error) => {
write!(f, "Obtaining framebuffer info failed: {buffer_info_error}")
}
}
}
}
impl Error for VideoFrameAllocationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::AlreadyAllocated => None,
Self::BufferInfoError(buffer_info_error) => Some(buffer_info_error),
}
}
}
impl std::fmt::Display for VideoFrameAccessError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BufferInfoError(buffer_info_error) => {
write!(f, "Obtaining framebuffer info failed: {buffer_info_error}")
}
Self::NotAllocated => f.write_str("No framebuffer is allocated"),
Self::Readonly => f.write_str("Framebuffer is read-only"),
}
}
}
impl Error for VideoFrameAccessError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::BufferInfoError(buffer_info_error) => Some(buffer_info_error),
Self::NotAllocated | Self::Readonly => None,
}
}
}
impl VideoFrame {
pub fn resolution(&self) -> Resolution {
Resolution::from_i32(self.raw.xres, self.raw.yres)
}
pub fn set_resolution(&mut self, resolution: Resolution) -> Result<(), AlreadyAllocatedError> {
if self.is_allocated() {
Err(AlreadyAllocatedError {})
} else {
(self.raw.xres, self.raw.yres) = resolution.to_i32();
self.raw.picture_aspect_ratio = 0.0;
Ok(())
}
}
pub fn four_cc(&self) -> Option<FourCCVideo> {
FourCCVideo::from_ffi(self.raw.FourCC)
}
pub fn raw_four_cc(&self) -> FourCC {
FourCC::from_ffi(self.raw.FourCC as i32)
}
pub fn set_four_cc(&mut self, four_cc: FourCCVideo) -> Result<(), AlreadyAllocatedError> {
if self.is_allocated() {
Err(AlreadyAllocatedError {})
} else {
self.raw.FourCC = four_cc.to_ffi();
Ok(())
}
}
pub fn frame_rate(&self) -> Rational32 {
Rational32::new_raw(self.raw.frame_rate_N, self.raw.frame_rate_D)
}
pub fn set_frame_rate(&mut self, frame_rate: Rational32) {
self.raw.frame_rate_N = *frame_rate.numer();
self.raw.frame_rate_D = *frame_rate.denom();
}
pub fn metadata(&self) -> Option<&CStr> {
if self.raw.p_metadata.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(self.raw.p_metadata) })
}
}
pub fn field_mode(&self) -> NDIFieldedFrameMode {
NDIFieldedFrameMode::from_ffi(self.raw.frame_format_type)
.expect("[Fatal FFI Error] Invalid frame format type")
}
pub fn set_frame_format(
&mut self,
frame_format: NDIFieldedFrameMode,
) -> Result<(), AlreadyAllocatedError> {
if self.is_allocated() {
Err(AlreadyAllocatedError {})
} else {
self.raw.frame_format_type = frame_format.to_ffi();
Ok(())
}
}
pub fn send_time(&self) -> NDITime {
NDITime::from_ffi(self.raw.timecode)
}
pub fn set_send_time(&mut self, time: NDITime) {
self.raw.timecode = time.to_ffi();
}
pub fn recv_time(&self) -> NDITime {
NDITime::from_ffi(self.raw.timestamp)
}
pub fn set_recv_time(&mut self, time: NDITime) {
self.raw.timestamp = time.to_ffi();
}
fn lib_stride(&self) -> i32 {
unsafe { self.raw.__bindgen_anon_1.line_stride_in_bytes }
}
}
#[derive(Debug, Clone)]
pub struct AlreadyAllocatedError {}
impl std::fmt::Display for AlreadyAllocatedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Framebuffer already allocated")
}
}
impl Error for AlreadyAllocatedError {}
#[cfg(feature = "dangerous_apis")]
impl VideoFrame {
pub unsafe fn force_set_resolution(&mut self, resolution: Resolution) {
(self.raw.xres, self.raw.yres) = resolution.to_i32();
self.raw.picture_aspect_ratio = resolution.aspect_ratio() as f32;
}
pub unsafe fn force_set_four_cc(&mut self, four_cc: FourCCVideo) {
self.raw.FourCC = four_cc.to_ffi();
}
pub unsafe fn force_set_raw_four_cc(&mut self, four_cc: FourCC) {
self.raw.FourCC = four_cc.to_ffi();
}
pub unsafe fn force_set_frame_format(&mut self, frame_format: NDIFieldedFrameMode) {
self.raw.frame_format_type = frame_format.to_ffi();
}
pub unsafe fn set_lib_stride(&mut self, stride: i32) {
self.raw.__bindgen_anon_1.line_stride_in_bytes = stride;
}
}
impl Debug for VideoFrame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "VideoFrame {{ ")?;
write!(f, "resolution: {}x{}, ", self.raw.xres, self.raw.yres)?;
write!(
f,
"frame rate: {:.2}fps, ",
self.frame_rate().to_f64().unwrap_or(-1.)
)?;
if let Some(cc) = self.four_cc() {
write!(f, "FourCC: {:?}, ", cc)?;
} else {
write!(f, "FourCC: {:#x}, ", self.raw.FourCC)?;
}
write!(f, "format: {:?}, ", self.field_mode())?;
write!(f, "stride: {}, ", self.lib_stride())?;
write!(f, "metadata: {:?}, ", self.metadata())?;
write!(
f,
"timing: send={:?} recv={:?}, ",
self.send_time(),
self.recv_time()
)?;
write!(f, "alloc: {:?} @ {:?} }}", self.raw.p_data, self.alloc)
}
}