ff_format/codec.rs
1//! Video and audio codec definitions.
2//!
3//! This module provides enums for identifying video and audio codecs
4//! commonly used in media files.
5//!
6//! # Examples
7//!
8//! ```
9//! use ff_format::codec::{VideoCodec, AudioCodec};
10//!
11//! let video = VideoCodec::H264;
12//! assert!(video.is_h264_family());
13//! assert_eq!(video.name(), "h264");
14//!
15//! let audio = AudioCodec::Aac;
16//! assert!(audio.is_lossy());
17//! ```
18
19use std::fmt;
20
21/// Video codec identifier.
22///
23/// This enum represents common video codecs used in media files.
24/// It covers the most widely used codecs while remaining extensible
25/// via the `Unknown` variant.
26///
27/// # Common Usage
28///
29/// - **H.264/AVC**: Most common codec for HD video, excellent compatibility
30/// - **H.265/HEVC**: Better compression than H.264, used for 4K content
31/// - **VP9**: Google's open codec for web video streaming
32/// - **AV1**: Next-gen open codec, excellent compression
33/// - **Apple `ProRes`**: Apple's professional editing codec
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
35#[non_exhaustive]
36pub enum VideoCodec {
37 /// H.264/AVC - most common video codec
38 #[default]
39 H264,
40 /// H.265/HEVC - successor to H.264, better compression
41 H265,
42 /// VP8 - Google's older open codec
43 Vp8,
44 /// VP9 - Google's open codec for web video streaming
45 Vp9,
46 /// AV1 - Alliance for Open Media codec, next-gen compression
47 Av1,
48 /// Apple's professional editing codec
49 ProRes,
50 /// MPEG-4 Part 2 - older codec, legacy support
51 Mpeg4,
52 /// MPEG-2 Video - DVD and broadcast standard
53 Mpeg2,
54 /// MJPEG - Motion JPEG, used by some cameras
55 Mjpeg,
56 /// Unknown or unsupported codec
57 Unknown,
58}
59
60impl VideoCodec {
61 /// Returns the codec name as a human-readable string.
62 ///
63 /// # Examples
64 ///
65 /// ```
66 /// use ff_format::codec::VideoCodec;
67 ///
68 /// assert_eq!(VideoCodec::H264.name(), "h264");
69 /// assert_eq!(VideoCodec::H265.name(), "hevc");
70 /// ```
71 #[must_use]
72 pub const fn name(&self) -> &'static str {
73 match self {
74 Self::H264 => "h264",
75 Self::H265 => "hevc",
76 Self::Vp8 => "vp8",
77 Self::Vp9 => "vp9",
78 Self::Av1 => "av1",
79 Self::ProRes => "prores",
80 Self::Mpeg4 => "mpeg4",
81 Self::Mpeg2 => "mpeg2video",
82 Self::Mjpeg => "mjpeg",
83 Self::Unknown => "unknown",
84 }
85 }
86
87 /// Returns the human-readable display name for the codec.
88 ///
89 /// # Examples
90 ///
91 /// ```
92 /// use ff_format::codec::VideoCodec;
93 ///
94 /// assert_eq!(VideoCodec::H264.display_name(), "H.264/AVC");
95 /// assert_eq!(VideoCodec::H265.display_name(), "H.265/HEVC");
96 /// ```
97 #[must_use]
98 pub const fn display_name(&self) -> &'static str {
99 match self {
100 Self::H264 => "H.264/AVC",
101 Self::H265 => "H.265/HEVC",
102 Self::Vp8 => "VP8",
103 Self::Vp9 => "VP9",
104 Self::Av1 => "AV1",
105 Self::ProRes => "Apple ProRes",
106 Self::Mpeg4 => "MPEG-4 Part 2",
107 Self::Mpeg2 => "MPEG-2",
108 Self::Mjpeg => "Motion JPEG",
109 Self::Unknown => "Unknown",
110 }
111 }
112
113 /// Returns `true` if this is part of the H.264 family.
114 ///
115 /// # Examples
116 ///
117 /// ```
118 /// use ff_format::codec::VideoCodec;
119 ///
120 /// assert!(VideoCodec::H264.is_h264_family());
121 /// assert!(!VideoCodec::H265.is_h264_family());
122 /// ```
123 #[must_use]
124 pub const fn is_h264_family(&self) -> bool {
125 matches!(self, Self::H264)
126 }
127
128 /// Returns `true` if this is part of the H.265/HEVC family.
129 ///
130 /// # Examples
131 ///
132 /// ```
133 /// use ff_format::codec::VideoCodec;
134 ///
135 /// assert!(VideoCodec::H265.is_h265_family());
136 /// assert!(!VideoCodec::H264.is_h265_family());
137 /// ```
138 #[must_use]
139 pub const fn is_h265_family(&self) -> bool {
140 matches!(self, Self::H265)
141 }
142
143 /// Returns `true` if this is a Google/WebM codec (VP8, VP9).
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// use ff_format::codec::VideoCodec;
149 ///
150 /// assert!(VideoCodec::Vp8.is_vp_family());
151 /// assert!(VideoCodec::Vp9.is_vp_family());
152 /// assert!(!VideoCodec::H264.is_vp_family());
153 /// ```
154 #[must_use]
155 pub const fn is_vp_family(&self) -> bool {
156 matches!(self, Self::Vp8 | Self::Vp9)
157 }
158
159 /// Returns `true` if this is a professional/editing codec.
160 ///
161 /// # Examples
162 ///
163 /// ```
164 /// use ff_format::codec::VideoCodec;
165 ///
166 /// assert!(VideoCodec::ProRes.is_professional());
167 /// assert!(!VideoCodec::H264.is_professional());
168 /// ```
169 #[must_use]
170 pub const fn is_professional(&self) -> bool {
171 matches!(self, Self::ProRes)
172 }
173
174 /// Returns `true` if this codec supports hardware acceleration on most platforms.
175 ///
176 /// # Examples
177 ///
178 /// ```
179 /// use ff_format::codec::VideoCodec;
180 ///
181 /// assert!(VideoCodec::H264.has_hardware_support());
182 /// assert!(VideoCodec::H265.has_hardware_support());
183 /// assert!(!VideoCodec::ProRes.has_hardware_support());
184 /// ```
185 #[must_use]
186 pub const fn has_hardware_support(&self) -> bool {
187 matches!(self, Self::H264 | Self::H265 | Self::Vp9 | Self::Av1)
188 }
189
190 /// Returns `true` if the codec is unknown.
191 ///
192 /// # Examples
193 ///
194 /// ```
195 /// use ff_format::codec::VideoCodec;
196 ///
197 /// assert!(VideoCodec::Unknown.is_unknown());
198 /// assert!(!VideoCodec::H264.is_unknown());
199 /// ```
200 #[must_use]
201 pub const fn is_unknown(&self) -> bool {
202 matches!(self, Self::Unknown)
203 }
204}
205
206impl fmt::Display for VideoCodec {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 write!(f, "{}", self.display_name())
209 }
210}
211
212/// Audio codec identifier.
213///
214/// This enum represents common audio codecs used in media files.
215/// It covers the most widely used codecs while remaining extensible
216/// via the `Unknown` variant.
217///
218/// # Common Usage
219///
220/// - **AAC**: Most common for streaming and mobile
221/// - **MP3**: Legacy but still widely supported
222/// - **Opus**: Excellent quality at low bitrates, used for voice communication
223/// - **FLAC**: Lossless compression
224/// - **PCM**: Uncompressed audio
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
226#[non_exhaustive]
227pub enum AudioCodec {
228 /// AAC (Advanced Audio Coding) - most common lossy codec
229 #[default]
230 Aac,
231 /// MP3 (MPEG-1 Audio Layer 3) - legacy lossy codec
232 Mp3,
233 /// Opus - modern lossy codec, excellent at low bitrates
234 Opus,
235 /// FLAC (Free Lossless Audio Codec) - lossless compression
236 Flac,
237 /// PCM (Pulse Code Modulation) - uncompressed audio
238 Pcm,
239 /// Vorbis - open lossy codec, used in Ogg containers
240 Vorbis,
241 /// AC3 (Dolby Digital) - surround sound codec
242 Ac3,
243 /// EAC3 (Dolby Digital Plus) - enhanced AC3
244 Eac3,
245 /// DTS (Digital Theater Systems) - surround sound codec
246 Dts,
247 /// ALAC (Apple Lossless Audio Codec)
248 Alac,
249 /// Unknown or unsupported codec
250 Unknown,
251}
252
253impl AudioCodec {
254 /// Returns the codec name as a human-readable string.
255 ///
256 /// # Examples
257 ///
258 /// ```
259 /// use ff_format::codec::AudioCodec;
260 ///
261 /// assert_eq!(AudioCodec::Aac.name(), "aac");
262 /// assert_eq!(AudioCodec::Flac.name(), "flac");
263 /// ```
264 #[must_use]
265 pub const fn name(&self) -> &'static str {
266 match self {
267 Self::Aac => "aac",
268 Self::Mp3 => "mp3",
269 Self::Opus => "opus",
270 Self::Flac => "flac",
271 Self::Pcm => "pcm",
272 Self::Vorbis => "vorbis",
273 Self::Ac3 => "ac3",
274 Self::Eac3 => "eac3",
275 Self::Dts => "dts",
276 Self::Alac => "alac",
277 Self::Unknown => "unknown",
278 }
279 }
280
281 /// Returns the human-readable display name for the codec.
282 ///
283 /// # Examples
284 ///
285 /// ```
286 /// use ff_format::codec::AudioCodec;
287 ///
288 /// assert_eq!(AudioCodec::Aac.display_name(), "AAC");
289 /// assert_eq!(AudioCodec::Flac.display_name(), "FLAC");
290 /// ```
291 #[must_use]
292 pub const fn display_name(&self) -> &'static str {
293 match self {
294 Self::Aac => "AAC",
295 Self::Mp3 => "MP3",
296 Self::Opus => "Opus",
297 Self::Flac => "FLAC",
298 Self::Pcm => "PCM",
299 Self::Vorbis => "Vorbis",
300 Self::Ac3 => "Dolby Digital (AC-3)",
301 Self::Eac3 => "Dolby Digital Plus (E-AC-3)",
302 Self::Dts => "DTS",
303 Self::Alac => "Apple Lossless",
304 Self::Unknown => "Unknown",
305 }
306 }
307
308 /// Returns `true` if this is a lossy codec.
309 ///
310 /// Lossy codecs discard some audio data for smaller file sizes.
311 ///
312 /// # Examples
313 ///
314 /// ```
315 /// use ff_format::codec::AudioCodec;
316 ///
317 /// assert!(AudioCodec::Aac.is_lossy());
318 /// assert!(AudioCodec::Mp3.is_lossy());
319 /// assert!(!AudioCodec::Flac.is_lossy());
320 /// ```
321 #[must_use]
322 pub const fn is_lossy(&self) -> bool {
323 matches!(
324 self,
325 Self::Aac | Self::Mp3 | Self::Opus | Self::Vorbis | Self::Ac3 | Self::Eac3 | Self::Dts
326 )
327 }
328
329 /// Returns `true` if this is a lossless codec.
330 ///
331 /// Lossless codecs preserve all audio data.
332 ///
333 /// # Examples
334 ///
335 /// ```
336 /// use ff_format::codec::AudioCodec;
337 ///
338 /// assert!(AudioCodec::Flac.is_lossless());
339 /// assert!(AudioCodec::Pcm.is_lossless());
340 /// assert!(AudioCodec::Alac.is_lossless());
341 /// assert!(!AudioCodec::Aac.is_lossless());
342 /// ```
343 #[must_use]
344 pub const fn is_lossless(&self) -> bool {
345 matches!(self, Self::Flac | Self::Pcm | Self::Alac)
346 }
347
348 /// Returns `true` if this is a surround sound codec.
349 ///
350 /// # Examples
351 ///
352 /// ```
353 /// use ff_format::codec::AudioCodec;
354 ///
355 /// assert!(AudioCodec::Ac3.is_surround());
356 /// assert!(AudioCodec::Dts.is_surround());
357 /// assert!(!AudioCodec::Aac.is_surround());
358 /// ```
359 #[must_use]
360 pub const fn is_surround(&self) -> bool {
361 matches!(self, Self::Ac3 | Self::Eac3 | Self::Dts)
362 }
363
364 /// Returns `true` if the codec is unknown.
365 ///
366 /// # Examples
367 ///
368 /// ```
369 /// use ff_format::codec::AudioCodec;
370 ///
371 /// assert!(AudioCodec::Unknown.is_unknown());
372 /// assert!(!AudioCodec::Aac.is_unknown());
373 /// ```
374 #[must_use]
375 pub const fn is_unknown(&self) -> bool {
376 matches!(self, Self::Unknown)
377 }
378}
379
380impl fmt::Display for AudioCodec {
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 write!(f, "{}", self.display_name())
383 }
384}
385
386#[cfg(test)]
387mod tests {
388 use super::*;
389
390 mod video_codec_tests {
391 use super::*;
392
393 #[test]
394 fn test_names() {
395 assert_eq!(VideoCodec::H264.name(), "h264");
396 assert_eq!(VideoCodec::H265.name(), "hevc");
397 assert_eq!(VideoCodec::Vp8.name(), "vp8");
398 assert_eq!(VideoCodec::Vp9.name(), "vp9");
399 assert_eq!(VideoCodec::Av1.name(), "av1");
400 assert_eq!(VideoCodec::ProRes.name(), "prores");
401 assert_eq!(VideoCodec::Mpeg4.name(), "mpeg4");
402 assert_eq!(VideoCodec::Mpeg2.name(), "mpeg2video");
403 assert_eq!(VideoCodec::Mjpeg.name(), "mjpeg");
404 assert_eq!(VideoCodec::Unknown.name(), "unknown");
405 }
406
407 #[test]
408 fn test_display_names() {
409 assert_eq!(VideoCodec::H264.display_name(), "H.264/AVC");
410 assert_eq!(VideoCodec::H265.display_name(), "H.265/HEVC");
411 assert_eq!(VideoCodec::ProRes.display_name(), "Apple ProRes");
412 }
413
414 #[test]
415 fn test_display() {
416 assert_eq!(format!("{}", VideoCodec::H264), "H.264/AVC");
417 assert_eq!(format!("{}", VideoCodec::Av1), "AV1");
418 }
419
420 #[test]
421 fn test_default() {
422 assert_eq!(VideoCodec::default(), VideoCodec::H264);
423 }
424
425 #[test]
426 fn test_codec_families() {
427 assert!(VideoCodec::H264.is_h264_family());
428 assert!(!VideoCodec::H265.is_h264_family());
429
430 assert!(VideoCodec::H265.is_h265_family());
431 assert!(!VideoCodec::H264.is_h265_family());
432
433 assert!(VideoCodec::Vp8.is_vp_family());
434 assert!(VideoCodec::Vp9.is_vp_family());
435 assert!(!VideoCodec::H264.is_vp_family());
436 }
437
438 #[test]
439 fn test_is_professional() {
440 assert!(VideoCodec::ProRes.is_professional());
441 assert!(!VideoCodec::H264.is_professional());
442 assert!(!VideoCodec::Unknown.is_professional());
443 }
444
445 #[test]
446 fn test_hardware_support() {
447 assert!(VideoCodec::H264.has_hardware_support());
448 assert!(VideoCodec::H265.has_hardware_support());
449 assert!(VideoCodec::Vp9.has_hardware_support());
450 assert!(VideoCodec::Av1.has_hardware_support());
451 assert!(!VideoCodec::ProRes.has_hardware_support());
452 assert!(!VideoCodec::Mjpeg.has_hardware_support());
453 }
454
455 #[test]
456 fn test_is_unknown() {
457 assert!(VideoCodec::Unknown.is_unknown());
458 assert!(!VideoCodec::H264.is_unknown());
459 }
460
461 #[test]
462 fn test_debug() {
463 assert_eq!(format!("{:?}", VideoCodec::H264), "H264");
464 assert_eq!(format!("{:?}", VideoCodec::H265), "H265");
465 }
466
467 #[test]
468 fn test_equality_and_hash() {
469 use std::collections::HashSet;
470
471 assert_eq!(VideoCodec::H264, VideoCodec::H264);
472 assert_ne!(VideoCodec::H264, VideoCodec::H265);
473
474 let mut set = HashSet::new();
475 set.insert(VideoCodec::H264);
476 set.insert(VideoCodec::H265);
477 assert!(set.contains(&VideoCodec::H264));
478 assert!(!set.contains(&VideoCodec::Vp9));
479 }
480
481 #[test]
482 fn test_copy() {
483 let codec = VideoCodec::H264;
484 let copied = codec;
485 assert_eq!(codec, copied);
486 }
487 }
488
489 mod audio_codec_tests {
490 use super::*;
491
492 #[test]
493 fn test_names() {
494 assert_eq!(AudioCodec::Aac.name(), "aac");
495 assert_eq!(AudioCodec::Mp3.name(), "mp3");
496 assert_eq!(AudioCodec::Opus.name(), "opus");
497 assert_eq!(AudioCodec::Flac.name(), "flac");
498 assert_eq!(AudioCodec::Pcm.name(), "pcm");
499 assert_eq!(AudioCodec::Vorbis.name(), "vorbis");
500 assert_eq!(AudioCodec::Ac3.name(), "ac3");
501 assert_eq!(AudioCodec::Eac3.name(), "eac3");
502 assert_eq!(AudioCodec::Dts.name(), "dts");
503 assert_eq!(AudioCodec::Alac.name(), "alac");
504 assert_eq!(AudioCodec::Unknown.name(), "unknown");
505 }
506
507 #[test]
508 fn test_display_names() {
509 assert_eq!(AudioCodec::Aac.display_name(), "AAC");
510 assert_eq!(AudioCodec::Flac.display_name(), "FLAC");
511 assert_eq!(AudioCodec::Ac3.display_name(), "Dolby Digital (AC-3)");
512 }
513
514 #[test]
515 fn test_display() {
516 assert_eq!(format!("{}", AudioCodec::Aac), "AAC");
517 assert_eq!(format!("{}", AudioCodec::Opus), "Opus");
518 }
519
520 #[test]
521 fn test_default() {
522 assert_eq!(AudioCodec::default(), AudioCodec::Aac);
523 }
524
525 #[test]
526 fn test_lossy_lossless() {
527 // Lossy codecs
528 assert!(AudioCodec::Aac.is_lossy());
529 assert!(AudioCodec::Mp3.is_lossy());
530 assert!(AudioCodec::Opus.is_lossy());
531 assert!(AudioCodec::Vorbis.is_lossy());
532 assert!(AudioCodec::Ac3.is_lossy());
533 assert!(AudioCodec::Eac3.is_lossy());
534 assert!(AudioCodec::Dts.is_lossy());
535
536 // Lossless codecs
537 assert!(AudioCodec::Flac.is_lossless());
538 assert!(AudioCodec::Pcm.is_lossless());
539 assert!(AudioCodec::Alac.is_lossless());
540
541 // Mutual exclusion
542 assert!(!AudioCodec::Aac.is_lossless());
543 assert!(!AudioCodec::Flac.is_lossy());
544 }
545
546 #[test]
547 fn test_surround() {
548 assert!(AudioCodec::Ac3.is_surround());
549 assert!(AudioCodec::Eac3.is_surround());
550 assert!(AudioCodec::Dts.is_surround());
551 assert!(!AudioCodec::Aac.is_surround());
552 assert!(!AudioCodec::Flac.is_surround());
553 }
554
555 #[test]
556 fn test_is_unknown() {
557 assert!(AudioCodec::Unknown.is_unknown());
558 assert!(!AudioCodec::Aac.is_unknown());
559 }
560
561 #[test]
562 fn test_debug() {
563 assert_eq!(format!("{:?}", AudioCodec::Aac), "Aac");
564 assert_eq!(format!("{:?}", AudioCodec::Flac), "Flac");
565 }
566
567 #[test]
568 fn test_equality_and_hash() {
569 use std::collections::HashSet;
570
571 assert_eq!(AudioCodec::Aac, AudioCodec::Aac);
572 assert_ne!(AudioCodec::Aac, AudioCodec::Mp3);
573
574 let mut set = HashSet::new();
575 set.insert(AudioCodec::Aac);
576 set.insert(AudioCodec::Flac);
577 assert!(set.contains(&AudioCodec::Aac));
578 assert!(!set.contains(&AudioCodec::Opus));
579 }
580
581 #[test]
582 fn test_copy() {
583 let codec = AudioCodec::Aac;
584 let copied = codec;
585 assert_eq!(codec, copied);
586 }
587 }
588}