oximedia_codec/
codec_caps.rs1#![allow(dead_code)]
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum HwAccelType {
12 None,
14 Cuda,
16 Qsv,
18 Vaapi,
20 VideoToolbox,
22 Amf,
24}
25
26impl HwAccelType {
27 pub fn is_available_stub(self) -> bool {
32 !matches!(self, HwAccelType::None)
33 }
34
35 pub fn name(self) -> &'static str {
37 match self {
38 HwAccelType::None => "Software",
39 HwAccelType::Cuda => "CUDA",
40 HwAccelType::Qsv => "Intel QSV",
41 HwAccelType::Vaapi => "VA-API",
42 HwAccelType::VideoToolbox => "VideoToolbox",
43 HwAccelType::Amf => "AMD AMF",
44 }
45 }
46}
47
48#[derive(Debug, Clone)]
50pub struct CodecCaps {
51 pub codec_id: String,
53 pub max_width: u32,
55 pub max_height: u32,
57 pub hw_accels: Vec<HwAccelType>,
59 pub b_frames: bool,
61 pub lossless: bool,
63}
64
65impl CodecCaps {
66 pub fn new(codec_id: impl Into<String>) -> Self {
68 Self {
69 codec_id: codec_id.into(),
70 max_width: 7680,
71 max_height: 4320,
72 hw_accels: Vec::new(),
73 b_frames: false,
74 lossless: false,
75 }
76 }
77
78 pub fn with_max_resolution(mut self, w: u32, h: u32) -> Self {
80 self.max_width = w;
81 self.max_height = h;
82 self
83 }
84
85 pub fn with_hw_accel(mut self, accel: HwAccelType) -> Self {
87 self.hw_accels.push(accel);
88 self
89 }
90
91 pub fn with_b_frames(mut self) -> Self {
93 self.b_frames = true;
94 self
95 }
96
97 pub fn with_lossless(mut self) -> Self {
99 self.lossless = true;
100 self
101 }
102
103 pub fn supports_hw_accel(&self, accel: HwAccelType) -> bool {
105 self.hw_accels.contains(&accel)
106 }
107
108 pub fn supports_resolution(&self, width: u32, height: u32) -> bool {
110 width <= self.max_width && height <= self.max_height
111 }
112}
113
114#[derive(Debug, Default)]
116pub struct CodecCapsRegistry {
117 entries: Vec<CodecCaps>,
118}
119
120impl CodecCapsRegistry {
121 pub fn new() -> Self {
123 Self {
124 entries: Vec::new(),
125 }
126 }
127
128 pub fn register(&mut self, caps: CodecCaps) {
132 if let Some(existing) = self
133 .entries
134 .iter_mut()
135 .find(|c| c.codec_id == caps.codec_id)
136 {
137 *existing = caps;
138 } else {
139 self.entries.push(caps);
140 }
141 }
142
143 pub fn find(&self, codec_id: &str) -> Option<&CodecCaps> {
145 self.entries.iter().find(|c| c.codec_id == codec_id)
146 }
147
148 pub fn len(&self) -> usize {
150 self.entries.len()
151 }
152
153 pub fn is_empty(&self) -> bool {
155 self.entries.is_empty()
156 }
157
158 pub fn codec_ids(&self) -> Vec<&str> {
160 self.entries.iter().map(|c| c.codec_id.as_str()).collect()
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_hw_accel_none_not_available() {
170 assert!(!HwAccelType::None.is_available_stub());
171 }
172
173 #[test]
174 fn test_hw_accel_cuda_available() {
175 assert!(HwAccelType::Cuda.is_available_stub());
176 }
177
178 #[test]
179 fn test_hw_accel_vaapi_available() {
180 assert!(HwAccelType::Vaapi.is_available_stub());
181 }
182
183 #[test]
184 fn test_hw_accel_name() {
185 assert_eq!(HwAccelType::Cuda.name(), "CUDA");
186 assert_eq!(HwAccelType::Qsv.name(), "Intel QSV");
187 assert_eq!(HwAccelType::VideoToolbox.name(), "VideoToolbox");
188 }
189
190 #[test]
191 fn test_codec_caps_default_max_resolution() {
192 let caps = CodecCaps::new("av1");
193 assert_eq!(caps.max_width, 7680);
194 assert_eq!(caps.max_height, 4320);
195 }
196
197 #[test]
198 fn test_codec_caps_supports_hw_accel_true() {
199 let caps = CodecCaps::new("h264").with_hw_accel(HwAccelType::Cuda);
200 assert!(caps.supports_hw_accel(HwAccelType::Cuda));
201 }
202
203 #[test]
204 fn test_codec_caps_supports_hw_accel_false() {
205 let caps = CodecCaps::new("av1");
206 assert!(!caps.supports_hw_accel(HwAccelType::Cuda));
207 }
208
209 #[test]
210 fn test_codec_caps_supports_resolution_within() {
211 let caps = CodecCaps::new("vp9").with_max_resolution(3840, 2160);
212 assert!(caps.supports_resolution(1920, 1080));
213 }
214
215 #[test]
216 fn test_codec_caps_supports_resolution_too_large() {
217 let caps = CodecCaps::new("vp9").with_max_resolution(3840, 2160);
218 assert!(!caps.supports_resolution(7680, 4320));
219 }
220
221 #[test]
222 fn test_codec_caps_lossless_flag() {
223 let caps = CodecCaps::new("av1").with_lossless();
224 assert!(caps.lossless);
225 }
226
227 #[test]
228 fn test_registry_register_and_find() {
229 let mut reg = CodecCapsRegistry::new();
230 reg.register(CodecCaps::new("av1"));
231 assert!(reg.find("av1").is_some());
232 }
233
234 #[test]
235 fn test_registry_find_missing() {
236 let reg = CodecCapsRegistry::new();
237 assert!(reg.find("h264").is_none());
238 }
239
240 #[test]
241 fn test_registry_replaces_existing() {
242 let mut reg = CodecCapsRegistry::new();
243 reg.register(CodecCaps::new("av1").with_max_resolution(1920, 1080));
244 reg.register(CodecCaps::new("av1").with_max_resolution(3840, 2160));
245 let caps = reg.find("av1").expect("should succeed");
246 assert_eq!(caps.max_width, 3840);
247 }
248
249 #[test]
250 fn test_registry_len_and_is_empty() {
251 let mut reg = CodecCapsRegistry::new();
252 assert!(reg.is_empty());
253 reg.register(CodecCaps::new("av1"));
254 assert_eq!(reg.len(), 1);
255 assert!(!reg.is_empty());
256 }
257
258 #[test]
259 fn test_registry_codec_ids() {
260 let mut reg = CodecCapsRegistry::new();
261 reg.register(CodecCaps::new("av1"));
262 reg.register(CodecCaps::new("vp9"));
263 let ids = reg.codec_ids();
264 assert!(ids.contains(&"av1"));
265 assert!(ids.contains(&"vp9"));
266 }
267}