use std::ffi::c_void;
use std::mem::MaybeUninit;
use std::ptr::null_mut;
use std::time::Duration;
use nvfbc_sys::{
NVFBC_TOCUDA_FLAGS_NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT,
NVFBC_TOCUDA_FLAGS_NVFBC_TOCUDA_GRAB_FLAGS_NOFLAGS,
NVFBC_TOCUDA_FLAGS_NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY
};
use crate::{
BufferFormat,
CaptureType,
Error,
Status,
};
use crate::common::{
Handle,
check_ret,
create_capture_session,
create_handle,
destroy_capture_session,
destroy_handle,
status,
};
pub enum CaptureMethod {
NoWait = NVFBC_TOCUDA_FLAGS_NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT as isize,
NoWaitIfNewFrame = NVFBC_TOCUDA_FLAGS_NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY as isize,
Blocking = NVFBC_TOCUDA_FLAGS_NVFBC_TOCUDA_GRAB_FLAGS_NOFLAGS as isize,
}
#[derive(Copy, Clone)]
pub struct CudaFrameInfo {
pub device_buffer: usize,
pub device_buffer_len: u32,
pub width: u32,
pub height: u32,
pub current_frame: u32,
pub is_new_frame: bool,
}
impl std::fmt::Debug for CudaFrameInfo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("CudaFrameInfo")
.field("device_buffer", &(&self.device_buffer as *const usize))
.field("device_buffer_len", &self.device_buffer_len)
.field("width", &self.width)
.field("height", &self.height)
.field("current_frame", &self.current_frame)
.finish()
}
}
pub struct CudaCapturer {
handle: Handle,
}
impl CudaCapturer {
pub fn new() -> Result<Self, Error> {
Ok(Self { handle: create_handle()? })
}
pub fn status(&self) -> Result<Status, Error> {
status(self.handle)
}
pub fn start(&self, buffer_format: BufferFormat, fps: u32) -> Result<(), Error> {
create_capture_session(
self.handle,
CaptureType::SharedCuda,
std::time::Duration::from_millis(1000 / fps as u64),
)?;
let mut params: nvfbc_sys::NVFBC_TOCUDA_SETUP_PARAMS = unsafe { MaybeUninit::zeroed().assume_init() };
params.dwVersion = nvfbc_sys::NVFBC_TOCUDA_SETUP_PARAMS_VER;
params.eBufferFormat = buffer_format as u32;
check_ret(self.handle, unsafe { nvfbc_sys::NvFBCToCudaSetUp(self.handle, &mut params) })
}
pub fn stop(&self) -> Result<(), Error> {
destroy_capture_session(self.handle)
}
pub fn next_frame(&mut self, capture_method: CaptureMethod, timeout: Option<Duration>) -> Result<CudaFrameInfo, Error> {
let mut device_buffer: *mut c_void = null_mut();
let mut frame_info: nvfbc_sys::NVFBC_FRAME_GRAB_INFO = unsafe { MaybeUninit::zeroed().assume_init() };
let mut params: nvfbc_sys::NVFBC_TOCUDA_GRAB_FRAME_PARAMS = unsafe { MaybeUninit::zeroed().assume_init() };
params.dwVersion = nvfbc_sys::NVFBC_TOCUDA_GRAB_FRAME_PARAMS_VER;
params.dwFlags = capture_method as u32;
params.pFrameGrabInfo = &mut frame_info;
params.pCUDADeviceBuffer = &mut device_buffer as *mut _ as *mut c_void;
if let Some(timeout) = timeout {
params.dwTimeoutMs = timeout.as_millis() as u32;
}
check_ret(self.handle, unsafe { nvfbc_sys::NvFBCToCudaGrabFrame(self.handle, &mut params) })?;
Ok(CudaFrameInfo {
device_buffer: device_buffer as usize,
device_buffer_len: frame_info.dwByteSize,
width: frame_info.dwWidth,
height: frame_info.dwHeight,
current_frame: frame_info.dwCurrentFrame,
is_new_frame: frame_info.bIsNewFrame != 0,
})
}
pub fn release_context(&self) -> Result<(), Error> {
let mut params: nvfbc_sys::NVFBC_RELEASE_CONTEXT_PARAMS = unsafe { MaybeUninit::zeroed().assume_init() };
params.dwVersion = nvfbc_sys::NVFBC_RELEASE_CONTEXT_PARAMS_VER;
check_ret(
self.handle,
unsafe { nvfbc_sys::NvFBCReleaseContext(self.handle, &mut params) }
)
}
pub fn bind_context(&self) -> Result<(), Error> {
let mut params: nvfbc_sys::NVFBC_BIND_CONTEXT_PARAMS = unsafe { MaybeUninit::zeroed().assume_init() };
params.dwVersion = nvfbc_sys::NVFBC_BIND_CONTEXT_PARAMS_VER;
check_ret(
self.handle,
unsafe { nvfbc_sys::NvFBCBindContext(self.handle, &mut params) }
)
}
}
impl Drop for CudaCapturer {
fn drop(&mut self) {
destroy_handle(self.handle).ok();
}
}