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