ff_encode/hardware.rs
1//! Hardware encoder definitions.
2
3use std::ffi::CString;
4use std::sync::OnceLock;
5
6/// Hardware encoder type.
7///
8/// Specifies which hardware acceleration to use for encoding.
9/// Hardware encoding is generally faster and more power-efficient than software encoding,
10/// but may have slightly lower quality at the same bitrate.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12#[non_exhaustive]
13pub enum HardwareEncoder {
14 /// Auto-detect available hardware encoder
15 #[default]
16 Auto,
17
18 /// Software encoding only (no hardware acceleration)
19 None,
20
21 /// NVIDIA NVENC
22 Nvenc,
23
24 /// Intel Quick Sync Video
25 Qsv,
26
27 /// AMD Advanced Media Framework (AMF, formerly VCE)
28 Amf,
29
30 /// Apple `VideoToolbox`
31 VideoToolbox,
32
33 /// VA-API (Linux)
34 Vaapi,
35}
36
37impl HardwareEncoder {
38 /// Get the list of available hardware encoders.
39 ///
40 /// Queries FFmpeg for available hardware encoders on the system.
41 /// This is useful for UI to show which hardware acceleration options
42 /// the user can select.
43 ///
44 /// The result is cached on first call for performance.
45 ///
46 /// # Examples
47 ///
48 /// ```no_run
49 /// use ff_encode::HardwareEncoder;
50 ///
51 /// let available = HardwareEncoder::available();
52 /// for hw in available {
53 /// println!("Available: {:?}", hw);
54 /// }
55 /// ```
56 #[must_use]
57 pub fn available() -> &'static [Self] {
58 static AVAILABLE: OnceLock<Vec<HardwareEncoder>> = OnceLock::new();
59
60 AVAILABLE.get_or_init(|| {
61 let mut result = Vec::new();
62
63 // Auto and None are always available
64 result.push(Self::Auto);
65 result.push(Self::None);
66
67 // Check each hardware encoder type
68 if Self::Nvenc.is_available() {
69 result.push(Self::Nvenc);
70 }
71 if Self::Qsv.is_available() {
72 result.push(Self::Qsv);
73 }
74 if Self::Amf.is_available() {
75 result.push(Self::Amf);
76 }
77 if Self::VideoToolbox.is_available() {
78 result.push(Self::VideoToolbox);
79 }
80 if Self::Vaapi.is_available() {
81 result.push(Self::Vaapi);
82 }
83
84 result
85 })
86 }
87
88 /// Check if this hardware encoder is available.
89 ///
90 /// Queries FFmpeg to determine if the hardware encoder is available
91 /// on the current system. This checks for both H.264 and H.265 support.
92 ///
93 /// # Examples
94 ///
95 /// ```no_run
96 /// use ff_encode::HardwareEncoder;
97 ///
98 /// if HardwareEncoder::Nvenc.is_available() {
99 /// println!("NVENC is available on this system");
100 /// }
101 /// ```
102 #[must_use]
103 pub fn is_available(self) -> bool {
104 match self {
105 // Auto and None are always available
106 Self::Auto | Self::None => true,
107
108 // Check hardware encoder availability
109 Self::Nvenc => is_encoder_available("h264_nvenc") || is_encoder_available("hevc_nvenc"),
110 Self::Qsv => is_encoder_available("h264_qsv") || is_encoder_available("hevc_qsv"),
111 Self::Amf => is_encoder_available("h264_amf") || is_encoder_available("hevc_amf"),
112 Self::VideoToolbox => {
113 is_encoder_available("h264_videotoolbox")
114 || is_encoder_available("hevc_videotoolbox")
115 }
116 Self::Vaapi => is_encoder_available("h264_vaapi") || is_encoder_available("hevc_vaapi"),
117 }
118 }
119}
120
121/// Helper function to check if an encoder is available.
122///
123/// # Arguments
124///
125/// * `name` - The encoder name to check (e.g., "h264_nvenc", "hevc_qsv")
126///
127/// # Returns
128///
129/// Returns `true` if the encoder is available, `false` otherwise.
130fn is_encoder_available(name: &str) -> bool {
131 unsafe {
132 ff_sys::ensure_initialized();
133
134 let Ok(c_name) = CString::new(name) else {
135 return false;
136 };
137
138 ff_sys::avcodec::find_encoder_by_name(c_name.as_ptr()).is_some()
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_default_hardware_encoder() {
148 assert_eq!(HardwareEncoder::default(), HardwareEncoder::Auto);
149 }
150
151 #[test]
152 fn test_auto_and_none_always_available() {
153 // Auto and None should always be available
154 assert!(HardwareEncoder::Auto.is_available());
155 assert!(HardwareEncoder::None.is_available());
156 }
157
158 #[test]
159 fn test_available_returns_at_least_auto_and_none() {
160 let available = HardwareEncoder::available();
161 assert!(available.contains(&HardwareEncoder::Auto));
162 assert!(available.contains(&HardwareEncoder::None));
163 assert!(available.len() >= 2);
164 }
165
166 #[test]
167 fn test_hardware_encoder_availability() {
168 // This test just checks that the functions don't panic
169 // Actual availability depends on system hardware
170 let _nvenc = HardwareEncoder::Nvenc.is_available();
171 let _qsv = HardwareEncoder::Qsv.is_available();
172 let _amf = HardwareEncoder::Amf.is_available();
173 let _videotoolbox = HardwareEncoder::VideoToolbox.is_available();
174 let _vaapi = HardwareEncoder::Vaapi.is_available();
175 }
176}