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 ProRes,
50 DnxHd,
52 Mpeg4,
54 Mpeg2,
56 Mjpeg,
58 Unknown,
60}
61
62impl VideoCodec {
63 #[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 #[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 #[must_use]
128 pub const fn is_h264_family(&self) -> bool {
129 matches!(self, Self::H264)
130 }
131
132 #[must_use]
143 pub const fn is_h265_family(&self) -> bool {
144 matches!(self, Self::H265)
145 }
146
147 #[must_use]
159 pub const fn is_vp_family(&self) -> bool {
160 matches!(self, Self::Vp8 | Self::Vp9)
161 }
162
163 #[must_use]
174 pub const fn is_professional(&self) -> bool {
175 matches!(self, Self::ProRes | Self::DnxHd)
176 }
177
178 #[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 #[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
230#[non_exhaustive]
231pub enum AudioCodec {
232 #[default]
234 Aac,
235 Mp3,
237 Opus,
239 Flac,
241 Pcm,
243 Vorbis,
245 Ac3,
247 Eac3,
249 Dts,
251 Alac,
253 Unknown,
255}
256
257impl AudioCodec {
258 #[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 #[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 #[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 #[must_use]
348 pub const fn is_lossless(&self) -> bool {
349 matches!(self, Self::Flac | Self::Pcm | Self::Alac)
350 }
351
352 #[must_use]
364 pub const fn is_surround(&self) -> bool {
365 matches!(self, Self::Ac3 | Self::Eac3 | Self::Dts)
366 }
367
368 #[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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
398pub enum SubtitleCodec {
399 Srt,
401 Ass,
403 Dvb,
405 Hdmv,
407 Webvtt,
409 Other(String),
411}
412
413impl SubtitleCodec {
414 #[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 #[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 #[must_use]
470 pub fn is_text_based(&self) -> bool {
471 matches!(self, Self::Srt | Self::Ass | Self::Webvtt)
472 }
473
474 #[must_use]
486 pub fn is_bitmap_based(&self) -> bool {
487 matches!(self, Self::Dvb | Self::Hdmv)
488 }
489
490 #[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 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 assert!(AudioCodec::Flac.is_lossless());
764 assert!(AudioCodec::Pcm.is_lossless());
765 assert!(AudioCodec::Alac.is_lossless());
766
767 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}