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    /// Avid DNxHD/DNxHR — professional editing codec for post-production
51    DnxHd,
52    /// MPEG-4 Part 2 - older codec, legacy support
53    Mpeg4,
54    /// MPEG-2 Video - DVD and broadcast standard
55    Mpeg2,
56    /// MJPEG - Motion JPEG, used by some cameras
57    Mjpeg,
58    /// Unknown or unsupported codec
59    Unknown,
60}
61
62impl VideoCodec {
63    /// Returns the codec name as a human-readable string.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use ff_format::codec::VideoCodec;
69    ///
70    /// assert_eq!(VideoCodec::H264.name(), "h264");
71    /// assert_eq!(VideoCodec::H265.name(), "hevc");
72    /// ```
73    #[must_use]
74    pub const fn name(&self) -> &'static str {
75        match self {
76            Self::H264 => "h264",
77            Self::H265 => "hevc",
78            Self::Vp8 => "vp8",
79            Self::Vp9 => "vp9",
80            Self::Av1 => "av1",
81            Self::ProRes => "prores",
82            Self::DnxHd => "dnxhd",
83            Self::Mpeg4 => "mpeg4",
84            Self::Mpeg2 => "mpeg2video",
85            Self::Mjpeg => "mjpeg",
86            Self::Unknown => "unknown",
87        }
88    }
89
90    /// Returns the human-readable display name for the codec.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use ff_format::codec::VideoCodec;
96    ///
97    /// assert_eq!(VideoCodec::H264.display_name(), "H.264/AVC");
98    /// assert_eq!(VideoCodec::H265.display_name(), "H.265/HEVC");
99    /// ```
100    #[must_use]
101    pub const fn display_name(&self) -> &'static str {
102        match self {
103            Self::H264 => "H.264/AVC",
104            Self::H265 => "H.265/HEVC",
105            Self::Vp8 => "VP8",
106            Self::Vp9 => "VP9",
107            Self::Av1 => "AV1",
108            Self::ProRes => "Apple ProRes",
109            Self::DnxHd => "Avid DNxHD/DNxHR",
110            Self::Mpeg4 => "MPEG-4 Part 2",
111            Self::Mpeg2 => "MPEG-2",
112            Self::Mjpeg => "Motion JPEG",
113            Self::Unknown => "Unknown",
114        }
115    }
116
117    /// Returns `true` if this is part of the H.264 family.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use ff_format::codec::VideoCodec;
123    ///
124    /// assert!(VideoCodec::H264.is_h264_family());
125    /// assert!(!VideoCodec::H265.is_h264_family());
126    /// ```
127    #[must_use]
128    pub const fn is_h264_family(&self) -> bool {
129        matches!(self, Self::H264)
130    }
131
132    /// Returns `true` if this is part of the H.265/HEVC family.
133    ///
134    /// # Examples
135    ///
136    /// ```
137    /// use ff_format::codec::VideoCodec;
138    ///
139    /// assert!(VideoCodec::H265.is_h265_family());
140    /// assert!(!VideoCodec::H264.is_h265_family());
141    /// ```
142    #[must_use]
143    pub const fn is_h265_family(&self) -> bool {
144        matches!(self, Self::H265)
145    }
146
147    /// Returns `true` if this is a Google/WebM codec (VP8, VP9).
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use ff_format::codec::VideoCodec;
153    ///
154    /// assert!(VideoCodec::Vp8.is_vp_family());
155    /// assert!(VideoCodec::Vp9.is_vp_family());
156    /// assert!(!VideoCodec::H264.is_vp_family());
157    /// ```
158    #[must_use]
159    pub const fn is_vp_family(&self) -> bool {
160        matches!(self, Self::Vp8 | Self::Vp9)
161    }
162
163    /// Returns `true` if this is a professional/editing codec.
164    ///
165    /// # Examples
166    ///
167    /// ```
168    /// use ff_format::codec::VideoCodec;
169    ///
170    /// assert!(VideoCodec::ProRes.is_professional());
171    /// assert!(!VideoCodec::H264.is_professional());
172    /// ```
173    #[must_use]
174    pub const fn is_professional(&self) -> bool {
175        matches!(self, Self::ProRes | Self::DnxHd)
176    }
177
178    /// Returns `true` if this codec supports hardware acceleration on most platforms.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// use ff_format::codec::VideoCodec;
184    ///
185    /// assert!(VideoCodec::H264.has_hardware_support());
186    /// assert!(VideoCodec::H265.has_hardware_support());
187    /// assert!(!VideoCodec::ProRes.has_hardware_support());
188    /// ```
189    #[must_use]
190    pub const fn has_hardware_support(&self) -> bool {
191        matches!(self, Self::H264 | Self::H265 | Self::Vp9 | Self::Av1)
192    }
193
194    /// Returns `true` if the codec is unknown.
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// use ff_format::codec::VideoCodec;
200    ///
201    /// assert!(VideoCodec::Unknown.is_unknown());
202    /// assert!(!VideoCodec::H264.is_unknown());
203    /// ```
204    #[must_use]
205    pub const fn is_unknown(&self) -> bool {
206        matches!(self, Self::Unknown)
207    }
208}
209
210impl fmt::Display for VideoCodec {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        write!(f, "{}", self.display_name())
213    }
214}
215
216/// Audio codec identifier.
217///
218/// This enum represents common audio codecs used in media files.
219/// It covers the most widely used codecs while remaining extensible
220/// via the `Unknown` variant.
221///
222/// # Common Usage
223///
224/// - **AAC**: Most common for streaming and mobile
225/// - **MP3**: Legacy but still widely supported
226/// - **Opus**: Excellent quality at low bitrates, used for voice communication
227/// - **FLAC**: Lossless compression
228/// - **PCM**: Uncompressed audio
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
230#[non_exhaustive]
231pub enum AudioCodec {
232    /// AAC (Advanced Audio Coding) - most common lossy codec
233    #[default]
234    Aac,
235    /// MP3 (MPEG-1 Audio Layer 3) - legacy lossy codec
236    Mp3,
237    /// Opus - modern lossy codec, excellent at low bitrates
238    Opus,
239    /// FLAC (Free Lossless Audio Codec) - lossless compression
240    Flac,
241    /// PCM (Pulse Code Modulation) - uncompressed audio
242    Pcm,
243    /// Vorbis - open lossy codec, used in Ogg containers
244    Vorbis,
245    /// AC3 (Dolby Digital) - surround sound codec
246    Ac3,
247    /// EAC3 (Dolby Digital Plus) - enhanced AC3
248    Eac3,
249    /// DTS (Digital Theater Systems) - surround sound codec
250    Dts,
251    /// ALAC (Apple Lossless Audio Codec)
252    Alac,
253    /// Unknown or unsupported codec
254    Unknown,
255}
256
257impl AudioCodec {
258    /// Returns the codec name as a human-readable string.
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// use ff_format::codec::AudioCodec;
264    ///
265    /// assert_eq!(AudioCodec::Aac.name(), "aac");
266    /// assert_eq!(AudioCodec::Flac.name(), "flac");
267    /// ```
268    #[must_use]
269    pub const fn name(&self) -> &'static str {
270        match self {
271            Self::Aac => "aac",
272            Self::Mp3 => "mp3",
273            Self::Opus => "opus",
274            Self::Flac => "flac",
275            Self::Pcm => "pcm",
276            Self::Vorbis => "vorbis",
277            Self::Ac3 => "ac3",
278            Self::Eac3 => "eac3",
279            Self::Dts => "dts",
280            Self::Alac => "alac",
281            Self::Unknown => "unknown",
282        }
283    }
284
285    /// Returns the human-readable display name for the codec.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// use ff_format::codec::AudioCodec;
291    ///
292    /// assert_eq!(AudioCodec::Aac.display_name(), "AAC");
293    /// assert_eq!(AudioCodec::Flac.display_name(), "FLAC");
294    /// ```
295    #[must_use]
296    pub const fn display_name(&self) -> &'static str {
297        match self {
298            Self::Aac => "AAC",
299            Self::Mp3 => "MP3",
300            Self::Opus => "Opus",
301            Self::Flac => "FLAC",
302            Self::Pcm => "PCM",
303            Self::Vorbis => "Vorbis",
304            Self::Ac3 => "Dolby Digital (AC-3)",
305            Self::Eac3 => "Dolby Digital Plus (E-AC-3)",
306            Self::Dts => "DTS",
307            Self::Alac => "Apple Lossless",
308            Self::Unknown => "Unknown",
309        }
310    }
311
312    /// Returns `true` if this is a lossy codec.
313    ///
314    /// Lossy codecs discard some audio data for smaller file sizes.
315    ///
316    /// # Examples
317    ///
318    /// ```
319    /// use ff_format::codec::AudioCodec;
320    ///
321    /// assert!(AudioCodec::Aac.is_lossy());
322    /// assert!(AudioCodec::Mp3.is_lossy());
323    /// assert!(!AudioCodec::Flac.is_lossy());
324    /// ```
325    #[must_use]
326    pub const fn is_lossy(&self) -> bool {
327        matches!(
328            self,
329            Self::Aac | Self::Mp3 | Self::Opus | Self::Vorbis | Self::Ac3 | Self::Eac3 | Self::Dts
330        )
331    }
332
333    /// Returns `true` if this is a lossless codec.
334    ///
335    /// Lossless codecs preserve all audio data.
336    ///
337    /// # Examples
338    ///
339    /// ```
340    /// use ff_format::codec::AudioCodec;
341    ///
342    /// assert!(AudioCodec::Flac.is_lossless());
343    /// assert!(AudioCodec::Pcm.is_lossless());
344    /// assert!(AudioCodec::Alac.is_lossless());
345    /// assert!(!AudioCodec::Aac.is_lossless());
346    /// ```
347    #[must_use]
348    pub const fn is_lossless(&self) -> bool {
349        matches!(self, Self::Flac | Self::Pcm | Self::Alac)
350    }
351
352    /// Returns `true` if this is a surround sound codec.
353    ///
354    /// # Examples
355    ///
356    /// ```
357    /// use ff_format::codec::AudioCodec;
358    ///
359    /// assert!(AudioCodec::Ac3.is_surround());
360    /// assert!(AudioCodec::Dts.is_surround());
361    /// assert!(!AudioCodec::Aac.is_surround());
362    /// ```
363    #[must_use]
364    pub const fn is_surround(&self) -> bool {
365        matches!(self, Self::Ac3 | Self::Eac3 | Self::Dts)
366    }
367
368    /// Returns `true` if the codec is unknown.
369    ///
370    /// # Examples
371    ///
372    /// ```
373    /// use ff_format::codec::AudioCodec;
374    ///
375    /// assert!(AudioCodec::Unknown.is_unknown());
376    /// assert!(!AudioCodec::Aac.is_unknown());
377    /// ```
378    #[must_use]
379    pub const fn is_unknown(&self) -> bool {
380        matches!(self, Self::Unknown)
381    }
382}
383
384impl fmt::Display for AudioCodec {
385    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386        write!(f, "{}", self.display_name())
387    }
388}
389
390/// Subtitle codec identifier.
391///
392/// This enum represents common subtitle codecs used in media files.
393/// It covers text-based and bitmap-based formats while remaining extensible
394/// via the `Other` variant.
395///
396/// Note: No `Copy` or `Default` derive because `Other(String)` is not `Copy`.
397#[derive(Debug, Clone, PartialEq, Eq, Hash)]
398pub enum SubtitleCodec {
399    /// `SubRip` / SRT — text-based timed subtitles
400    Srt,
401    /// ASS/SSA (Advanced `SubStation` Alpha) — styled text subtitles
402    Ass,
403    /// DVB bitmap subtitles (digital broadcast)
404    Dvb,
405    /// HDMV/PGS — Blu-ray bitmap subtitles
406    Hdmv,
407    /// `WebVTT` — web-standard text subtitles
408    Webvtt,
409    /// Unrecognized codec; raw codec name stored for transparency
410    Other(String),
411}
412
413impl SubtitleCodec {
414    /// Returns the codec name as a short string.
415    ///
416    /// # Examples
417    ///
418    /// ```
419    /// use ff_format::codec::SubtitleCodec;
420    ///
421    /// assert_eq!(SubtitleCodec::Srt.name(), "srt");
422    /// assert_eq!(SubtitleCodec::Ass.name(), "ass");
423    /// ```
424    #[must_use]
425    pub fn name(&self) -> &str {
426        match self {
427            Self::Srt => "srt",
428            Self::Ass => "ass",
429            Self::Dvb => "dvb_subtitle",
430            Self::Hdmv => "hdmv_pgs_subtitle",
431            Self::Webvtt => "webvtt",
432            Self::Other(name) => name.as_str(),
433        }
434    }
435
436    /// Returns the human-readable display name for the codec.
437    ///
438    /// # Examples
439    ///
440    /// ```
441    /// use ff_format::codec::SubtitleCodec;
442    ///
443    /// assert_eq!(SubtitleCodec::Srt.display_name(), "SubRip (SRT)");
444    /// assert_eq!(SubtitleCodec::Hdmv.display_name(), "HDMV/PGS");
445    /// ```
446    #[must_use]
447    pub fn display_name(&self) -> &str {
448        match self {
449            Self::Srt => "SubRip (SRT)",
450            Self::Ass => "ASS/SSA",
451            Self::Dvb => "DVB Subtitle",
452            Self::Hdmv => "HDMV/PGS",
453            Self::Webvtt => "WebVTT",
454            Self::Other(name) => name.as_str(),
455        }
456    }
457
458    /// Returns `true` if this is a text-based subtitle codec.
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// use ff_format::codec::SubtitleCodec;
464    ///
465    /// assert!(SubtitleCodec::Srt.is_text_based());
466    /// assert!(SubtitleCodec::Ass.is_text_based());
467    /// assert!(!SubtitleCodec::Dvb.is_text_based());
468    /// ```
469    #[must_use]
470    pub fn is_text_based(&self) -> bool {
471        matches!(self, Self::Srt | Self::Ass | Self::Webvtt)
472    }
473
474    /// Returns `true` if this is a bitmap-based subtitle codec.
475    ///
476    /// # Examples
477    ///
478    /// ```
479    /// use ff_format::codec::SubtitleCodec;
480    ///
481    /// assert!(SubtitleCodec::Dvb.is_bitmap_based());
482    /// assert!(SubtitleCodec::Hdmv.is_bitmap_based());
483    /// assert!(!SubtitleCodec::Srt.is_bitmap_based());
484    /// ```
485    #[must_use]
486    pub fn is_bitmap_based(&self) -> bool {
487        matches!(self, Self::Dvb | Self::Hdmv)
488    }
489
490    /// Returns `true` if the codec is unrecognized.
491    ///
492    /// # Examples
493    ///
494    /// ```
495    /// use ff_format::codec::SubtitleCodec;
496    ///
497    /// assert!(SubtitleCodec::Other("dvd_subtitle".to_string()).is_unknown());
498    /// assert!(!SubtitleCodec::Srt.is_unknown());
499    /// ```
500    #[must_use]
501    pub fn is_unknown(&self) -> bool {
502        matches!(self, Self::Other(_))
503    }
504}
505
506impl fmt::Display for SubtitleCodec {
507    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508        write!(f, "{}", self.display_name())
509    }
510}
511
512#[cfg(test)]
513mod tests {
514    use super::*;
515
516    mod video_codec_tests {
517        use super::*;
518
519        #[test]
520        fn test_names() {
521            assert_eq!(VideoCodec::H264.name(), "h264");
522            assert_eq!(VideoCodec::H265.name(), "hevc");
523            assert_eq!(VideoCodec::Vp8.name(), "vp8");
524            assert_eq!(VideoCodec::Vp9.name(), "vp9");
525            assert_eq!(VideoCodec::Av1.name(), "av1");
526            assert_eq!(VideoCodec::ProRes.name(), "prores");
527            assert_eq!(VideoCodec::DnxHd.name(), "dnxhd");
528            assert_eq!(VideoCodec::Mpeg4.name(), "mpeg4");
529            assert_eq!(VideoCodec::Mpeg2.name(), "mpeg2video");
530            assert_eq!(VideoCodec::Mjpeg.name(), "mjpeg");
531            assert_eq!(VideoCodec::Unknown.name(), "unknown");
532        }
533
534        #[test]
535        fn test_display_names() {
536            assert_eq!(VideoCodec::H264.display_name(), "H.264/AVC");
537            assert_eq!(VideoCodec::H265.display_name(), "H.265/HEVC");
538            assert_eq!(VideoCodec::ProRes.display_name(), "Apple ProRes");
539        }
540
541        #[test]
542        fn test_display() {
543            assert_eq!(format!("{}", VideoCodec::H264), "H.264/AVC");
544            assert_eq!(format!("{}", VideoCodec::Av1), "AV1");
545        }
546
547        #[test]
548        fn test_default() {
549            assert_eq!(VideoCodec::default(), VideoCodec::H264);
550        }
551
552        #[test]
553        fn test_codec_families() {
554            assert!(VideoCodec::H264.is_h264_family());
555            assert!(!VideoCodec::H265.is_h264_family());
556
557            assert!(VideoCodec::H265.is_h265_family());
558            assert!(!VideoCodec::H264.is_h265_family());
559
560            assert!(VideoCodec::Vp8.is_vp_family());
561            assert!(VideoCodec::Vp9.is_vp_family());
562            assert!(!VideoCodec::H264.is_vp_family());
563        }
564
565        #[test]
566        fn test_is_professional() {
567            assert!(VideoCodec::ProRes.is_professional());
568            assert!(VideoCodec::DnxHd.is_professional());
569            assert!(!VideoCodec::H264.is_professional());
570            assert!(!VideoCodec::Unknown.is_professional());
571        }
572
573        #[test]
574        fn dnxhd_should_have_correct_name_and_display_name() {
575            assert_eq!(VideoCodec::DnxHd.name(), "dnxhd");
576            assert_eq!(VideoCodec::DnxHd.display_name(), "Avid DNxHD/DNxHR");
577        }
578
579        #[test]
580        fn dnxhd_should_be_professional() {
581            assert!(VideoCodec::DnxHd.is_professional());
582            assert!(!VideoCodec::DnxHd.is_h264_family());
583            assert!(!VideoCodec::DnxHd.is_h265_family());
584            assert!(!VideoCodec::DnxHd.is_vp_family());
585            assert!(!VideoCodec::DnxHd.is_unknown());
586        }
587
588        #[test]
589        fn dnxhd_should_not_have_hardware_support() {
590            assert!(!VideoCodec::DnxHd.has_hardware_support());
591        }
592
593        #[test]
594        fn test_hardware_support() {
595            assert!(VideoCodec::H264.has_hardware_support());
596            assert!(VideoCodec::H265.has_hardware_support());
597            assert!(VideoCodec::Vp9.has_hardware_support());
598            assert!(VideoCodec::Av1.has_hardware_support());
599            assert!(!VideoCodec::ProRes.has_hardware_support());
600            assert!(!VideoCodec::Mjpeg.has_hardware_support());
601        }
602
603        #[test]
604        fn test_is_unknown() {
605            assert!(VideoCodec::Unknown.is_unknown());
606            assert!(!VideoCodec::H264.is_unknown());
607        }
608
609        #[test]
610        fn test_debug() {
611            assert_eq!(format!("{:?}", VideoCodec::H264), "H264");
612            assert_eq!(format!("{:?}", VideoCodec::H265), "H265");
613        }
614
615        #[test]
616        fn test_equality_and_hash() {
617            use std::collections::HashSet;
618
619            assert_eq!(VideoCodec::H264, VideoCodec::H264);
620            assert_ne!(VideoCodec::H264, VideoCodec::H265);
621
622            let mut set = HashSet::new();
623            set.insert(VideoCodec::H264);
624            set.insert(VideoCodec::H265);
625            assert!(set.contains(&VideoCodec::H264));
626            assert!(!set.contains(&VideoCodec::Vp9));
627        }
628
629        #[test]
630        fn test_copy() {
631            let codec = VideoCodec::H264;
632            let copied = codec;
633            assert_eq!(codec, copied);
634        }
635    }
636
637    mod subtitle_codec_tests {
638        use super::*;
639
640        #[test]
641        fn name_should_return_short_codec_name() {
642            assert_eq!(SubtitleCodec::Srt.name(), "srt");
643            assert_eq!(SubtitleCodec::Ass.name(), "ass");
644            assert_eq!(SubtitleCodec::Dvb.name(), "dvb_subtitle");
645            assert_eq!(SubtitleCodec::Hdmv.name(), "hdmv_pgs_subtitle");
646            assert_eq!(SubtitleCodec::Webvtt.name(), "webvtt");
647            assert_eq!(
648                SubtitleCodec::Other("dvd_subtitle".to_string()).name(),
649                "dvd_subtitle"
650            );
651        }
652
653        #[test]
654        fn display_name_should_return_human_readable_name() {
655            assert_eq!(SubtitleCodec::Srt.display_name(), "SubRip (SRT)");
656            assert_eq!(SubtitleCodec::Ass.display_name(), "ASS/SSA");
657            assert_eq!(SubtitleCodec::Dvb.display_name(), "DVB Subtitle");
658            assert_eq!(SubtitleCodec::Hdmv.display_name(), "HDMV/PGS");
659            assert_eq!(SubtitleCodec::Webvtt.display_name(), "WebVTT");
660        }
661
662        #[test]
663        fn display_should_use_display_name() {
664            assert_eq!(format!("{}", SubtitleCodec::Srt), "SubRip (SRT)");
665            assert_eq!(format!("{}", SubtitleCodec::Hdmv), "HDMV/PGS");
666        }
667
668        #[test]
669        fn is_text_based_should_return_true_for_text_codecs() {
670            assert!(SubtitleCodec::Srt.is_text_based());
671            assert!(SubtitleCodec::Ass.is_text_based());
672            assert!(SubtitleCodec::Webvtt.is_text_based());
673            assert!(!SubtitleCodec::Dvb.is_text_based());
674            assert!(!SubtitleCodec::Hdmv.is_text_based());
675        }
676
677        #[test]
678        fn is_bitmap_based_should_return_true_for_bitmap_codecs() {
679            assert!(SubtitleCodec::Dvb.is_bitmap_based());
680            assert!(SubtitleCodec::Hdmv.is_bitmap_based());
681            assert!(!SubtitleCodec::Srt.is_bitmap_based());
682            assert!(!SubtitleCodec::Ass.is_bitmap_based());
683            assert!(!SubtitleCodec::Webvtt.is_bitmap_based());
684        }
685
686        #[test]
687        fn is_unknown_should_return_true_only_for_other_variant() {
688            assert!(SubtitleCodec::Other("dvd_subtitle".to_string()).is_unknown());
689            assert!(!SubtitleCodec::Srt.is_unknown());
690            assert!(!SubtitleCodec::Dvb.is_unknown());
691        }
692
693        #[test]
694        fn equality_should_compare_by_value() {
695            assert_eq!(SubtitleCodec::Srt, SubtitleCodec::Srt);
696            assert_ne!(SubtitleCodec::Srt, SubtitleCodec::Ass);
697            assert_eq!(
698                SubtitleCodec::Other("foo".to_string()),
699                SubtitleCodec::Other("foo".to_string())
700            );
701            assert_ne!(
702                SubtitleCodec::Other("foo".to_string()),
703                SubtitleCodec::Other("bar".to_string())
704            );
705        }
706
707        #[test]
708        fn clone_should_produce_equal_value() {
709            let codec = SubtitleCodec::Other("test".to_string());
710            let cloned = codec.clone();
711            assert_eq!(codec, cloned);
712        }
713    }
714
715    mod audio_codec_tests {
716        use super::*;
717
718        #[test]
719        fn test_names() {
720            assert_eq!(AudioCodec::Aac.name(), "aac");
721            assert_eq!(AudioCodec::Mp3.name(), "mp3");
722            assert_eq!(AudioCodec::Opus.name(), "opus");
723            assert_eq!(AudioCodec::Flac.name(), "flac");
724            assert_eq!(AudioCodec::Pcm.name(), "pcm");
725            assert_eq!(AudioCodec::Vorbis.name(), "vorbis");
726            assert_eq!(AudioCodec::Ac3.name(), "ac3");
727            assert_eq!(AudioCodec::Eac3.name(), "eac3");
728            assert_eq!(AudioCodec::Dts.name(), "dts");
729            assert_eq!(AudioCodec::Alac.name(), "alac");
730            assert_eq!(AudioCodec::Unknown.name(), "unknown");
731        }
732
733        #[test]
734        fn test_display_names() {
735            assert_eq!(AudioCodec::Aac.display_name(), "AAC");
736            assert_eq!(AudioCodec::Flac.display_name(), "FLAC");
737            assert_eq!(AudioCodec::Ac3.display_name(), "Dolby Digital (AC-3)");
738        }
739
740        #[test]
741        fn test_display() {
742            assert_eq!(format!("{}", AudioCodec::Aac), "AAC");
743            assert_eq!(format!("{}", AudioCodec::Opus), "Opus");
744        }
745
746        #[test]
747        fn test_default() {
748            assert_eq!(AudioCodec::default(), AudioCodec::Aac);
749        }
750
751        #[test]
752        fn test_lossy_lossless() {
753            // Lossy codecs
754            assert!(AudioCodec::Aac.is_lossy());
755            assert!(AudioCodec::Mp3.is_lossy());
756            assert!(AudioCodec::Opus.is_lossy());
757            assert!(AudioCodec::Vorbis.is_lossy());
758            assert!(AudioCodec::Ac3.is_lossy());
759            assert!(AudioCodec::Eac3.is_lossy());
760            assert!(AudioCodec::Dts.is_lossy());
761
762            // Lossless codecs
763            assert!(AudioCodec::Flac.is_lossless());
764            assert!(AudioCodec::Pcm.is_lossless());
765            assert!(AudioCodec::Alac.is_lossless());
766
767            // Mutual exclusion
768            assert!(!AudioCodec::Aac.is_lossless());
769            assert!(!AudioCodec::Flac.is_lossy());
770        }
771
772        #[test]
773        fn test_surround() {
774            assert!(AudioCodec::Ac3.is_surround());
775            assert!(AudioCodec::Eac3.is_surround());
776            assert!(AudioCodec::Dts.is_surround());
777            assert!(!AudioCodec::Aac.is_surround());
778            assert!(!AudioCodec::Flac.is_surround());
779        }
780
781        #[test]
782        fn test_is_unknown() {
783            assert!(AudioCodec::Unknown.is_unknown());
784            assert!(!AudioCodec::Aac.is_unknown());
785        }
786
787        #[test]
788        fn test_debug() {
789            assert_eq!(format!("{:?}", AudioCodec::Aac), "Aac");
790            assert_eq!(format!("{:?}", AudioCodec::Flac), "Flac");
791        }
792
793        #[test]
794        fn test_equality_and_hash() {
795            use std::collections::HashSet;
796
797            assert_eq!(AudioCodec::Aac, AudioCodec::Aac);
798            assert_ne!(AudioCodec::Aac, AudioCodec::Mp3);
799
800            let mut set = HashSet::new();
801            set.insert(AudioCodec::Aac);
802            set.insert(AudioCodec::Flac);
803            assert!(set.contains(&AudioCodec::Aac));
804            assert!(!set.contains(&AudioCodec::Opus));
805        }
806
807        #[test]
808        fn test_copy() {
809            let codec = AudioCodec::Aac;
810            let copied = codec;
811            assert_eq!(codec, copied);
812        }
813    }
814}