1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
//! Defines a wrapper around
//! [`NVENCSTATUS`](crate::sys::nvEncodeAPI::NVENCSTATUS) to provide ergonomic
//! error handling.
use std::{error::Error, ffi::CStr, fmt};
use super::{api::ENCODE_API, encoder::Encoder};
use crate::sys::nvEncodeAPI::NVENCSTATUS;
/// Wrapper enum around [`NVENCSTATUS`].
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorKind {
/// No encode capable devices were detected.
NoEncodeDevice = 1,
/// The device passed by the client is not supported.
UnsupportedDevice = 2,
/// The encoder device supplied by the client is not valid.
InvalidEncoderDevice = 3,
/// The device passed to the API call is invalid.
InvalidDevice = 4,
/// The device passed to the API call is no longer available
/// and needs to be reinitialized. The clients need to destroy the
/// current encoder session by freeing the allocated input output
/// buffers and destroying the device and create a new encoding session.
DeviceNotExist = 5,
/// One or more of the pointers passed to the API call is invalid.
InvalidPtr = 6,
/// The completion event passed in the [`EncodeAPI.encode_picture`]
/// call is invalid.
InvalidEvent = 7,
/// One or more of the parameter passed to the API call is invalid.
InvalidParam = 8,
/// An API call was made in wrong sequence or order.
InvalidCall = 9,
/// the API call failed because it was unable to allocate enough memory
/// to perform the requested operation.
OutOfMemory = 10,
/// The encoder has not been initialized with
/// [`EncodeAPI.initialize_encoder`] or that initialization has failed.
/// The client cannot allocate input or output buffers or do any encoding
/// related operation before successfully initializing the encoder.
EncoderNotInitialized = 11,
/// An unsupported parameter was passed by the client.
UnsupportedParam = 12,
/// The [`EncodeAPI.lock_bitstream`] failed to lock the output
/// buffer. This happens when the client makes a non-blocking lock call
/// to access the output bitstream by passing the `doNotWait` flag.
/// This is not a fatal error and client should retry the same operation
/// after few milliseconds.
LockBusy = 13,
/// The size of the user buffer passed by the client is insufficient for
/// the requested operation.
NotEnoughBuffer = 14,
/// An invalid struct version was used by the client.
InvalidVersion = 15,
/// [`EncodeAPI.map_input_resource`] failed to map the client provided
/// input resource.
MapFailed = 16,
/// The encode driver requires more input buffers to produce an output
/// bitstream. If this error is returned from [`EncodeAPI.encode_picture`],
/// this is not a fatal error. If the client is encoding with B frames
/// then, [`EncodeAPI.encode_picture`] might be buffering the input
/// frame for re-ordering.
///
/// A client operating in synchronous mode cannot call
/// [`EncodeAPI.lock_bitstream`] on the output bitstream buffer if
/// [`EncodeAPI.encode_picture`] returned this variant. The client must
/// continue providing input frames until encode driver returns
/// successfully. After a success the client
/// can call [`EncodeAPI.lock_bitstream`] on the output buffers in the
/// same order in which it has called [`EncodeAPI.encode_picture`].
NeedMoreInput = 17,
/// The hardware encoder is busy encoding and is unable to encode
/// the input. The client should call [EncodeAPI.encode_picture] again after
/// few milliseconds.
EncoderBusy = 18,
/// The completion event passed in [`EncodeAPI.encode_picture`]
/// has not been registered with encoder driver using
/// [`EncodeAPI.register_async_event`].
EventNotRegistered = 19,
/// An unknown internal error has occurred.
Generic = 20,
/// The client is attempting to use a feature
/// that is not available for the license type for the current system.
IncompatibleClientKey = 21,
/// the client is attempting to use a feature
/// that is not implemented for the current version.
Unimplemented = 22,
/// [`EncodeAPI.register_resource`] failed to register the resource.
ResourceRegisterFailed = 23,
/// The client is attempting to unregister a resource
/// that has not been successfully registered.
ResourceNotRegistered = 24,
/// The client is attempting to unmap a resource
/// that has not been successfully mapped.
ResourceNotMapped = 25,
/// The encode driver requires more output buffers to write an
/// output bitstream. If this error is returned from
/// [`EncodeAPI.restore_encoder_state`], this is not a fatal error. If the
/// client is encoding with B frames then,
/// [`EncodeAPI.restore_encoder_state`] API might be requiring the extra
/// output buffer for accommodating overlay frame output in a separate
/// buffer, for AV1 codec. In this case, the client must call
/// [`EncodeAPI.restore_encoder_state`] API again with
/// an output bitstream as input along with the parameters in the previous
/// call. When operating in asynchronous mode of encoding, client must
/// also specify the completion event.
NeedMoreOutput = 26,
}
/// Wrapper struct around [`NVENCSTATUS`].
///
/// This struct also contains a string with additional info
/// when it is relevant and available.
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct EncodeError {
kind: ErrorKind,
string: Option<String>,
}
impl EncodeError {
/// Getter for the error kind.
#[must_use]
pub fn kind(&self) -> ErrorKind {
self.kind
}
/// Getter for the error string.
#[must_use]
pub fn string(&self) -> Option<&str> {
self.string.as_deref()
}
}
impl fmt::Display for EncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.string {
Some(s) => write!(f, "{:?}: {s}", self.kind),
None => write!(f, "{:?}", self.kind),
}
}
}
impl Error for EncodeError {}
impl From<NVENCSTATUS> for ErrorKind {
fn from(status: NVENCSTATUS) -> Self {
match status {
NVENCSTATUS::NV_ENC_SUCCESS => {
unreachable!("Success should not be converted to an error.")
}
NVENCSTATUS::NV_ENC_ERR_NO_ENCODE_DEVICE => Self::NoEncodeDevice,
NVENCSTATUS::NV_ENC_ERR_UNSUPPORTED_DEVICE => Self::UnsupportedDevice,
NVENCSTATUS::NV_ENC_ERR_INVALID_ENCODERDEVICE => Self::InvalidEncoderDevice,
NVENCSTATUS::NV_ENC_ERR_INVALID_DEVICE => Self::InvalidDevice,
NVENCSTATUS::NV_ENC_ERR_DEVICE_NOT_EXIST => Self::DeviceNotExist,
NVENCSTATUS::NV_ENC_ERR_INVALID_PTR => Self::InvalidPtr,
NVENCSTATUS::NV_ENC_ERR_INVALID_EVENT => Self::InvalidEvent,
NVENCSTATUS::NV_ENC_ERR_INVALID_PARAM => Self::InvalidParam,
NVENCSTATUS::NV_ENC_ERR_INVALID_CALL => Self::InvalidCall,
NVENCSTATUS::NV_ENC_ERR_OUT_OF_MEMORY => Self::OutOfMemory,
NVENCSTATUS::NV_ENC_ERR_ENCODER_NOT_INITIALIZED => Self::EncoderNotInitialized,
NVENCSTATUS::NV_ENC_ERR_UNSUPPORTED_PARAM => Self::UnsupportedParam,
NVENCSTATUS::NV_ENC_ERR_LOCK_BUSY => Self::LockBusy,
NVENCSTATUS::NV_ENC_ERR_NOT_ENOUGH_BUFFER => Self::NotEnoughBuffer,
NVENCSTATUS::NV_ENC_ERR_INVALID_VERSION => Self::InvalidVersion,
NVENCSTATUS::NV_ENC_ERR_MAP_FAILED => Self::MapFailed,
NVENCSTATUS::NV_ENC_ERR_NEED_MORE_INPUT => Self::NeedMoreInput,
NVENCSTATUS::NV_ENC_ERR_ENCODER_BUSY => Self::EncoderBusy,
NVENCSTATUS::NV_ENC_ERR_EVENT_NOT_REGISTERD => Self::EventNotRegistered,
NVENCSTATUS::NV_ENC_ERR_GENERIC => Self::Generic,
NVENCSTATUS::NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY => Self::IncompatibleClientKey,
NVENCSTATUS::NV_ENC_ERR_UNIMPLEMENTED => Self::Unimplemented,
NVENCSTATUS::NV_ENC_ERR_RESOURCE_REGISTER_FAILED => Self::ResourceRegisterFailed,
NVENCSTATUS::NV_ENC_ERR_RESOURCE_NOT_REGISTERED => Self::ResourceNotRegistered,
NVENCSTATUS::NV_ENC_ERR_RESOURCE_NOT_MAPPED => Self::ResourceNotMapped,
NVENCSTATUS::NV_ENC_ERR_NEED_MORE_OUTPUT => Self::NeedMoreOutput,
}
}
}
impl NVENCSTATUS {
/// Convert an [`NVENCSTATUS`] to a [`Result`].
///
/// [`NVENCSTATUS::NV_ENC_SUCCESS`] is converted to `Ok(())`,
/// and all other variants are mapped to the corresponding variant
/// in [`ErrorKind`]. The error type is [`EncodeError`] which has
/// a kind and an optional `String` which might contain additional
/// information about the error.
///
/// # Errors
///
/// Returns an error whenever the status is not
/// [`NVENCSTATUS::NV_ENC_SUCCESS`].
///
/// # Examples
///
/// ```
/// # use cudarc::driver::CudaDevice;
/// # use nvidia_video_codec_sdk::{sys::nvEncodeAPI::GUID, EncodeError, Encoder, ErrorKind};
/// # let cuda_device = CudaDevice::new(0).unwrap();
/// let encoder = Encoder::initialize_with_cuda(cuda_device).unwrap();
/// // Cause an error by passing in an invalid GUID.
/// // `Encoder::get_supported_input_formats()` uses `.result()` internally
/// let error = encoder
/// .get_supported_input_formats(GUID::default())
/// .unwrap_err();
/// // Get the kind.
/// assert_eq!(error.kind(), ErrorKind::InvalidParam);
/// // Get the error message.
/// // Unfortunately, it's not always helpful.
/// assert_eq!(error.string(), Some("EncodeAPI Internal Error."));
/// ```
pub fn result(self, encoder: &Encoder) -> Result<(), EncodeError> {
self.result_without_string().map_err(|mut err| {
err.string = match err.kind {
// Avoid getting the string if it is not needed.
ErrorKind::LockBusy
| ErrorKind::EncoderBusy
| ErrorKind::NeedMoreInput
| ErrorKind::OutOfMemory => None,
// Otherwise allocate an owned `String` with the error.
_ => Some(
unsafe { CStr::from_ptr((ENCODE_API.get_last_error_string)(encoder.ptr)) }
.to_string_lossy()
.to_string(),
),
}
.and_then(|s| if s.is_empty() { None } else { Some(s) });
err
})
}
/// Convert an [`NVENCSTATUS`] to a [`Result`] without
/// using an [`Encoder`].
///
/// This function is the same as [`NVENCSTATUS::result`] except
/// it does not get the error string because it does not have access
/// to an [`Encoder`]. This is only useful if you do not have an [`Encoder`]
/// yet, for example when initializing the API.
///
/// You should always prefer to use [`NVENCSTATUS::result`] when possible.
///
/// # Errors
///
/// Returns an error whenever the status is not
/// [`NVENCSTATUS::NV_ENC_SUCCESS`].
pub fn result_without_string(self) -> Result<(), EncodeError> {
match self {
Self::NV_ENC_SUCCESS => Ok(()),
err => Err(EncodeError {
kind: err.into(),
string: None,
}),
}
}
}