use std::{ffi::c_void, ptr, sync::Arc};
use cudarc::driver::CudaContext;
use super::{api::ENCODE_API, result::EncodeError, session::Session};
use crate::sys::nvEncodeAPI::{
GUID,
NVENCAPI_VERSION,
NV_ENC_BUFFER_FORMAT,
NV_ENC_CONFIG,
NV_ENC_CONFIG_VER,
NV_ENC_DEVICE_TYPE,
NV_ENC_INITIALIZE_PARAMS,
NV_ENC_INITIALIZE_PARAMS_VER,
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS,
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER,
NV_ENC_PRESET_CONFIG,
NV_ENC_PRESET_CONFIG_VER,
NV_ENC_TUNING_INFO,
};
#[derive(Debug)]
pub struct Encoder {
pub(crate) ptr: *mut c_void,
pub(crate) ctx: Arc<CudaContext>,
}
impl Drop for Encoder {
fn drop(&mut self) {
unsafe { (ENCODE_API.destroy_encoder)(self.ptr) }
.result(self)
.expect("The encoder pointer should be valid.");
}
}
impl Encoder {
pub fn initialize_with_cuda(cuda_ctx: Arc<CudaContext>) -> Result<Self, EncodeError> {
let mut encoder = ptr::null_mut();
let mut session_params = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS {
version: NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER,
deviceType: NV_ENC_DEVICE_TYPE::NV_ENC_DEVICE_TYPE_CUDA,
apiVersion: NVENCAPI_VERSION,
device: cuda_ctx.cu_ctx().cast::<c_void>(),
..Default::default()
};
if let err @ Err(_) =
unsafe { (ENCODE_API.open_encode_session_ex)(&mut session_params, &mut encoder) }
.result_without_string()
{
unsafe { (ENCODE_API.destroy_encoder)(encoder) }.result_without_string()?;
err?;
}
Ok(Self {
ptr: encoder,
ctx: cuda_ctx,
})
}
pub fn get_encode_guids(&self) -> Result<Vec<GUID>, EncodeError> {
let mut supported_count = 0;
unsafe { (ENCODE_API.get_encode_guid_count)(self.ptr, &mut supported_count) }
.result(self)?;
let mut encode_guids = vec![GUID::default(); supported_count as usize];
let mut actual_count = 0;
unsafe {
(ENCODE_API.get_encode_guids)(
self.ptr,
encode_guids.as_mut_ptr(),
supported_count,
&mut actual_count,
)
}
.result(self)?;
encode_guids.truncate(actual_count as usize);
Ok(encode_guids)
}
pub fn get_preset_guids(&self, encode_guid: GUID) -> Result<Vec<GUID>, EncodeError> {
let mut preset_count = 0;
unsafe { (ENCODE_API.get_encode_preset_count)(self.ptr, encode_guid, &mut preset_count) }
.result(self)?;
let mut actual_count = 0;
let mut preset_guids = vec![GUID::default(); preset_count as usize];
unsafe {
(ENCODE_API.get_encode_preset_guids)(
self.ptr,
encode_guid,
preset_guids.as_mut_ptr(),
preset_count,
&mut actual_count,
)
}
.result(self)?;
preset_guids.truncate(actual_count as usize);
Ok(preset_guids)
}
pub fn get_profile_guids(&self, encode_guid: GUID) -> Result<Vec<GUID>, EncodeError> {
let mut profile_count = 0;
unsafe {
(ENCODE_API.get_encode_profile_guid_count)(self.ptr, encode_guid, &mut profile_count)
}
.result(self)?;
let mut profile_guids = vec![GUID::default(); profile_count as usize];
let mut actual_count = 0;
unsafe {
(ENCODE_API.get_encode_profile_guids)(
self.ptr,
encode_guid,
profile_guids.as_mut_ptr(),
profile_count,
&mut actual_count,
)
}
.result(self)?;
profile_guids.truncate(actual_count as usize);
Ok(profile_guids)
}
pub fn get_supported_input_formats(
&self,
encode_guid: GUID,
) -> Result<Vec<NV_ENC_BUFFER_FORMAT>, EncodeError> {
let mut format_count = 0;
unsafe { (ENCODE_API.get_input_format_count)(self.ptr, encode_guid, &mut format_count) }
.result(self)?;
let mut supported_input_formats =
vec![NV_ENC_BUFFER_FORMAT::NV_ENC_BUFFER_FORMAT_UNDEFINED; format_count as usize];
let mut actual_count = 0;
unsafe {
(ENCODE_API.get_input_formats)(
self.ptr,
encode_guid,
supported_input_formats.as_mut_ptr(),
format_count,
&mut actual_count,
)
}
.result(self)?;
supported_input_formats.truncate(actual_count as usize);
Ok(supported_input_formats)
}
pub fn get_preset_config(
&self,
encode_guid: GUID,
preset_guid: GUID,
tuning_info: NV_ENC_TUNING_INFO,
) -> Result<NV_ENC_PRESET_CONFIG, EncodeError> {
let mut preset_config = NV_ENC_PRESET_CONFIG {
version: NV_ENC_PRESET_CONFIG_VER,
presetCfg: NV_ENC_CONFIG {
version: NV_ENC_CONFIG_VER,
..Default::default()
},
..Default::default()
};
unsafe {
(ENCODE_API.get_encode_preset_config_ex)(
self.ptr,
encode_guid,
preset_guid,
tuning_info,
&mut preset_config,
)
}
.result(self)?;
Ok(preset_config)
}
pub fn start_session(
self,
buffer_format: NV_ENC_BUFFER_FORMAT,
mut initialize_params: EncoderInitParams<'_>,
) -> Result<Session, EncodeError> {
let initialize_params = &mut initialize_params.param;
let width = initialize_params.encodeWidth;
let height = initialize_params.encodeHeight;
unsafe { (ENCODE_API.initialize_encoder)(self.ptr, initialize_params) }.result(&self)?;
Ok(Session {
encoder: self,
width,
height,
buffer_format,
encode_guid: initialize_params.encodeGUID,
})
}
}
#[derive(Debug)]
pub struct EncoderInitParams<'a> {
param: NV_ENC_INITIALIZE_PARAMS,
marker: std::marker::PhantomData<&'a mut NV_ENC_CONFIG>,
}
impl<'a> EncoderInitParams<'a> {
#[must_use]
pub fn new(encode_guid: GUID, width: u32, height: u32) -> Self {
let param = NV_ENC_INITIALIZE_PARAMS {
version: NV_ENC_INITIALIZE_PARAMS_VER,
encodeGUID: encode_guid,
encodeWidth: width,
encodeHeight: height,
..Default::default()
};
Self {
param,
marker: std::marker::PhantomData,
}
}
pub fn preset_guid(&mut self, preset_guid: GUID) -> &mut Self {
self.param.presetGUID = preset_guid;
self
}
pub fn tuning_info(&mut self, tuning_info: NV_ENC_TUNING_INFO) -> &mut Self {
self.param.tuningInfo = tuning_info;
self
}
pub fn encode_config(&mut self, encode_config: &'a mut NV_ENC_CONFIG) -> &mut Self {
self.param.encodeConfig = encode_config;
self
}
pub fn display_aspect_ratio(&mut self, width: u32, height: u32) -> &mut Self {
self.param.darWidth = width;
self.param.darHeight = height;
self
}
pub fn framerate(&mut self, numerator: u32, denominator: u32) -> &mut Self {
self.param.frameRateNum = numerator;
self.param.frameRateDen = denominator;
self
}
pub fn enable_picture_type_decision(&mut self) -> &mut Self {
self.param.enablePTD = 1;
self
}
}