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 Ffv1,
66 Unknown,
68}
69
70impl VideoCodec {
71 #[must_use]
82 pub const fn name(&self) -> &'static str {
83 match self {
84 Self::H264 => "h264",
85 Self::H265 => "hevc",
86 Self::Vp8 => "vp8",
87 Self::Vp9 => "vp9",
88 Self::Av1 | Self::Av1Svt => "av1",
89 Self::ProRes => "prores",
90 Self::DnxHd => "dnxhd",
91 Self::Mpeg4 => "mpeg4",
92 Self::Mpeg2 => "mpeg2video",
93 Self::Mjpeg => "mjpeg",
94 Self::Png => "png",
95 Self::Ffv1 => "ffv1",
96 Self::Unknown => "unknown",
97 }
98 }
99
100 #[must_use]
111 pub const fn display_name(&self) -> &'static str {
112 match self {
113 Self::H264 => "H.264/AVC",
114 Self::H265 => "H.265/HEVC",
115 Self::Vp8 => "VP8",
116 Self::Vp9 => "VP9",
117 Self::Av1 => "AV1",
118 Self::Av1Svt => "AV1 (SVT)",
119 Self::ProRes => "Apple ProRes",
120 Self::DnxHd => "Avid DNxHD/DNxHR",
121 Self::Mpeg4 => "MPEG-4 Part 2",
122 Self::Mpeg2 => "MPEG-2",
123 Self::Mjpeg => "Motion JPEG",
124 Self::Png => "PNG",
125 Self::Ffv1 => "FFV1",
126 Self::Unknown => "Unknown",
127 }
128 }
129
130 #[must_use]
141 pub const fn is_h264_family(&self) -> bool {
142 matches!(self, Self::H264)
143 }
144
145 #[must_use]
156 pub const fn is_h265_family(&self) -> bool {
157 matches!(self, Self::H265)
158 }
159
160 #[must_use]
172 pub const fn is_vp_family(&self) -> bool {
173 matches!(self, Self::Vp8 | Self::Vp9)
174 }
175
176 #[must_use]
187 pub const fn is_professional(&self) -> bool {
188 matches!(self, Self::ProRes | Self::DnxHd)
189 }
190
191 #[must_use]
203 pub const fn has_hardware_support(&self) -> bool {
204 matches!(self, Self::H264 | Self::H265 | Self::Vp9 | Self::Av1)
205 }
206
207 #[must_use]
218 pub const fn is_unknown(&self) -> bool {
219 matches!(self, Self::Unknown)
220 }
221}
222
223impl fmt::Display for VideoCodec {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 write!(f, "{}", self.display_name())
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
243#[non_exhaustive]
244pub enum AudioCodec {
245 #[default]
247 Aac,
248 Mp3,
250 Opus,
252 Flac,
254 Pcm,
256 Pcm16,
258 Pcm24,
260 Vorbis,
262 Ac3,
264 Eac3,
266 Dts,
268 Alac,
270 Unknown,
272}
273
274impl AudioCodec {
275 #[must_use]
286 pub const fn name(&self) -> &'static str {
287 match self {
288 Self::Aac => "aac",
289 Self::Mp3 => "mp3",
290 Self::Opus => "opus",
291 Self::Flac => "flac",
292 Self::Pcm => "pcm",
293 Self::Pcm16 => "pcm_s16le",
294 Self::Pcm24 => "pcm_s24le",
295 Self::Vorbis => "vorbis",
296 Self::Ac3 => "ac3",
297 Self::Eac3 => "eac3",
298 Self::Dts => "dts",
299 Self::Alac => "alac",
300 Self::Unknown => "unknown",
301 }
302 }
303
304 #[must_use]
315 pub const fn display_name(&self) -> &'static str {
316 match self {
317 Self::Aac => "AAC",
318 Self::Mp3 => "MP3",
319 Self::Opus => "Opus",
320 Self::Flac => "FLAC",
321 Self::Pcm => "PCM",
322 Self::Pcm16 => "PCM 16-bit",
323 Self::Pcm24 => "PCM 24-bit",
324 Self::Vorbis => "Vorbis",
325 Self::Ac3 => "Dolby Digital (AC-3)",
326 Self::Eac3 => "Dolby Digital Plus (E-AC-3)",
327 Self::Dts => "DTS",
328 Self::Alac => "Apple Lossless",
329 Self::Unknown => "Unknown",
330 }
331 }
332
333 #[must_use]
347 pub const fn is_lossy(&self) -> bool {
348 matches!(
349 self,
350 Self::Aac | Self::Mp3 | Self::Opus | Self::Vorbis | Self::Ac3 | Self::Eac3 | Self::Dts
351 )
352 }
353
354 #[must_use]
369 pub const fn is_lossless(&self) -> bool {
370 matches!(
371 self,
372 Self::Flac | Self::Pcm | Self::Pcm16 | Self::Pcm24 | Self::Alac
373 )
374 }
375
376 #[must_use]
388 pub const fn is_surround(&self) -> bool {
389 matches!(self, Self::Ac3 | Self::Eac3 | Self::Dts)
390 }
391
392 #[must_use]
403 pub const fn is_unknown(&self) -> bool {
404 matches!(self, Self::Unknown)
405 }
406}
407
408impl fmt::Display for AudioCodec {
409 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410 write!(f, "{}", self.display_name())
411 }
412}
413
414#[derive(Debug, Clone, PartialEq, Eq, Hash)]
422pub enum SubtitleCodec {
423 Srt,
425 Ass,
427 Dvb,
429 Hdmv,
431 Webvtt,
433 Other(String),
435}
436
437impl SubtitleCodec {
438 #[must_use]
449 pub fn name(&self) -> &str {
450 match self {
451 Self::Srt => "srt",
452 Self::Ass => "ass",
453 Self::Dvb => "dvb_subtitle",
454 Self::Hdmv => "hdmv_pgs_subtitle",
455 Self::Webvtt => "webvtt",
456 Self::Other(name) => name.as_str(),
457 }
458 }
459
460 #[must_use]
471 pub fn display_name(&self) -> &str {
472 match self {
473 Self::Srt => "SubRip (SRT)",
474 Self::Ass => "ASS/SSA",
475 Self::Dvb => "DVB Subtitle",
476 Self::Hdmv => "HDMV/PGS",
477 Self::Webvtt => "WebVTT",
478 Self::Other(name) => name.as_str(),
479 }
480 }
481
482 #[must_use]
494 pub fn is_text_based(&self) -> bool {
495 matches!(self, Self::Srt | Self::Ass | Self::Webvtt)
496 }
497
498 #[must_use]
510 pub fn is_bitmap_based(&self) -> bool {
511 matches!(self, Self::Dvb | Self::Hdmv)
512 }
513
514 #[must_use]
525 pub fn is_unknown(&self) -> bool {
526 matches!(self, Self::Other(_))
527 }
528}
529
530impl fmt::Display for SubtitleCodec {
531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532 write!(f, "{}", self.display_name())
533 }
534}
535
536#[cfg(test)]
537mod tests {
538 use super::*;
539
540 mod video_codec_tests {
541 use super::*;
542
543 #[test]
544 fn test_names() {
545 assert_eq!(VideoCodec::H264.name(), "h264");
546 assert_eq!(VideoCodec::H265.name(), "hevc");
547 assert_eq!(VideoCodec::Vp8.name(), "vp8");
548 assert_eq!(VideoCodec::Vp9.name(), "vp9");
549 assert_eq!(VideoCodec::Av1.name(), "av1");
550 assert_eq!(VideoCodec::ProRes.name(), "prores");
551 assert_eq!(VideoCodec::DnxHd.name(), "dnxhd");
552 assert_eq!(VideoCodec::Mpeg4.name(), "mpeg4");
553 assert_eq!(VideoCodec::Mpeg2.name(), "mpeg2video");
554 assert_eq!(VideoCodec::Mjpeg.name(), "mjpeg");
555 assert_eq!(VideoCodec::Unknown.name(), "unknown");
556 }
557
558 #[test]
559 fn test_display_names() {
560 assert_eq!(VideoCodec::H264.display_name(), "H.264/AVC");
561 assert_eq!(VideoCodec::H265.display_name(), "H.265/HEVC");
562 assert_eq!(VideoCodec::ProRes.display_name(), "Apple ProRes");
563 }
564
565 #[test]
566 fn test_display() {
567 assert_eq!(format!("{}", VideoCodec::H264), "H.264/AVC");
568 assert_eq!(format!("{}", VideoCodec::Av1), "AV1");
569 }
570
571 #[test]
572 fn test_default() {
573 assert_eq!(VideoCodec::default(), VideoCodec::H264);
574 }
575
576 #[test]
577 fn test_codec_families() {
578 assert!(VideoCodec::H264.is_h264_family());
579 assert!(!VideoCodec::H265.is_h264_family());
580
581 assert!(VideoCodec::H265.is_h265_family());
582 assert!(!VideoCodec::H264.is_h265_family());
583
584 assert!(VideoCodec::Vp8.is_vp_family());
585 assert!(VideoCodec::Vp9.is_vp_family());
586 assert!(!VideoCodec::H264.is_vp_family());
587 }
588
589 #[test]
590 fn test_is_professional() {
591 assert!(VideoCodec::ProRes.is_professional());
592 assert!(VideoCodec::DnxHd.is_professional());
593 assert!(!VideoCodec::H264.is_professional());
594 assert!(!VideoCodec::Unknown.is_professional());
595 }
596
597 #[test]
598 fn av1svt_name_should_return_av1() {
599 assert_eq!(VideoCodec::Av1Svt.name(), "av1");
600 }
601
602 #[test]
603 fn av1svt_display_name_should_return_av1_svt() {
604 assert_eq!(VideoCodec::Av1Svt.display_name(), "AV1 (SVT)");
605 }
606
607 #[test]
608 fn dnxhd_should_have_correct_name_and_display_name() {
609 assert_eq!(VideoCodec::DnxHd.name(), "dnxhd");
610 assert_eq!(VideoCodec::DnxHd.display_name(), "Avid DNxHD/DNxHR");
611 }
612
613 #[test]
614 fn dnxhd_should_be_professional() {
615 assert!(VideoCodec::DnxHd.is_professional());
616 assert!(!VideoCodec::DnxHd.is_h264_family());
617 assert!(!VideoCodec::DnxHd.is_h265_family());
618 assert!(!VideoCodec::DnxHd.is_vp_family());
619 assert!(!VideoCodec::DnxHd.is_unknown());
620 }
621
622 #[test]
623 fn dnxhd_should_not_have_hardware_support() {
624 assert!(!VideoCodec::DnxHd.has_hardware_support());
625 }
626
627 #[test]
628 fn test_hardware_support() {
629 assert!(VideoCodec::H264.has_hardware_support());
630 assert!(VideoCodec::H265.has_hardware_support());
631 assert!(VideoCodec::Vp9.has_hardware_support());
632 assert!(VideoCodec::Av1.has_hardware_support());
633 assert!(!VideoCodec::ProRes.has_hardware_support());
634 assert!(!VideoCodec::Mjpeg.has_hardware_support());
635 }
636
637 #[test]
638 fn test_is_unknown() {
639 assert!(VideoCodec::Unknown.is_unknown());
640 assert!(!VideoCodec::H264.is_unknown());
641 }
642
643 #[test]
644 fn test_debug() {
645 assert_eq!(format!("{:?}", VideoCodec::H264), "H264");
646 assert_eq!(format!("{:?}", VideoCodec::H265), "H265");
647 }
648
649 #[test]
650 fn test_equality_and_hash() {
651 use std::collections::HashSet;
652
653 assert_eq!(VideoCodec::H264, VideoCodec::H264);
654 assert_ne!(VideoCodec::H264, VideoCodec::H265);
655
656 let mut set = HashSet::new();
657 set.insert(VideoCodec::H264);
658 set.insert(VideoCodec::H265);
659 assert!(set.contains(&VideoCodec::H264));
660 assert!(!set.contains(&VideoCodec::Vp9));
661 }
662
663 #[test]
664 fn test_copy() {
665 let codec = VideoCodec::H264;
666 let copied = codec;
667 assert_eq!(codec, copied);
668 }
669 }
670
671 mod subtitle_codec_tests {
672 use super::*;
673
674 #[test]
675 fn name_should_return_short_codec_name() {
676 assert_eq!(SubtitleCodec::Srt.name(), "srt");
677 assert_eq!(SubtitleCodec::Ass.name(), "ass");
678 assert_eq!(SubtitleCodec::Dvb.name(), "dvb_subtitle");
679 assert_eq!(SubtitleCodec::Hdmv.name(), "hdmv_pgs_subtitle");
680 assert_eq!(SubtitleCodec::Webvtt.name(), "webvtt");
681 assert_eq!(
682 SubtitleCodec::Other("dvd_subtitle".to_string()).name(),
683 "dvd_subtitle"
684 );
685 }
686
687 #[test]
688 fn display_name_should_return_human_readable_name() {
689 assert_eq!(SubtitleCodec::Srt.display_name(), "SubRip (SRT)");
690 assert_eq!(SubtitleCodec::Ass.display_name(), "ASS/SSA");
691 assert_eq!(SubtitleCodec::Dvb.display_name(), "DVB Subtitle");
692 assert_eq!(SubtitleCodec::Hdmv.display_name(), "HDMV/PGS");
693 assert_eq!(SubtitleCodec::Webvtt.display_name(), "WebVTT");
694 }
695
696 #[test]
697 fn display_should_use_display_name() {
698 assert_eq!(format!("{}", SubtitleCodec::Srt), "SubRip (SRT)");
699 assert_eq!(format!("{}", SubtitleCodec::Hdmv), "HDMV/PGS");
700 }
701
702 #[test]
703 fn is_text_based_should_return_true_for_text_codecs() {
704 assert!(SubtitleCodec::Srt.is_text_based());
705 assert!(SubtitleCodec::Ass.is_text_based());
706 assert!(SubtitleCodec::Webvtt.is_text_based());
707 assert!(!SubtitleCodec::Dvb.is_text_based());
708 assert!(!SubtitleCodec::Hdmv.is_text_based());
709 }
710
711 #[test]
712 fn is_bitmap_based_should_return_true_for_bitmap_codecs() {
713 assert!(SubtitleCodec::Dvb.is_bitmap_based());
714 assert!(SubtitleCodec::Hdmv.is_bitmap_based());
715 assert!(!SubtitleCodec::Srt.is_bitmap_based());
716 assert!(!SubtitleCodec::Ass.is_bitmap_based());
717 assert!(!SubtitleCodec::Webvtt.is_bitmap_based());
718 }
719
720 #[test]
721 fn is_unknown_should_return_true_only_for_other_variant() {
722 assert!(SubtitleCodec::Other("dvd_subtitle".to_string()).is_unknown());
723 assert!(!SubtitleCodec::Srt.is_unknown());
724 assert!(!SubtitleCodec::Dvb.is_unknown());
725 }
726
727 #[test]
728 fn equality_should_compare_by_value() {
729 assert_eq!(SubtitleCodec::Srt, SubtitleCodec::Srt);
730 assert_ne!(SubtitleCodec::Srt, SubtitleCodec::Ass);
731 assert_eq!(
732 SubtitleCodec::Other("foo".to_string()),
733 SubtitleCodec::Other("foo".to_string())
734 );
735 assert_ne!(
736 SubtitleCodec::Other("foo".to_string()),
737 SubtitleCodec::Other("bar".to_string())
738 );
739 }
740
741 #[test]
742 fn clone_should_produce_equal_value() {
743 let codec = SubtitleCodec::Other("test".to_string());
744 let cloned = codec.clone();
745 assert_eq!(codec, cloned);
746 }
747 }
748
749 mod audio_codec_tests {
750 use super::*;
751
752 #[test]
753 fn test_names() {
754 assert_eq!(AudioCodec::Aac.name(), "aac");
755 assert_eq!(AudioCodec::Mp3.name(), "mp3");
756 assert_eq!(AudioCodec::Opus.name(), "opus");
757 assert_eq!(AudioCodec::Flac.name(), "flac");
758 assert_eq!(AudioCodec::Pcm.name(), "pcm");
759 assert_eq!(AudioCodec::Vorbis.name(), "vorbis");
760 assert_eq!(AudioCodec::Ac3.name(), "ac3");
761 assert_eq!(AudioCodec::Eac3.name(), "eac3");
762 assert_eq!(AudioCodec::Dts.name(), "dts");
763 assert_eq!(AudioCodec::Alac.name(), "alac");
764 assert_eq!(AudioCodec::Unknown.name(), "unknown");
765 }
766
767 #[test]
768 fn test_display_names() {
769 assert_eq!(AudioCodec::Aac.display_name(), "AAC");
770 assert_eq!(AudioCodec::Flac.display_name(), "FLAC");
771 assert_eq!(AudioCodec::Ac3.display_name(), "Dolby Digital (AC-3)");
772 }
773
774 #[test]
775 fn test_display() {
776 assert_eq!(format!("{}", AudioCodec::Aac), "AAC");
777 assert_eq!(format!("{}", AudioCodec::Opus), "Opus");
778 }
779
780 #[test]
781 fn test_default() {
782 assert_eq!(AudioCodec::default(), AudioCodec::Aac);
783 }
784
785 #[test]
786 fn test_lossy_lossless() {
787 assert!(AudioCodec::Aac.is_lossy());
789 assert!(AudioCodec::Mp3.is_lossy());
790 assert!(AudioCodec::Opus.is_lossy());
791 assert!(AudioCodec::Vorbis.is_lossy());
792 assert!(AudioCodec::Ac3.is_lossy());
793 assert!(AudioCodec::Eac3.is_lossy());
794 assert!(AudioCodec::Dts.is_lossy());
795
796 assert!(AudioCodec::Flac.is_lossless());
798 assert!(AudioCodec::Pcm.is_lossless());
799 assert!(AudioCodec::Alac.is_lossless());
800
801 assert!(!AudioCodec::Aac.is_lossless());
803 assert!(!AudioCodec::Flac.is_lossy());
804 }
805
806 #[test]
807 fn test_surround() {
808 assert!(AudioCodec::Ac3.is_surround());
809 assert!(AudioCodec::Eac3.is_surround());
810 assert!(AudioCodec::Dts.is_surround());
811 assert!(!AudioCodec::Aac.is_surround());
812 assert!(!AudioCodec::Flac.is_surround());
813 }
814
815 #[test]
816 fn pcm16_name_should_return_pcm_s16le() {
817 assert_eq!(AudioCodec::Pcm16.name(), "pcm_s16le");
818 }
819
820 #[test]
821 fn pcm24_name_should_return_pcm_s24le() {
822 assert_eq!(AudioCodec::Pcm24.name(), "pcm_s24le");
823 }
824
825 #[test]
826 fn pcm16_should_be_lossless() {
827 assert!(AudioCodec::Pcm16.is_lossless());
828 assert!(!AudioCodec::Pcm16.is_lossy());
829 }
830
831 #[test]
832 fn pcm24_should_be_lossless() {
833 assert!(AudioCodec::Pcm24.is_lossless());
834 assert!(!AudioCodec::Pcm24.is_lossy());
835 }
836
837 #[test]
838 fn test_is_unknown() {
839 assert!(AudioCodec::Unknown.is_unknown());
840 assert!(!AudioCodec::Aac.is_unknown());
841 }
842
843 #[test]
844 fn test_debug() {
845 assert_eq!(format!("{:?}", AudioCodec::Aac), "Aac");
846 assert_eq!(format!("{:?}", AudioCodec::Flac), "Flac");
847 }
848
849 #[test]
850 fn test_equality_and_hash() {
851 use std::collections::HashSet;
852
853 assert_eq!(AudioCodec::Aac, AudioCodec::Aac);
854 assert_ne!(AudioCodec::Aac, AudioCodec::Mp3);
855
856 let mut set = HashSet::new();
857 set.insert(AudioCodec::Aac);
858 set.insert(AudioCodec::Flac);
859 assert!(set.contains(&AudioCodec::Aac));
860 assert!(!set.contains(&AudioCodec::Opus));
861 }
862
863 #[test]
864 fn test_copy() {
865 let codec = AudioCodec::Aac;
866 let copied = codec;
867 assert_eq!(codec, copied);
868 }
869 }
870}