nvidia_video_codec_sdk/safe/encoder.rs
1//! The [`Encoder`] is the main entrypoint for the Encoder API.
2//!
3//! The [`Encoder`] provides a slightly higher-level abstraction over the
4//! encoder API. This module also defines builders for some of the parameter
5//! structs used by the interface.
6
7use std::{ffi::c_void, ptr, sync::Arc};
8
9use cudarc::driver::CudaContext;
10
11use super::{api::ENCODE_API, result::EncodeError, session::Session};
12use crate::sys::nvEncodeAPI::{
13 GUID,
14 NVENCAPI_VERSION,
15 NV_ENC_BUFFER_FORMAT,
16 NV_ENC_CONFIG,
17 NV_ENC_CONFIG_VER,
18 NV_ENC_DEVICE_TYPE,
19 NV_ENC_INITIALIZE_PARAMS,
20 NV_ENC_INITIALIZE_PARAMS_VER,
21 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS,
22 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER,
23 NV_ENC_PRESET_CONFIG,
24 NV_ENC_PRESET_CONFIG_VER,
25 NV_ENC_TUNING_INFO,
26};
27
28/// Entrypoint for the Encoder API.
29///
30/// The general usage follows these steps:
31/// 1. Initialize the encoder.
32/// 2. Set up the desired encoding parameters.
33/// 3. Allocate or register input and output buffers.
34/// 4. Copy frames to input buffers, encode, and read out of output bitstream.
35/// 5. Close the encoding session and clean up.
36///
37/// With this wrapper cleanup is performed automatically.
38/// To do the other steps this struct provides associated functions
39/// such as [`Encoder::get_encode_guids`] or
40/// [`Encoder::get_supported_input_formats`].
41///
42/// Once the configuration is completed, a session should be initialized with
43/// [`Encoder::start_session`] to get a [`Session`].
44/// This type has further function to create input and output buffers
45/// and encode pictures.
46///
47/// See [NVIDIA Video Codec SDK - Video Encoder API Programming Guide](https://docs.nvidia.com/video-technologies/video-codec-sdk/12.0/nvenc-video-encoder-api-prog-guide/index.html).
48#[derive(Debug)]
49pub struct Encoder {
50 pub(crate) ptr: *mut c_void,
51 // Used to fetch the device pointer for an externally allocated buffer
52 pub(crate) ctx: Arc<CudaContext>,
53}
54
55/// The client must flush the encoder before freeing any resources.
56/// Do this by sending an EOS encode frame.
57/// (This is also done automatically when [`Session`] is dropped.).
58///
59/// Sending an EOS frame might still generate data, so if you care
60/// about this you should send an EOS frame yourself.
61///
62/// The client must free all the input and output resources before
63/// destroying the encoder.
64/// If using events, they must also be unregistered.
65impl Drop for Encoder {
66 fn drop(&mut self) {
67 unsafe { (ENCODE_API.destroy_encoder)(self.ptr) }
68 .result(self)
69 .expect("The encoder pointer should be valid.");
70 }
71}
72
73impl Encoder {
74 /// Create an [`Encoder`] with CUDA as the encode device.
75 ///
76 /// See [NVIDIA docs](https://docs.nvidia.com/video-technologies/video-codec-sdk/12.0/nvenc-video-encoder-api-prog-guide/index.html#cuda).
77 ///
78 /// # Errors
79 ///
80 /// Could error if there was no encode capable device detected
81 /// or if the encode device was invalid.
82 ///
83 /// # Examples
84 ///
85 /// ```
86 /// # use cudarc::driver::CudaContext;
87 /// # use nvidia_video_codec_sdk::Encoder;
88 /// let cuda_ctx = CudaContext::new(0).unwrap();
89 /// let encoder = Encoder::initialize_with_cuda(cuda_ctx).unwrap();
90 /// ```
91 pub fn initialize_with_cuda(cuda_ctx: Arc<CudaContext>) -> Result<Self, EncodeError> {
92 let mut encoder = ptr::null_mut();
93 let mut session_params = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS {
94 version: NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER,
95 deviceType: NV_ENC_DEVICE_TYPE::NV_ENC_DEVICE_TYPE_CUDA,
96 apiVersion: NVENCAPI_VERSION,
97 // Pass the CUDA Context as the device.
98 // valid casting since CUcontext is a *mut
99 device: cuda_ctx.cu_ctx().cast::<c_void>(),
100 ..Default::default()
101 };
102
103 if let err @ Err(_) =
104 unsafe { (ENCODE_API.open_encode_session_ex)(&mut session_params, &mut encoder) }
105 .result_without_string()
106 {
107 // We are required to destroy the encoder if there was an error.
108 unsafe { (ENCODE_API.destroy_encoder)(encoder) }.result_without_string()?;
109 err?;
110 }
111
112 Ok(Self {
113 ptr: encoder,
114 ctx: cuda_ctx,
115 })
116 }
117
118 // TODO:
119 // - Make Encoder generic in Device.
120 // - Add functions to create Encoder from other encode devices.
121
122 /// Get the encode GUIDs which the encoder supports.
123 ///
124 /// You should use this function to check whether your
125 /// machine supports the video compression standard
126 /// that you with to use.
127 ///
128 /// See [NVIDIA docs](https://docs.nvidia.com/video-technologies/video-codec-sdk/12.0/nvenc-video-encoder-api-prog-guide/index.html#selecting-encoder-codec-guid).
129 ///
130 /// # Errors
131 ///
132 /// Could error if we run out of memory.
133 ///
134 /// # Examples
135 ///
136 /// ```
137 /// # use cudarc::driver::CudaContext;
138 /// # use nvidia_video_codec_sdk::{sys::nvEncodeAPI::NV_ENC_CODEC_H264_GUID, Encoder};
139 /// # let cuda_ctx = CudaContext::new(0).unwrap();
140 /// let encoder = Encoder::initialize_with_cuda(cuda_ctx).unwrap();
141 /// let encode_guids = encoder.get_encode_guids().unwrap();
142 /// // Confirm that this machine support encoding to H.264.
143 /// assert!(encode_guids.contains(&NV_ENC_CODEC_H264_GUID));
144 /// ```
145 pub fn get_encode_guids(&self) -> Result<Vec<GUID>, EncodeError> {
146 // Query number of supported encoder codec GUIDs.
147 let mut supported_count = 0;
148 unsafe { (ENCODE_API.get_encode_guid_count)(self.ptr, &mut supported_count) }
149 .result(self)?;
150 // Get the supported GUIDs.
151 let mut encode_guids = vec![GUID::default(); supported_count as usize];
152 let mut actual_count = 0;
153 unsafe {
154 (ENCODE_API.get_encode_guids)(
155 self.ptr,
156 encode_guids.as_mut_ptr(),
157 supported_count,
158 &mut actual_count,
159 )
160 }
161 .result(self)?;
162 encode_guids.truncate(actual_count as usize);
163 Ok(encode_guids)
164 }
165
166 /// Get the encode preset GUIDs which the encoder supports
167 /// for the given codec GUID.
168 ///
169 /// You should use this function to check whether your
170 /// machine supports the encode preset that you wish to use.
171 ///
172 /// See [NVIDIA docs](https://docs.nvidia.com/video-technologies/video-codec-sdk/12.0/nvenc-video-encoder-api-prog-guide/index.html#selecting-encoder-preset-configuration).
173 ///
174 /// # Errors
175 ///
176 /// Could error if the encode GUID is invalid
177 /// or we run out of memory.
178 ///
179 /// # Examples
180 ///
181 /// ```
182 /// # use cudarc::driver::CudaContext;
183 /// # use nvidia_video_codec_sdk::{
184 /// # sys::nvEncodeAPI::{NV_ENC_CODEC_H264_GUID, NV_ENC_PRESET_P1_GUID},
185 /// # Encoder,
186 /// # };
187 /// # let cuda_ctx = CudaContext::new(0).unwrap();
188 /// let encoder = Encoder::initialize_with_cuda(cuda_ctx).unwrap();
189 ///
190 /// //* Check if H.264 encoding is supported. *//
191 /// # let encode_guids = encoder.get_encode_guids().unwrap();
192 /// # assert!(encode_guids.contains(&NV_ENC_CODEC_H264_GUID));
193 ///
194 /// let preset_guids = encoder.get_preset_guids(NV_ENC_CODEC_H264_GUID).unwrap();
195 /// // Confirm that H.264 supports the P1 preset (high performance, low quality) on this machine.
196 /// assert!(preset_guids.contains(&NV_ENC_PRESET_P1_GUID));
197 /// ```
198 pub fn get_preset_guids(&self, encode_guid: GUID) -> Result<Vec<GUID>, EncodeError> {
199 // Query the number of preset GUIDS.
200 let mut preset_count = 0;
201 unsafe { (ENCODE_API.get_encode_preset_count)(self.ptr, encode_guid, &mut preset_count) }
202 .result(self)?;
203 // Get the preset GUIDs.
204 let mut actual_count = 0;
205 let mut preset_guids = vec![GUID::default(); preset_count as usize];
206 unsafe {
207 (ENCODE_API.get_encode_preset_guids)(
208 self.ptr,
209 encode_guid,
210 preset_guids.as_mut_ptr(),
211 preset_count,
212 &mut actual_count,
213 )
214 }
215 .result(self)?;
216 preset_guids.truncate(actual_count as usize);
217 Ok(preset_guids)
218 }
219
220 /// Get the encode profile GUIDs which the encoder supports
221 /// for the given codec GUID.
222 ///
223 /// You should use this function to check whether your
224 /// machine supports the encode profile that you wish to use.
225 ///
226 /// See [NVIDIA docs](https://docs.nvidia.com/video-technologies/video-codec-sdk/12.0/nvenc-video-encoder-api-prog-guide/index.html#selecting-an-encoder-profile).
227 ///
228 /// # Errors
229 ///
230 /// Could error if the encode GUID is invalid
231 /// or we run out of memory.
232 ///
233 /// # Examples
234 ///
235 /// ```
236 /// # use cudarc::driver::CudaContext;
237 /// # use nvidia_video_codec_sdk::{
238 /// # sys::nvEncodeAPI::{NV_ENC_CODEC_H264_GUID, NV_ENC_H264_PROFILE_HIGH_GUID},
239 /// # Encoder,
240 /// # };
241 /// # let cuda_ctx = CudaContext::new(0).unwrap();
242 /// let encoder = Encoder::initialize_with_cuda(cuda_ctx).unwrap();
243 ///
244 /// //* Check if H.264 encoding is supported. *//
245 /// # let encode_guids = encoder.get_encode_guids().unwrap();
246 /// # assert!(encode_guids.contains(&NV_ENC_CODEC_H264_GUID));
247 ///
248 /// let profile_guids = encoder.get_profile_guids(NV_ENC_CODEC_H264_GUID).unwrap();
249 /// // Confirm that H.264 supports the HIGH profile on this machine.
250 /// assert!(profile_guids.contains(&NV_ENC_H264_PROFILE_HIGH_GUID));
251 /// ```
252 pub fn get_profile_guids(&self, encode_guid: GUID) -> Result<Vec<GUID>, EncodeError> {
253 // Query the number of profile GUIDs.
254 let mut profile_count = 0;
255 unsafe {
256 (ENCODE_API.get_encode_profile_guid_count)(self.ptr, encode_guid, &mut profile_count)
257 }
258 .result(self)?;
259 // Get the profile GUIDs.
260 let mut profile_guids = vec![GUID::default(); profile_count as usize];
261 let mut actual_count = 0;
262 unsafe {
263 (ENCODE_API.get_encode_profile_guids)(
264 self.ptr,
265 encode_guid,
266 profile_guids.as_mut_ptr(),
267 profile_count,
268 &mut actual_count,
269 )
270 }
271 .result(self)?;
272 profile_guids.truncate(actual_count as usize);
273 Ok(profile_guids)
274 }
275
276 /// Get the buffer formats which the encoder supports
277 /// for the given codec GUID.
278 ///
279 /// You should use this function to check whether your
280 /// machine supports the buffer format that you wish to use.
281 ///
282 /// See [NVIDIA docs](https://docs.nvidia.com/video-technologies/video-codec-sdk/12.0/nvenc-video-encoder-api-prog-guide/index.html#getting-supported-list-of-input-formats).
283 ///
284 /// # Errors
285 ///
286 /// Could error if the encode GUID is invalid
287 /// or we run out of memory.
288 ///
289 /// # Examples
290 ///
291 /// ```
292 /// # use cudarc::driver::CudaContext;
293 /// # use nvidia_video_codec_sdk::{
294 /// # sys::nvEncodeAPI::{NV_ENC_BUFFER_FORMAT, NV_ENC_CODEC_H264_GUID},
295 /// # Encoder,
296 /// # };
297 /// # let cuda_ctx = CudaContext::new(0).unwrap();
298 /// let encoder = Encoder::initialize_with_cuda(cuda_ctx).unwrap();
299 ///
300 /// //* Check if H.264 encoding is supported. *//
301 /// # let encode_guids = encoder.get_encode_guids().unwrap();
302 /// # assert!(encode_guids.contains(&NV_ENC_CODEC_H264_GUID));
303 ///
304 /// let input_guids = encoder
305 /// .get_supported_input_formats(NV_ENC_CODEC_H264_GUID)
306 /// .unwrap();
307 /// // Confirm that H.264 supports the `ARGB10` format on this machine.
308 /// assert!(input_guids.contains(&NV_ENC_BUFFER_FORMAT::NV_ENC_BUFFER_FORMAT_ARGB10));
309 /// ```
310 pub fn get_supported_input_formats(
311 &self,
312 encode_guid: GUID,
313 ) -> Result<Vec<NV_ENC_BUFFER_FORMAT>, EncodeError> {
314 // Query the number of supported input formats.
315 let mut format_count = 0;
316 unsafe { (ENCODE_API.get_input_format_count)(self.ptr, encode_guid, &mut format_count) }
317 .result(self)?;
318 // Get the supported input formats.
319 let mut supported_input_formats =
320 vec![NV_ENC_BUFFER_FORMAT::NV_ENC_BUFFER_FORMAT_UNDEFINED; format_count as usize];
321 let mut actual_count = 0;
322 unsafe {
323 (ENCODE_API.get_input_formats)(
324 self.ptr,
325 encode_guid,
326 supported_input_formats.as_mut_ptr(),
327 format_count,
328 &mut actual_count,
329 )
330 }
331 .result(self)?;
332 supported_input_formats.truncate(actual_count as usize);
333 Ok(supported_input_formats)
334 }
335
336 /// Get the preset config struct from the given codec GUID, preset GUID,
337 /// and tuning info.
338 ///
339 /// You should use this function to generate a preset config for the
340 /// encoder session if you want to modify the preset further.
341 ///
342 /// See [NVIDIA docs](https://docs.nvidia.com/video-technologies/video-codec-sdk/12.0/nvenc-video-encoder-api-prog-guide/index.html#selecting-encoder-preset-configuration)
343 ///
344 /// # Errors
345 ///
346 /// Could error if `encode_guid` or `preset_guid` is invalid,
347 /// if `tuning_info` is set to
348 /// [`NV_ENC_TUNING_INFO::NV_ENC_TUNING_INFO_UNDEFINED`] or
349 /// [`NV_ENC_TUNING_INFO::NV_ENC_TUNING_INFO_COUNT`],
350 /// or if we run out of memory.
351 ///
352 /// # Examples
353 ///
354 /// ```
355 /// # use cudarc::driver::CudaContext;
356 /// # use nvidia_video_codec_sdk::{
357 /// # sys::nvEncodeAPI::{
358 /// # NV_ENC_CODEC_H264_GUID,
359 /// # NV_ENC_PRESET_P1_GUID,
360 /// # NV_ENC_TUNING_INFO,
361 /// # },
362 /// # Encoder,
363 /// # };
364 /// # let cuda_ctx = CudaContext::new(0).unwrap();
365 /// let encoder = Encoder::initialize_with_cuda(cuda_ctx).unwrap();
366 ///
367 /// //* Check if H.264 encoding and the P1 preset (highest performance) are supported. *//
368 /// # let encode_guids = encoder.get_encode_guids().unwrap();
369 /// # assert!(encode_guids.contains(&NV_ENC_CODEC_H264_GUID));
370 /// # let preset_guids = encoder.get_preset_guids(NV_ENC_CODEC_H264_GUID).unwrap();
371 /// # assert!(preset_guids.contains(&NV_ENC_PRESET_P1_GUID));
372 ///
373 /// // Create the preset config.
374 /// let _preset_config = encoder
375 /// .get_preset_config(
376 /// NV_ENC_CODEC_H264_GUID,
377 /// NV_ENC_PRESET_P1_GUID,
378 /// NV_ENC_TUNING_INFO::NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY,
379 /// )
380 /// .unwrap();
381 /// ```
382 pub fn get_preset_config(
383 &self,
384 encode_guid: GUID,
385 preset_guid: GUID,
386 tuning_info: NV_ENC_TUNING_INFO,
387 ) -> Result<NV_ENC_PRESET_CONFIG, EncodeError> {
388 let mut preset_config = NV_ENC_PRESET_CONFIG {
389 version: NV_ENC_PRESET_CONFIG_VER,
390 presetCfg: NV_ENC_CONFIG {
391 version: NV_ENC_CONFIG_VER,
392 ..Default::default()
393 },
394 ..Default::default()
395 };
396 unsafe {
397 (ENCODE_API.get_encode_preset_config_ex)(
398 self.ptr,
399 encode_guid,
400 preset_guid,
401 tuning_info,
402 &mut preset_config,
403 )
404 }
405 .result(self)?;
406 Ok(preset_config)
407 }
408
409 /// Initialize an encoder session with the given configuration.
410 ///
411 /// You must do this before you can encode a picture.
412 /// You should use the [`NV_ENC_INITIALIZE_PARAMS`] builder
413 /// via [`NV_ENC_INITIALIZE_PARAMS::new`].
414 ///
415 /// See [NVIDIA docs](https://docs.nvidia.com/video-technologies/video-codec-sdk/12.0/nvenc-video-encoder-api-prog-guide/index.html#initializing-the-hardware-encoder-session).
416 ///
417 /// # Errors
418 ///
419 /// Could error if the `initialize_params` are invalid
420 /// or if we run out of memory.
421 ///
422 /// # Examples
423 ///
424 /// ```
425 /// # use cudarc::driver::CudaContext;
426 /// # use nvidia_video_codec_sdk::{
427 /// # sys::nvEncodeAPI::{
428 /// # NV_ENC_BUFFER_FORMAT::NV_ENC_BUFFER_FORMAT_ARGB,
429 /// # NV_ENC_CODEC_H264_GUID,
430 /// # },
431 /// # Encoder, EncoderInitParams
432 /// # };
433 /// # let cuda_ctx = CudaContext::new(0).unwrap();
434 /// let encoder = Encoder::initialize_with_cuda(cuda_ctx).unwrap();
435 ///
436 /// //* Check if `NV_ENC_CODEC_H264_GUID` is supported. *//
437 /// # let encode_guids = encoder.get_encode_guids().unwrap();
438 /// # assert!(encode_guids.contains(&NV_ENC_CODEC_H264_GUID));
439 ///
440 /// // Initialize the encoder session.
441 /// let _session = encoder
442 /// .start_session(
443 /// NV_ENC_BUFFER_FORMAT_ARGB,
444 /// EncoderInitParams::new(NV_ENC_CODEC_H264_GUID, 1920, 1080),
445 /// )
446 /// .unwrap();
447 /// ```
448 pub fn start_session(
449 self,
450 buffer_format: NV_ENC_BUFFER_FORMAT,
451 mut initialize_params: EncoderInitParams<'_>,
452 ) -> Result<Session, EncodeError> {
453 let initialize_params = &mut initialize_params.param;
454 let width = initialize_params.encodeWidth;
455 let height = initialize_params.encodeHeight;
456 unsafe { (ENCODE_API.initialize_encoder)(self.ptr, initialize_params) }.result(&self)?;
457 Ok(Session {
458 encoder: self,
459 width,
460 height,
461 buffer_format,
462 encode_guid: initialize_params.encodeGUID,
463 })
464 }
465}
466
467/// A safe wrapper for [`NV_ENC_INITIALIZE_PARAMS`], which is the encoder
468/// initialize parameter.
469#[derive(Debug)]
470pub struct EncoderInitParams<'a> {
471 param: NV_ENC_INITIALIZE_PARAMS,
472 marker: std::marker::PhantomData<&'a mut NV_ENC_CONFIG>,
473}
474
475impl<'a> EncoderInitParams<'a> {
476 /// Create a new builder for [`EncoderInitParams`], which is a wrapper for
477 /// [`NV_ENC_INITIALIZE_PARAMS`].
478 #[must_use]
479 pub fn new(encode_guid: GUID, width: u32, height: u32) -> Self {
480 let param = NV_ENC_INITIALIZE_PARAMS {
481 version: NV_ENC_INITIALIZE_PARAMS_VER,
482 encodeGUID: encode_guid,
483 encodeWidth: width,
484 encodeHeight: height,
485 ..Default::default()
486 };
487 Self {
488 param,
489 marker: std::marker::PhantomData,
490 }
491 }
492
493 /// Specifies the preset for encoding. If the preset GUID is set then
494 /// the preset configuration will be applied before any other parameter.
495 pub fn preset_guid(&mut self, preset_guid: GUID) -> &mut Self {
496 self.param.presetGUID = preset_guid;
497 self
498 }
499
500 /// Tuning Info of NVENC encoding(`tuning_info` is not applicable to H264
501 /// and HEVC meonly mode).
502 pub fn tuning_info(&mut self, tuning_info: NV_ENC_TUNING_INFO) -> &mut Self {
503 self.param.tuningInfo = tuning_info;
504 self
505 }
506
507 /// Specifies the advanced codec specific structure. If client has sent a
508 /// valid codec config structure, it will override parameters set by the
509 /// [`EncoderInitParams::preset_guid`].
510 ///
511 /// The client can query the interface for codec-specific parameters
512 /// using [`Encoder::get_preset_config`](super::encoder::Encoder::get_preset_config).
513 /// It can then modify (if required) some of the codec config parameters and
514 /// send down a custom config structure using this method. Even in this
515 /// case the client is recommended to pass the same preset GUID it has
516 /// used to get the config.
517 pub fn encode_config(&mut self, encode_config: &'a mut NV_ENC_CONFIG) -> &mut Self {
518 self.param.encodeConfig = encode_config;
519 self
520 }
521
522 /// Specifies the display aspect ratio (H264/HEVC) or the render
523 /// width/height (AV1).
524 pub fn display_aspect_ratio(&mut self, width: u32, height: u32) -> &mut Self {
525 self.param.darWidth = width;
526 self.param.darHeight = height;
527 self
528 }
529
530 /// Specifies the framerate in frames per second as a fraction
531 /// `numerator / denominator`.
532 pub fn framerate(&mut self, numerator: u32, denominator: u32) -> &mut Self {
533 self.param.frameRateNum = numerator;
534 self.param.frameRateDen = denominator;
535 self
536 }
537
538 /// Enable the Picture Type Decision to be taken by the
539 /// `NvEncodeAPI` interface.
540 pub fn enable_picture_type_decision(&mut self) -> &mut Self {
541 self.param.enablePTD = 1;
542 self
543 }
544}