1#![allow(dead_code)]
9
10pub type CodecId = String;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct CodecFeatures {
24 pub codec: CodecId,
26 pub hdr: bool,
28 pub ten_bit: bool,
30 pub bframe: bool,
32 pub lossless: bool,
34 pub audio: bool,
36 pub svc: bool,
38 pub max_bit_depth: u8,
40}
41
42impl CodecFeatures {
43 #[must_use]
51 pub fn new(codec: &str) -> Self {
52 let id = codec.to_lowercase();
53 match id.as_str() {
54 "av1" => Self {
55 codec: id,
56 hdr: true,
57 ten_bit: true,
58 bframe: true,
59 lossless: true,
60 audio: false,
61 svc: true,
62 max_bit_depth: 12,
63 },
64 "vp9" => Self {
65 codec: id,
66 hdr: true,
67 ten_bit: true,
68 bframe: false,
69 lossless: true,
70 audio: false,
71 svc: true,
72 max_bit_depth: 12,
73 },
74 "vp8" => Self {
75 codec: id,
76 hdr: false,
77 ten_bit: false,
78 bframe: false,
79 lossless: false,
80 audio: false,
81 svc: false,
82 max_bit_depth: 8,
83 },
84 "theora" => Self {
85 codec: id,
86 hdr: false,
87 ten_bit: false,
88 bframe: false,
89 lossless: false,
90 audio: false,
91 svc: false,
92 max_bit_depth: 8,
93 },
94 "h264" | "avc" => Self {
95 codec: id,
96 hdr: false,
97 ten_bit: true,
98 bframe: true,
99 lossless: false,
100 audio: false,
101 svc: true,
102 max_bit_depth: 10,
103 },
104 "h265" | "hevc" => Self {
105 codec: id,
106 hdr: true,
107 ten_bit: true,
108 bframe: true,
109 lossless: false,
110 audio: false,
111 svc: true,
112 max_bit_depth: 12,
113 },
114 "opus" => Self {
115 codec: id,
116 hdr: false,
117 ten_bit: false,
118 bframe: false,
119 lossless: false,
120 audio: true,
121 svc: false,
122 max_bit_depth: 16,
123 },
124 "vorbis" => Self {
125 codec: id,
126 hdr: false,
127 ten_bit: false,
128 bframe: false,
129 lossless: false,
130 audio: true,
131 svc: false,
132 max_bit_depth: 16,
133 },
134 "flac" => Self {
135 codec: id,
136 hdr: false,
137 ten_bit: false,
138 bframe: false,
139 lossless: true,
140 audio: true,
141 svc: false,
142 max_bit_depth: 24,
143 },
144 "ffv1" => Self {
145 codec: id,
146 hdr: true,
147 ten_bit: true,
148 bframe: false,
149 lossless: true,
150 audio: false,
151 svc: false,
152 max_bit_depth: 16,
153 },
154 "jpegxl" | "jxl" => Self {
155 codec: id,
156 hdr: true,
157 ten_bit: true,
158 bframe: false,
159 lossless: true,
160 audio: false,
161 svc: false,
162 max_bit_depth: 16,
163 },
164 _ => Self {
165 codec: id,
166 hdr: false,
167 ten_bit: false,
168 bframe: false,
169 lossless: false,
170 audio: false,
171 svc: false,
172 max_bit_depth: 8,
173 },
174 }
175 }
176
177 #[must_use]
179 pub fn supports_hdr(&self) -> bool {
180 self.hdr
181 }
182
183 #[must_use]
185 pub fn supports_10bit(&self) -> bool {
186 self.ten_bit
187 }
188
189 #[must_use]
191 pub fn supports_bframe(&self) -> bool {
192 self.bframe
193 }
194
195 #[must_use]
197 pub fn supports_lossless(&self) -> bool {
198 self.lossless
199 }
200
201 #[must_use]
203 pub fn is_audio(&self) -> bool {
204 self.audio
205 }
206
207 #[must_use]
209 pub fn supports_svc(&self) -> bool {
210 self.svc
211 }
212
213 #[must_use]
215 pub fn max_bit_depth(&self) -> u8 {
216 self.max_bit_depth
217 }
218}
219
220#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn av1_features_correct() {
230 let f = CodecFeatures::new("av1");
231 assert!(f.supports_hdr());
232 assert!(f.supports_10bit());
233 assert!(f.supports_bframe());
234 assert!(f.supports_lossless());
235 assert!(!f.is_audio());
236 assert_eq!(f.max_bit_depth(), 12);
237 }
238
239 #[test]
240 fn vp9_no_bframe() {
241 let f = CodecFeatures::new("vp9");
242 assert!(!f.supports_bframe());
243 assert!(f.supports_10bit());
244 }
245
246 #[test]
247 fn vp8_basic_only() {
248 let f = CodecFeatures::new("vp8");
249 assert!(!f.supports_hdr());
250 assert!(!f.supports_10bit());
251 assert!(!f.supports_bframe());
252 assert_eq!(f.max_bit_depth(), 8);
253 }
254
255 #[test]
256 fn opus_is_audio() {
257 let f = CodecFeatures::new("opus");
258 assert!(f.is_audio());
259 assert!(!f.supports_bframe());
260 }
261
262 #[test]
263 fn flac_lossless_audio() {
264 let f = CodecFeatures::new("flac");
265 assert!(f.supports_lossless());
266 assert!(f.is_audio());
267 assert_eq!(f.max_bit_depth(), 24);
268 }
269
270 #[test]
271 fn ffv1_lossless_video_hdr() {
272 let f = CodecFeatures::new("ffv1");
273 assert!(f.supports_hdr());
274 assert!(f.supports_lossless());
275 assert!(!f.is_audio());
276 }
277
278 #[test]
279 fn case_insensitive_lookup() {
280 let lower = CodecFeatures::new("AV1");
281 let upper = CodecFeatures::new("av1");
282 assert_eq!(lower.supports_hdr(), upper.supports_hdr());
283 }
284
285 #[test]
286 fn unknown_codec_conservative_defaults() {
287 let f = CodecFeatures::new("my_unknown_codec");
288 assert!(!f.supports_hdr());
289 assert!(!f.supports_10bit());
290 assert!(!f.supports_bframe());
291 assert_eq!(f.max_bit_depth(), 8);
292 }
293
294 #[test]
295 fn h265_hdr_and_bframe() {
296 let f = CodecFeatures::new("h265");
297 assert!(f.supports_hdr());
298 assert!(f.supports_bframe());
299 }
300}