Skip to main content

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}