1use std::fmt;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
35#[non_exhaustive]
36pub enum VideoCodec {
37 #[default]
39 H264,
40 H265,
42 Vp8,
44 Vp9,
46 Av1,
48 Av1Svt,
52 ProRes,
54 DnxHd,
56 Mpeg4,
58 Mpeg2,
60 Mjpeg,
62 Png,
64 Unknown,
66}
67
68impl VideoCodec {
69 #[must_use]
80 pub const fn name(&self) -> &'static str {
81 match self {
82 Self::H264 => "h264",
83 Self::H265 => "hevc",
84 Self::Vp8 => "vp8",
85 Self::Vp9 => "vp9",
86 Self::Av1 | Self::Av1Svt => "av1",
87 Self::ProRes => "prores",
88 Self::DnxHd => "dnxhd",
89 Self::Mpeg4 => "mpeg4",
90 Self::Mpeg2 => "mpeg2video",
91 Self::Mjpeg => "mjpeg",
92 Self::Png => "png",
93 Self::Unknown => "unknown",
94 }
95 }
96
97 #[must_use]
108 pub const fn display_name(&self) -> &'static str {
109 match self {
110 Self::H264 => "H.264/AVC",
111 Self::H265 => "H.265/HEVC",
112 Self::Vp8 => "VP8",
113 Self::Vp9 => "VP9",
114 Self::Av1 => "AV1",
115 Self::Av1Svt => "AV1 (SVT)",
116 Self::ProRes => "Apple ProRes",
117 Self::DnxHd => "Avid DNxHD/DNxHR",
118 Self::Mpeg4 => "MPEG-4 Part 2",
119 Self::Mpeg2 => "MPEG-2",
120 Self::Mjpeg => "Motion JPEG",
121 Self::Png => "PNG",
122 Self::Unknown => "Unknown",
123 }
124 }
125
126 #[must_use]
137 pub const fn is_h264_family(&self) -> bool {
138 matches!(self, Self::H264)
139 }
140
141 #[must_use]
152 pub const fn is_h265_family(&self) -> bool {
153 matches!(self, Self::H265)
154 }
155
156 #[must_use]
168 pub const fn is_vp_family(&self) -> bool {
169 matches!(self, Self::Vp8 | Self::Vp9)
170 }
171
172 #[must_use]
183 pub const fn is_professional(&self) -> bool {
184 matches!(self, Self::ProRes | Self::DnxHd)
185 }
186
187 #[must_use]
199 pub const fn has_hardware_support(&self) -> bool {
200 matches!(self, Self::H264 | Self::H265 | Self::Vp9 | Self::Av1)
201 }
202
203 #[must_use]
214 pub const fn is_unknown(&self) -> bool {
215 matches!(self, Self::Unknown)
216 }
217}
218
219impl fmt::Display for VideoCodec {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(f, "{}", self.display_name())
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
239#[non_exhaustive]
240pub enum AudioCodec {
241 #[default]
243 Aac,
244 Mp3,
246 Opus,
248 Flac,
250 Pcm,
252 Pcm16,
254 Pcm24,
256 Vorbis,
258 Ac3,
260 Eac3,
262 Dts,
264 Alac,
266 Unknown,
268}
269
270impl AudioCodec {
271 #[must_use]
282 pub const fn name(&self) -> &'static str {
283 match self {
284 Self::Aac => "aac",
285 Self::Mp3 => "mp3",
286 Self::Opus => "opus",
287 Self::Flac => "flac",
288 Self::Pcm => "pcm",
289 Self::Pcm16 => "pcm_s16le",
290 Self::Pcm24 => "pcm_s24le",
291 Self::Vorbis => "vorbis",
292 Self::Ac3 => "ac3",
293 Self::Eac3 => "eac3",
294 Self::Dts => "dts",
295 Self::Alac => "alac",
296 Self::Unknown => "unknown",
297 }
298 }
299
300 #[must_use]
311 pub const fn display_name(&self) -> &'static str {
312 match self {
313 Self::Aac => "AAC",
314 Self::Mp3 => "MP3",
315 Self::Opus => "Opus",
316 Self::Flac => "FLAC",
317 Self::Pcm => "PCM",
318 Self::Pcm16 => "PCM 16-bit",
319 Self::Pcm24 => "PCM 24-bit",
320 Self::Vorbis => "Vorbis",
321 Self::Ac3 => "Dolby Digital (AC-3)",
322 Self::Eac3 => "Dolby Digital Plus (E-AC-3)",
323 Self::Dts => "DTS",
324 Self::Alac => "Apple Lossless",
325 Self::Unknown => "Unknown",
326 }
327 }
328
329 #[must_use]
343 pub const fn is_lossy(&self) -> bool {
344 matches!(
345 self,
346 Self::Aac | Self::Mp3 | Self::Opus | Self::Vorbis | Self::Ac3 | Self::Eac3 | Self::Dts
347 )
348 }
349
350 #[must_use]
365 pub const fn is_lossless(&self) -> bool {
366 matches!(
367 self,
368 Self::Flac | Self::Pcm | Self::Pcm16 | Self::Pcm24 | Self::Alac
369 )
370 }
371
372 #[must_use]
384 pub const fn is_surround(&self) -> bool {
385 matches!(self, Self::Ac3 | Self::Eac3 | Self::Dts)
386 }
387
388 #[must_use]
399 pub const fn is_unknown(&self) -> bool {
400 matches!(self, Self::Unknown)
401 }
402}
403
404impl fmt::Display for AudioCodec {
405 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406 write!(f, "{}", self.display_name())
407 }
408}
409
410#[derive(Debug, Clone, PartialEq, Eq, Hash)]
418pub enum SubtitleCodec {
419 Srt,
421 Ass,
423 Dvb,
425 Hdmv,
427 Webvtt,
429 Other(String),
431}
432
433impl SubtitleCodec {
434 #[must_use]
445 pub fn name(&self) -> &str {
446 match self {
447 Self::Srt => "srt",
448 Self::Ass => "ass",
449 Self::Dvb => "dvb_subtitle",
450 Self::Hdmv => "hdmv_pgs_subtitle",
451 Self::Webvtt => "webvtt",
452 Self::Other(name) => name.as_str(),
453 }
454 }
455
456 #[must_use]
467 pub fn display_name(&self) -> &str {
468 match self {
469 Self::Srt => "SubRip (SRT)",
470 Self::Ass => "ASS/SSA",
471 Self::Dvb => "DVB Subtitle",
472 Self::Hdmv => "HDMV/PGS",
473 Self::Webvtt => "WebVTT",
474 Self::Other(name) => name.as_str(),
475 }
476 }
477
478 #[must_use]
490 pub fn is_text_based(&self) -> bool {
491 matches!(self, Self::Srt | Self::Ass | Self::Webvtt)
492 }
493
494 #[must_use]
506 pub fn is_bitmap_based(&self) -> bool {
507 matches!(self, Self::Dvb | Self::Hdmv)
508 }
509
510 #[must_use]
521 pub fn is_unknown(&self) -> bool {
522 matches!(self, Self::Other(_))
523 }
524}
525
526impl fmt::Display for SubtitleCodec {
527 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528 write!(f, "{}", self.display_name())
529 }
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535
536 mod video_codec_tests {
537 use super::*;
538
539 #[test]
540 fn test_names() {
541 assert_eq!(VideoCodec::H264.name(), "h264");
542 assert_eq!(VideoCodec::H265.name(), "hevc");
543 assert_eq!(VideoCodec::Vp8.name(), "vp8");
544 assert_eq!(VideoCodec::Vp9.name(), "vp9");
545 assert_eq!(VideoCodec::Av1.name(), "av1");
546 assert_eq!(VideoCodec::ProRes.name(), "prores");
547 assert_eq!(VideoCodec::DnxHd.name(), "dnxhd");
548 assert_eq!(VideoCodec::Mpeg4.name(), "mpeg4");
549 assert_eq!(VideoCodec::Mpeg2.name(), "mpeg2video");
550 assert_eq!(VideoCodec::Mjpeg.name(), "mjpeg");
551 assert_eq!(VideoCodec::Unknown.name(), "unknown");
552 }
553
554 #[test]
555 fn test_display_names() {
556 assert_eq!(VideoCodec::H264.display_name(), "H.264/AVC");
557 assert_eq!(VideoCodec::H265.display_name(), "H.265/HEVC");
558 assert_eq!(VideoCodec::ProRes.display_name(), "Apple ProRes");
559 }
560
561 #[test]
562 fn test_display() {
563 assert_eq!(format!("{}", VideoCodec::H264), "H.264/AVC");
564 assert_eq!(format!("{}", VideoCodec::Av1), "AV1");
565 }
566
567 #[test]
568 fn test_default() {
569 assert_eq!(VideoCodec::default(), VideoCodec::H264);
570 }
571
572 #[test]
573 fn test_codec_families() {
574 assert!(VideoCodec::H264.is_h264_family());
575 assert!(!VideoCodec::H265.is_h264_family());
576
577 assert!(VideoCodec::H265.is_h265_family());
578 assert!(!VideoCodec::H264.is_h265_family());
579
580 assert!(VideoCodec::Vp8.is_vp_family());
581 assert!(VideoCodec::Vp9.is_vp_family());
582 assert!(!VideoCodec::H264.is_vp_family());
583 }
584
585 #[test]
586 fn test_is_professional() {
587 assert!(VideoCodec::ProRes.is_professional());
588 assert!(VideoCodec::DnxHd.is_professional());
589 assert!(!VideoCodec::H264.is_professional());
590 assert!(!VideoCodec::Unknown.is_professional());
591 }
592
593 #[test]
594 fn av1svt_name_should_return_av1() {
595 assert_eq!(VideoCodec::Av1Svt.name(), "av1");
596 }
597
598 #[test]
599 fn av1svt_display_name_should_return_av1_svt() {
600 assert_eq!(VideoCodec::Av1Svt.display_name(), "AV1 (SVT)");
601 }
602
603 #[test]
604 fn dnxhd_should_have_correct_name_and_display_name() {
605 assert_eq!(VideoCodec::DnxHd.name(), "dnxhd");
606 assert_eq!(VideoCodec::DnxHd.display_name(), "Avid DNxHD/DNxHR");
607 }
608
609 #[test]
610 fn dnxhd_should_be_professional() {
611 assert!(VideoCodec::DnxHd.is_professional());
612 assert!(!VideoCodec::DnxHd.is_h264_family());
613 assert!(!VideoCodec::DnxHd.is_h265_family());
614 assert!(!VideoCodec::DnxHd.is_vp_family());
615 assert!(!VideoCodec::DnxHd.is_unknown());
616 }
617
618 #[test]
619 fn dnxhd_should_not_have_hardware_support() {
620 assert!(!VideoCodec::DnxHd.has_hardware_support());
621 }
622
623 #[test]
624 fn test_hardware_support() {
625 assert!(VideoCodec::H264.has_hardware_support());
626 assert!(VideoCodec::H265.has_hardware_support());
627 assert!(VideoCodec::Vp9.has_hardware_support());
628 assert!(VideoCodec::Av1.has_hardware_support());
629 assert!(!VideoCodec::ProRes.has_hardware_support());
630 assert!(!VideoCodec::Mjpeg.has_hardware_support());
631 }
632
633 #[test]
634 fn test_is_unknown() {
635 assert!(VideoCodec::Unknown.is_unknown());
636 assert!(!VideoCodec::H264.is_unknown());
637 }
638
639 #[test]
640 fn test_debug() {
641 assert_eq!(format!("{:?}", VideoCodec::H264), "H264");
642 assert_eq!(format!("{:?}", VideoCodec::H265), "H265");
643 }
644
645 #[test]
646 fn test_equality_and_hash() {
647 use std::collections::HashSet;
648
649 assert_eq!(VideoCodec::H264, VideoCodec::H264);
650 assert_ne!(VideoCodec::H264, VideoCodec::H265);
651
652 let mut set = HashSet::new();
653 set.insert(VideoCodec::H264);
654 set.insert(VideoCodec::H265);
655 assert!(set.contains(&VideoCodec::H264));
656 assert!(!set.contains(&VideoCodec::Vp9));
657 }
658
659 #[test]
660 fn test_copy() {
661 let codec = VideoCodec::H264;
662 let copied = codec;
663 assert_eq!(codec, copied);
664 }
665 }
666
667 mod subtitle_codec_tests {
668 use super::*;
669
670 #[test]
671 fn name_should_return_short_codec_name() {
672 assert_eq!(SubtitleCodec::Srt.name(), "srt");
673 assert_eq!(SubtitleCodec::Ass.name(), "ass");
674 assert_eq!(SubtitleCodec::Dvb.name(), "dvb_subtitle");
675 assert_eq!(SubtitleCodec::Hdmv.name(), "hdmv_pgs_subtitle");
676 assert_eq!(SubtitleCodec::Webvtt.name(), "webvtt");
677 assert_eq!(
678 SubtitleCodec::Other("dvd_subtitle".to_string()).name(),
679 "dvd_subtitle"
680 );
681 }
682
683 #[test]
684 fn display_name_should_return_human_readable_name() {
685 assert_eq!(SubtitleCodec::Srt.display_name(), "SubRip (SRT)");
686 assert_eq!(SubtitleCodec::Ass.display_name(), "ASS/SSA");
687 assert_eq!(SubtitleCodec::Dvb.display_name(), "DVB Subtitle");
688 assert_eq!(SubtitleCodec::Hdmv.display_name(), "HDMV/PGS");
689 assert_eq!(SubtitleCodec::Webvtt.display_name(), "WebVTT");
690 }
691
692 #[test]
693 fn display_should_use_display_name() {
694 assert_eq!(format!("{}", SubtitleCodec::Srt), "SubRip (SRT)");
695 assert_eq!(format!("{}", SubtitleCodec::Hdmv), "HDMV/PGS");
696 }
697
698 #[test]
699 fn is_text_based_should_return_true_for_text_codecs() {
700 assert!(SubtitleCodec::Srt.is_text_based());
701 assert!(SubtitleCodec::Ass.is_text_based());
702 assert!(SubtitleCodec::Webvtt.is_text_based());
703 assert!(!SubtitleCodec::Dvb.is_text_based());
704 assert!(!SubtitleCodec::Hdmv.is_text_based());
705 }
706
707 #[test]
708 fn is_bitmap_based_should_return_true_for_bitmap_codecs() {
709 assert!(SubtitleCodec::Dvb.is_bitmap_based());
710 assert!(SubtitleCodec::Hdmv.is_bitmap_based());
711 assert!(!SubtitleCodec::Srt.is_bitmap_based());
712 assert!(!SubtitleCodec::Ass.is_bitmap_based());
713 assert!(!SubtitleCodec::Webvtt.is_bitmap_based());
714 }
715
716 #[test]
717 fn is_unknown_should_return_true_only_for_other_variant() {
718 assert!(SubtitleCodec::Other("dvd_subtitle".to_string()).is_unknown());
719 assert!(!SubtitleCodec::Srt.is_unknown());
720 assert!(!SubtitleCodec::Dvb.is_unknown());
721 }
722
723 #[test]
724 fn equality_should_compare_by_value() {
725 assert_eq!(SubtitleCodec::Srt, SubtitleCodec::Srt);
726 assert_ne!(SubtitleCodec::Srt, SubtitleCodec::Ass);
727 assert_eq!(
728 SubtitleCodec::Other("foo".to_string()),
729 SubtitleCodec::Other("foo".to_string())
730 );
731 assert_ne!(
732 SubtitleCodec::Other("foo".to_string()),
733 SubtitleCodec::Other("bar".to_string())
734 );
735 }
736
737 #[test]
738 fn clone_should_produce_equal_value() {
739 let codec = SubtitleCodec::Other("test".to_string());
740 let cloned = codec.clone();
741 assert_eq!(codec, cloned);
742 }
743 }
744
745 mod audio_codec_tests {
746 use super::*;
747
748 #[test]
749 fn test_names() {
750 assert_eq!(AudioCodec::Aac.name(), "aac");
751 assert_eq!(AudioCodec::Mp3.name(), "mp3");
752 assert_eq!(AudioCodec::Opus.name(), "opus");
753 assert_eq!(AudioCodec::Flac.name(), "flac");
754 assert_eq!(AudioCodec::Pcm.name(), "pcm");
755 assert_eq!(AudioCodec::Vorbis.name(), "vorbis");
756 assert_eq!(AudioCodec::Ac3.name(), "ac3");
757 assert_eq!(AudioCodec::Eac3.name(), "eac3");
758 assert_eq!(AudioCodec::Dts.name(), "dts");
759 assert_eq!(AudioCodec::Alac.name(), "alac");
760 assert_eq!(AudioCodec::Unknown.name(), "unknown");
761 }
762
763 #[test]
764 fn test_display_names() {
765 assert_eq!(AudioCodec::Aac.display_name(), "AAC");
766 assert_eq!(AudioCodec::Flac.display_name(), "FLAC");
767 assert_eq!(AudioCodec::Ac3.display_name(), "Dolby Digital (AC-3)");
768 }
769
770 #[test]
771 fn test_display() {
772 assert_eq!(format!("{}", AudioCodec::Aac), "AAC");
773 assert_eq!(format!("{}", AudioCodec::Opus), "Opus");
774 }
775
776 #[test]
777 fn test_default() {
778 assert_eq!(AudioCodec::default(), AudioCodec::Aac);
779 }
780
781 #[test]
782 fn test_lossy_lossless() {
783 assert!(AudioCodec::Aac.is_lossy());
785 assert!(AudioCodec::Mp3.is_lossy());
786 assert!(AudioCodec::Opus.is_lossy());
787 assert!(AudioCodec::Vorbis.is_lossy());
788 assert!(AudioCodec::Ac3.is_lossy());
789 assert!(AudioCodec::Eac3.is_lossy());
790 assert!(AudioCodec::Dts.is_lossy());
791
792 assert!(AudioCodec::Flac.is_lossless());
794 assert!(AudioCodec::Pcm.is_lossless());
795 assert!(AudioCodec::Alac.is_lossless());
796
797 assert!(!AudioCodec::Aac.is_lossless());
799 assert!(!AudioCodec::Flac.is_lossy());
800 }
801
802 #[test]
803 fn test_surround() {
804 assert!(AudioCodec::Ac3.is_surround());
805 assert!(AudioCodec::Eac3.is_surround());
806 assert!(AudioCodec::Dts.is_surround());
807 assert!(!AudioCodec::Aac.is_surround());
808 assert!(!AudioCodec::Flac.is_surround());
809 }
810
811 #[test]
812 fn pcm16_name_should_return_pcm_s16le() {
813 assert_eq!(AudioCodec::Pcm16.name(), "pcm_s16le");
814 }
815
816 #[test]
817 fn pcm24_name_should_return_pcm_s24le() {
818 assert_eq!(AudioCodec::Pcm24.name(), "pcm_s24le");
819 }
820
821 #[test]
822 fn pcm16_should_be_lossless() {
823 assert!(AudioCodec::Pcm16.is_lossless());
824 assert!(!AudioCodec::Pcm16.is_lossy());
825 }
826
827 #[test]
828 fn pcm24_should_be_lossless() {
829 assert!(AudioCodec::Pcm24.is_lossless());
830 assert!(!AudioCodec::Pcm24.is_lossy());
831 }
832
833 #[test]
834 fn test_is_unknown() {
835 assert!(AudioCodec::Unknown.is_unknown());
836 assert!(!AudioCodec::Aac.is_unknown());
837 }
838
839 #[test]
840 fn test_debug() {
841 assert_eq!(format!("{:?}", AudioCodec::Aac), "Aac");
842 assert_eq!(format!("{:?}", AudioCodec::Flac), "Flac");
843 }
844
845 #[test]
846 fn test_equality_and_hash() {
847 use std::collections::HashSet;
848
849 assert_eq!(AudioCodec::Aac, AudioCodec::Aac);
850 assert_ne!(AudioCodec::Aac, AudioCodec::Mp3);
851
852 let mut set = HashSet::new();
853 set.insert(AudioCodec::Aac);
854 set.insert(AudioCodec::Flac);
855 assert!(set.contains(&AudioCodec::Aac));
856 assert!(!set.contains(&AudioCodec::Opus));
857 }
858
859 #[test]
860 fn test_copy() {
861 let codec = AudioCodec::Aac;
862 let copied = codec;
863 assert_eq!(codec, copied);
864 }
865 }
866}