1use num::ToPrimitive;
2use std::fmt::Debug;
3use std::{ffi::CStr, sync::Arc};
4
5use num::Rational32;
6
7pub(crate) use crate::bindings::NDIlib_video_frame_v2_t as NDIRawVideoFrame;
8use crate::receiver::RawReceiver;
9use crate::{
10 bindings,
11 buffer_info::BufferInfo,
12 enums::NDIFieldedFrameMode,
13 four_cc::{BufferInfoError, FourCC, FourCCVideo},
14 resolution::Resolution,
15 sender::RawSender,
16 timecode::NDITime,
17};
18
19use super::{NDIFrame, RawBufferManagement, RawFrame, drop_guard::FrameDataDropGuard};
20
21impl RawBufferManagement for NDIRawVideoFrame {
22 #[inline]
23 unsafe fn drop_with_recv(&mut self, recv: &Arc<RawReceiver>) {
24 unsafe { bindings::NDIlib_recv_free_video_v2(recv.raw_ptr(), self) }
25 }
26
27 #[inline]
28 unsafe fn drop_with_sender(&mut self, _sender: &Arc<RawSender>) {
29 panic!(
30 "NDIRawVideoFrame cannot be dropped with a sender as it cannot be received by the sender."
31 )
32 }
33
34 fn assert_unwritten(&self) {
35 assert!(
36 self.p_data.is_null(),
37 "[Fatal FFI Error] NDIRawVideoFrame data is not null, but should be."
38 );
39 assert!(
40 self.p_metadata.is_null(),
41 "[Fatal FFI Error] NDIRawVideoFrame metadata is not null, but should be."
42 );
43 }
44}
45
46unsafe impl Send for NDIRawVideoFrame {}
47unsafe impl Sync for NDIRawVideoFrame {}
48
49impl RawFrame for NDIRawVideoFrame {}
50
51pub type VideoFrame = NDIFrame<NDIRawVideoFrame>;
55
56impl Default for VideoFrame {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62impl VideoFrame {
63 pub fn new() -> Self {
65 let raw = NDIRawVideoFrame {
66 xres: 0,
67 yres: 0,
68 FourCC: FourCCVideo::UYVY.to_ffi(),
69 frame_rate_N: 30_000,
70 frame_rate_D: 1001,
71 picture_aspect_ratio: 0.0,
72 frame_format_type: NDIFieldedFrameMode::Progressive.to_ffi(),
73 timecode: bindings::NDIlib_send_timecode_synthesize,
74 p_metadata: std::ptr::null_mut(),
75 p_data: std::ptr::null_mut(),
76 __bindgen_anon_1: bindings::NDIlib_video_frame_v2_t__bindgen_ty_1 {
77 line_stride_in_bytes: 0,
78 },
79 timestamp: 0,
80 };
81 Self {
82 raw,
83 alloc: FrameDataDropGuard::NullPtr,
84 custom_state: (),
85 }
86 }
87
88 pub fn buffer_info(&self) -> Result<BufferInfo, BufferInfoError> {
90 if let Some(cc) = self.four_cc() {
91 cc.buffer_info(self.resolution(), self.field_mode())
92 } else {
93 Err(BufferInfoError::UnspecifiedFourCC)
94 }
95 }
96
97 pub fn try_alloc(&mut self) -> Result<(), VideoFrameAllocationError> {
99 if self.is_allocated() {
100 Err(VideoFrameAllocationError::AlreadyAllocated)?;
101 }
102
103 let info = self
104 .buffer_info()
105 .map_err(VideoFrameAllocationError::BufferInfoError)?;
106
107 let (alloc, ptr) = FrameDataDropGuard::new_boxed(info.size);
108 self.alloc = alloc;
109 self.raw.p_data = ptr;
110 self.raw.__bindgen_anon_1.line_stride_in_bytes = info.line_stride as i32;
111
112 Ok(())
113 }
114
115 pub fn alloc(&mut self) {
117 self.try_alloc().unwrap();
118 }
119
120 pub fn dealloc(&mut self) {
122 let drops_metadata = self.alloc.is_from_sdk();
123 unsafe { self.alloc.drop_buffer(&mut self.raw) };
124 self.raw.p_data = std::ptr::null_mut();
125 self.raw.__bindgen_anon_1.line_stride_in_bytes = -1;
126 if drops_metadata {
127 self.raw.p_metadata = std::ptr::null_mut();
128 }
129 }
130
131 pub fn video_data(&self) -> Result<(&[u8], BufferInfo), VideoFrameAccessError> {
133 if !self.is_allocated() {
134 Err(VideoFrameAccessError::NotAllocated)?;
135 }
136
137 let info = self
138 .buffer_info()
139 .map_err(VideoFrameAccessError::BufferInfoError)?;
140
141 assert_eq!(
142 info.line_stride,
143 self.lib_stride() as usize,
144 "[Fatal FFI Error] Stride mismatch"
145 );
146
147 assert!(
148 !self.raw.p_data.is_null(),
149 "[Invariant Error] data pointer does not match allocation"
150 );
151 Ok((
152 unsafe { std::slice::from_raw_parts(self.raw.p_data, info.size) },
153 info,
154 ))
155 }
156
157 pub fn video_data_mut(&mut self) -> Result<(&mut [u8], BufferInfo), VideoFrameAccessError> {
159 if !self.is_allocated() {
160 Err(VideoFrameAccessError::NotAllocated)?;
161 }
162
163 if !self.alloc.is_mut() {
164 Err(VideoFrameAccessError::Readonly)?;
165 }
166
167 let info = self
168 .buffer_info()
169 .map_err(VideoFrameAccessError::BufferInfoError)?;
170
171 assert_eq!(
172 info.line_stride,
173 self.lib_stride() as usize,
174 "[Fatal FFI Error] Stride mismatch"
175 );
176
177 assert!(
178 !self.raw.p_data.is_null(),
179 "[Invariant Error] data pointer does not match allocation"
180 );
181
182 Ok((
183 unsafe { std::slice::from_raw_parts_mut(self.raw.p_data, info.size) },
184 info,
185 ))
186 }
187}
188
189#[non_exhaustive]
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191pub enum VideoFrameAllocationError {
192 AlreadyAllocated,
195 BufferInfoError(BufferInfoError),
197}
198
199#[non_exhaustive]
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
201pub enum VideoFrameAccessError {
202 NotAllocated,
204 Readonly,
207 BufferInfoError(BufferInfoError),
209}
210
211impl VideoFrame {
213 pub fn resolution(&self) -> Resolution {
215 Resolution::from_i32(self.raw.xres, self.raw.yres)
216 }
217
218 pub fn set_resolution(&mut self, resolution: Resolution) -> Result<(), AlreadyAllocatedError> {
221 if self.is_allocated() {
222 Err(AlreadyAllocatedError {})
223 } else {
224 (self.raw.xres, self.raw.yres) = resolution.to_i32();
225
226 self.raw.picture_aspect_ratio = 0.0;
231 Ok(())
232 }
233 }
234
235 pub fn four_cc(&self) -> Option<FourCCVideo> {
237 FourCCVideo::from_ffi(self.raw.FourCC)
238 }
239
240 pub fn raw_four_cc(&self) -> FourCC {
241 FourCC::from_ffi(self.raw.FourCC)
242 }
243
244 pub fn set_four_cc(&mut self, four_cc: FourCCVideo) -> Result<(), AlreadyAllocatedError> {
247 if self.is_allocated() {
248 Err(AlreadyAllocatedError {})
249 } else {
250 self.raw.FourCC = four_cc.to_ffi();
251 Ok(())
252 }
253 }
254
255 pub fn frame_rate(&self) -> Rational32 {
256 Rational32::new_raw(self.raw.frame_rate_N, self.raw.frame_rate_D)
257 }
258 pub fn set_frame_rate(&mut self, frame_rate: Rational32) {
259 self.raw.frame_rate_N = *frame_rate.numer();
260 self.raw.frame_rate_D = *frame_rate.denom();
261 }
262
263 pub fn metadata(&self) -> Option<&CStr> {
265 if self.raw.p_metadata.is_null() {
266 None
267 } else {
268 Some(unsafe { CStr::from_ptr(self.raw.p_metadata) })
269 }
270 }
271 pub fn field_mode(&self) -> NDIFieldedFrameMode {
275 NDIFieldedFrameMode::from_ffi(self.raw.frame_format_type)
276 .expect("[Fatal FFI Error] Invalid frame format type")
277 }
278 pub fn set_frame_format(
281 &mut self,
282 frame_format: NDIFieldedFrameMode,
283 ) -> Result<(), AlreadyAllocatedError> {
284 if self.is_allocated() {
285 Err(AlreadyAllocatedError {})
286 } else {
287 self.raw.frame_format_type = frame_format.to_ffi();
288 Ok(())
289 }
290 }
291
292 pub fn send_time(&self) -> NDITime {
293 NDITime::from_ffi(self.raw.timecode)
294 }
295 pub fn set_send_time(&mut self, time: NDITime) {
296 self.raw.timecode = time.to_ffi();
297 }
298
299 pub fn recv_time(&self) -> NDITime {
300 NDITime::from_ffi(self.raw.timestamp)
301 }
302 pub fn set_recv_time(&mut self, time: NDITime) {
303 self.raw.timestamp = time.to_ffi();
304 }
305
306 fn lib_stride(&self) -> i32 {
308 unsafe { self.raw.__bindgen_anon_1.line_stride_in_bytes }
309 }
310}
311
312#[derive(Debug, Clone)]
313pub struct AlreadyAllocatedError {}
314
315#[cfg(feature = "dangerous_apis")]
317impl VideoFrame {
318 pub unsafe fn force_set_resolution(&mut self, resolution: Resolution) {
326 (self.raw.xres, self.raw.yres) = resolution.to_i32();
327 self.raw.picture_aspect_ratio = resolution.aspect_ratio() as f32;
328 }
329
330 pub unsafe fn force_set_four_cc(&mut self, four_cc: FourCCVideo) {
338 self.raw.FourCC = four_cc.to_ffi();
339 }
340
341 pub unsafe fn force_set_raw_four_cc(&mut self, four_cc: FourCC) {
349 self.raw.FourCC = four_cc.to_ffi();
350 }
351
352 pub unsafe fn force_set_frame_format(&mut self, frame_format: NDIFieldedFrameMode) {
360 self.raw.frame_format_type = frame_format.to_ffi();
361 }
362
363 pub unsafe fn set_lib_stride(&mut self, stride: i32) {
370 self.raw.__bindgen_anon_1.line_stride_in_bytes = stride;
371 }
372}
373
374impl Debug for VideoFrame {
375 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376 write!(f, "VideoFrame {{ ")?;
377
378 write!(f, "resolution: {}x{}, ", self.raw.xres, self.raw.yres)?;
379
380 write!(
381 f,
382 "frame rate: {:.2}fps, ",
383 self.frame_rate().to_f64().unwrap_or(-1.)
384 )?;
385
386 if let Some(cc) = self.four_cc() {
387 write!(f, "FourCC: {:?}, ", cc)?;
388 } else {
389 write!(f, "FourCC: {:#x}, ", self.raw.FourCC)?;
390 }
391
392 write!(f, "format: {:?}, ", self.field_mode())?;
393
394 write!(f, "stride: {}, ", self.lib_stride())?;
395
396 write!(f, "metadata: {:?}, ", self.metadata())?;
397
398 write!(
399 f,
400 "timing: send={:?} recv={:?}, ",
401 self.send_time(),
402 self.recv_time()
403 )?;
404
405 write!(f, "alloc: {:?} @ {:?} }}", self.raw.p_data, self.alloc)
406 }
407}