1use std::time::Duration;
43
44use crate::channel::ChannelLayout;
45use crate::codec::{AudioCodec, SubtitleCodec, VideoCodec};
46use crate::color::{ColorPrimaries, ColorRange, ColorSpace};
47use crate::pixel::PixelFormat;
48use crate::sample::SampleFormat;
49use crate::time::Rational;
50
51#[derive(Debug, Clone)]
75pub struct VideoStreamInfo {
76 index: u32,
78 codec: VideoCodec,
80 codec_name: String,
82 width: u32,
84 height: u32,
86 pixel_format: PixelFormat,
88 frame_rate: Rational,
90 duration: Option<Duration>,
92 bitrate: Option<u64>,
94 frame_count: Option<u64>,
96 color_space: ColorSpace,
98 color_range: ColorRange,
100 color_primaries: ColorPrimaries,
102}
103
104impl VideoStreamInfo {
105 #[must_use]
123 pub fn builder() -> VideoStreamInfoBuilder {
124 VideoStreamInfoBuilder::default()
125 }
126
127 #[must_use]
129 #[inline]
130 pub const fn index(&self) -> u32 {
131 self.index
132 }
133
134 #[must_use]
136 #[inline]
137 pub const fn codec(&self) -> VideoCodec {
138 self.codec
139 }
140
141 #[must_use]
143 #[inline]
144 pub fn codec_name(&self) -> &str {
145 &self.codec_name
146 }
147
148 #[must_use]
150 #[inline]
151 pub const fn width(&self) -> u32 {
152 self.width
153 }
154
155 #[must_use]
157 #[inline]
158 pub const fn height(&self) -> u32 {
159 self.height
160 }
161
162 #[must_use]
164 #[inline]
165 pub const fn pixel_format(&self) -> PixelFormat {
166 self.pixel_format
167 }
168
169 #[must_use]
171 #[inline]
172 pub const fn frame_rate(&self) -> Rational {
173 self.frame_rate
174 }
175
176 #[must_use]
178 #[inline]
179 pub fn fps(&self) -> f64 {
180 self.frame_rate.as_f64()
181 }
182
183 #[must_use]
185 #[inline]
186 pub const fn duration(&self) -> Option<Duration> {
187 self.duration
188 }
189
190 #[must_use]
192 #[inline]
193 pub const fn bitrate(&self) -> Option<u64> {
194 self.bitrate
195 }
196
197 #[must_use]
199 #[inline]
200 pub const fn frame_count(&self) -> Option<u64> {
201 self.frame_count
202 }
203
204 #[must_use]
206 #[inline]
207 pub const fn color_space(&self) -> ColorSpace {
208 self.color_space
209 }
210
211 #[must_use]
213 #[inline]
214 pub const fn color_range(&self) -> ColorRange {
215 self.color_range
216 }
217
218 #[must_use]
220 #[inline]
221 pub const fn color_primaries(&self) -> ColorPrimaries {
222 self.color_primaries
223 }
224
225 #[must_use]
227 #[inline]
228 pub fn aspect_ratio(&self) -> f64 {
229 if self.height == 0 {
230 log::warn!(
231 "aspect_ratio unavailable, height is 0, returning 0.0 \
232 width={} height=0 fallback=0.0",
233 self.width
234 );
235 0.0
236 } else {
237 f64::from(self.width) / f64::from(self.height)
238 }
239 }
240
241 #[must_use]
243 #[inline]
244 pub const fn is_hd(&self) -> bool {
245 self.height >= 720
246 }
247
248 #[must_use]
250 #[inline]
251 pub const fn is_full_hd(&self) -> bool {
252 self.height >= 1080
253 }
254
255 #[must_use]
257 #[inline]
258 pub const fn is_4k(&self) -> bool {
259 self.height >= 2160
260 }
261
262 #[must_use]
299 #[inline]
300 pub fn is_hdr(&self) -> bool {
301 self.color_primaries.is_wide_gamut() && self.pixel_format.is_high_bit_depth()
303 }
304}
305
306impl Default for VideoStreamInfo {
307 fn default() -> Self {
308 Self {
309 index: 0,
310 codec: VideoCodec::default(),
311 codec_name: String::new(),
312 width: 0,
313 height: 0,
314 pixel_format: PixelFormat::default(),
315 frame_rate: Rational::new(30, 1),
316 duration: None,
317 bitrate: None,
318 frame_count: None,
319 color_space: ColorSpace::default(),
320 color_range: ColorRange::default(),
321 color_primaries: ColorPrimaries::default(),
322 }
323 }
324}
325
326#[derive(Debug, Clone, Default)]
328pub struct VideoStreamInfoBuilder {
329 index: u32,
330 codec: VideoCodec,
331 codec_name: String,
332 width: u32,
333 height: u32,
334 pixel_format: PixelFormat,
335 frame_rate: Rational,
336 duration: Option<Duration>,
337 bitrate: Option<u64>,
338 frame_count: Option<u64>,
339 color_space: ColorSpace,
340 color_range: ColorRange,
341 color_primaries: ColorPrimaries,
342}
343
344impl VideoStreamInfoBuilder {
345 #[must_use]
347 pub fn index(mut self, index: u32) -> Self {
348 self.index = index;
349 self
350 }
351
352 #[must_use]
354 pub fn codec(mut self, codec: VideoCodec) -> Self {
355 self.codec = codec;
356 self
357 }
358
359 #[must_use]
361 pub fn codec_name(mut self, name: impl Into<String>) -> Self {
362 self.codec_name = name.into();
363 self
364 }
365
366 #[must_use]
368 pub fn width(mut self, width: u32) -> Self {
369 self.width = width;
370 self
371 }
372
373 #[must_use]
375 pub fn height(mut self, height: u32) -> Self {
376 self.height = height;
377 self
378 }
379
380 #[must_use]
382 pub fn pixel_format(mut self, format: PixelFormat) -> Self {
383 self.pixel_format = format;
384 self
385 }
386
387 #[must_use]
389 pub fn frame_rate(mut self, rate: Rational) -> Self {
390 self.frame_rate = rate;
391 self
392 }
393
394 #[must_use]
396 pub fn duration(mut self, duration: Duration) -> Self {
397 self.duration = Some(duration);
398 self
399 }
400
401 #[must_use]
403 pub fn bitrate(mut self, bitrate: u64) -> Self {
404 self.bitrate = Some(bitrate);
405 self
406 }
407
408 #[must_use]
410 pub fn frame_count(mut self, count: u64) -> Self {
411 self.frame_count = Some(count);
412 self
413 }
414
415 #[must_use]
417 pub fn color_space(mut self, space: ColorSpace) -> Self {
418 self.color_space = space;
419 self
420 }
421
422 #[must_use]
424 pub fn color_range(mut self, range: ColorRange) -> Self {
425 self.color_range = range;
426 self
427 }
428
429 #[must_use]
431 pub fn color_primaries(mut self, primaries: ColorPrimaries) -> Self {
432 self.color_primaries = primaries;
433 self
434 }
435
436 #[must_use]
438 pub fn build(self) -> VideoStreamInfo {
439 VideoStreamInfo {
440 index: self.index,
441 codec: self.codec,
442 codec_name: self.codec_name,
443 width: self.width,
444 height: self.height,
445 pixel_format: self.pixel_format,
446 frame_rate: self.frame_rate,
447 duration: self.duration,
448 bitrate: self.bitrate,
449 frame_count: self.frame_count,
450 color_space: self.color_space,
451 color_range: self.color_range,
452 color_primaries: self.color_primaries,
453 }
454 }
455}
456
457#[derive(Debug, Clone)]
480pub struct AudioStreamInfo {
481 index: u32,
483 codec: AudioCodec,
485 codec_name: String,
487 sample_rate: u32,
489 channels: u32,
491 channel_layout: ChannelLayout,
493 sample_format: SampleFormat,
495 duration: Option<Duration>,
497 bitrate: Option<u64>,
499 language: Option<String>,
501}
502
503impl AudioStreamInfo {
504 #[must_use]
521 pub fn builder() -> AudioStreamInfoBuilder {
522 AudioStreamInfoBuilder::default()
523 }
524
525 #[must_use]
527 #[inline]
528 pub const fn index(&self) -> u32 {
529 self.index
530 }
531
532 #[must_use]
534 #[inline]
535 pub const fn codec(&self) -> AudioCodec {
536 self.codec
537 }
538
539 #[must_use]
541 #[inline]
542 pub fn codec_name(&self) -> &str {
543 &self.codec_name
544 }
545
546 #[must_use]
548 #[inline]
549 pub const fn sample_rate(&self) -> u32 {
550 self.sample_rate
551 }
552
553 #[must_use]
555 #[inline]
556 pub const fn channels(&self) -> u32 {
557 self.channels
558 }
559
560 #[must_use]
562 #[inline]
563 pub const fn channel_layout(&self) -> ChannelLayout {
564 self.channel_layout
565 }
566
567 #[must_use]
569 #[inline]
570 pub const fn sample_format(&self) -> SampleFormat {
571 self.sample_format
572 }
573
574 #[must_use]
576 #[inline]
577 pub const fn duration(&self) -> Option<Duration> {
578 self.duration
579 }
580
581 #[must_use]
583 #[inline]
584 pub const fn bitrate(&self) -> Option<u64> {
585 self.bitrate
586 }
587
588 #[must_use]
590 #[inline]
591 pub fn language(&self) -> Option<&str> {
592 self.language.as_deref()
593 }
594
595 #[must_use]
597 #[inline]
598 pub const fn is_mono(&self) -> bool {
599 self.channels == 1
600 }
601
602 #[must_use]
604 #[inline]
605 pub const fn is_stereo(&self) -> bool {
606 self.channels == 2
607 }
608
609 #[must_use]
611 #[inline]
612 pub const fn is_surround(&self) -> bool {
613 self.channels > 2
614 }
615}
616
617impl Default for AudioStreamInfo {
618 fn default() -> Self {
619 Self {
620 index: 0,
621 codec: AudioCodec::default(),
622 codec_name: String::new(),
623 sample_rate: 48000,
624 channels: 2,
625 channel_layout: ChannelLayout::default(),
626 sample_format: SampleFormat::default(),
627 duration: None,
628 bitrate: None,
629 language: None,
630 }
631 }
632}
633
634#[derive(Debug, Clone, Default)]
636pub struct AudioStreamInfoBuilder {
637 index: u32,
638 codec: AudioCodec,
639 codec_name: String,
640 sample_rate: u32,
641 channels: u32,
642 channel_layout: Option<ChannelLayout>,
643 sample_format: SampleFormat,
644 duration: Option<Duration>,
645 bitrate: Option<u64>,
646 language: Option<String>,
647}
648
649impl AudioStreamInfoBuilder {
650 #[must_use]
652 pub fn index(mut self, index: u32) -> Self {
653 self.index = index;
654 self
655 }
656
657 #[must_use]
659 pub fn codec(mut self, codec: AudioCodec) -> Self {
660 self.codec = codec;
661 self
662 }
663
664 #[must_use]
666 pub fn codec_name(mut self, name: impl Into<String>) -> Self {
667 self.codec_name = name.into();
668 self
669 }
670
671 #[must_use]
673 pub fn sample_rate(mut self, rate: u32) -> Self {
674 self.sample_rate = rate;
675 self
676 }
677
678 #[must_use]
682 pub fn channels(mut self, channels: u32) -> Self {
683 self.channels = channels;
684 self
685 }
686
687 #[must_use]
689 pub fn channel_layout(mut self, layout: ChannelLayout) -> Self {
690 self.channel_layout = Some(layout);
691 self
692 }
693
694 #[must_use]
696 pub fn sample_format(mut self, format: SampleFormat) -> Self {
697 self.sample_format = format;
698 self
699 }
700
701 #[must_use]
703 pub fn duration(mut self, duration: Duration) -> Self {
704 self.duration = Some(duration);
705 self
706 }
707
708 #[must_use]
710 pub fn bitrate(mut self, bitrate: u64) -> Self {
711 self.bitrate = Some(bitrate);
712 self
713 }
714
715 #[must_use]
717 pub fn language(mut self, lang: impl Into<String>) -> Self {
718 self.language = Some(lang.into());
719 self
720 }
721
722 #[must_use]
724 pub fn build(self) -> AudioStreamInfo {
725 let channel_layout = self.channel_layout.unwrap_or_else(|| {
726 log::warn!(
727 "channel_layout not set, deriving from channel count \
728 channels={} fallback=from_channels",
729 self.channels
730 );
731 ChannelLayout::from_channels(self.channels)
732 });
733
734 AudioStreamInfo {
735 index: self.index,
736 codec: self.codec,
737 codec_name: self.codec_name,
738 sample_rate: self.sample_rate,
739 channels: self.channels,
740 channel_layout,
741 sample_format: self.sample_format,
742 duration: self.duration,
743 bitrate: self.bitrate,
744 language: self.language,
745 }
746 }
747}
748
749#[derive(Debug, Clone)]
770pub struct SubtitleStreamInfo {
771 index: u32,
773 codec: SubtitleCodec,
775 codec_name: String,
777 language: Option<String>,
779 title: Option<String>,
781 duration: Option<Duration>,
783 forced: bool,
785}
786
787impl SubtitleStreamInfo {
788 #[must_use]
790 pub fn builder() -> SubtitleStreamInfoBuilder {
791 SubtitleStreamInfoBuilder::default()
792 }
793
794 #[must_use]
796 #[inline]
797 pub const fn index(&self) -> u32 {
798 self.index
799 }
800
801 #[must_use]
803 #[inline]
804 pub fn codec(&self) -> &SubtitleCodec {
805 &self.codec
806 }
807
808 #[must_use]
810 #[inline]
811 pub fn codec_name(&self) -> &str {
812 &self.codec_name
813 }
814
815 #[must_use]
817 #[inline]
818 pub fn language(&self) -> Option<&str> {
819 self.language.as_deref()
820 }
821
822 #[must_use]
824 #[inline]
825 pub fn title(&self) -> Option<&str> {
826 self.title.as_deref()
827 }
828
829 #[must_use]
831 #[inline]
832 pub const fn duration(&self) -> Option<Duration> {
833 self.duration
834 }
835
836 #[must_use]
838 #[inline]
839 pub const fn is_forced(&self) -> bool {
840 self.forced
841 }
842
843 #[must_use]
845 #[inline]
846 pub fn is_text_based(&self) -> bool {
847 self.codec.is_text_based()
848 }
849}
850
851#[derive(Debug, Clone)]
853pub struct SubtitleStreamInfoBuilder {
854 index: u32,
855 codec: SubtitleCodec,
856 codec_name: String,
857 language: Option<String>,
858 title: Option<String>,
859 duration: Option<Duration>,
860 forced: bool,
861}
862
863impl Default for SubtitleStreamInfoBuilder {
864 fn default() -> Self {
865 Self {
866 index: 0,
867 codec: SubtitleCodec::Other(String::new()),
868 codec_name: String::new(),
869 language: None,
870 title: None,
871 duration: None,
872 forced: false,
873 }
874 }
875}
876
877impl SubtitleStreamInfoBuilder {
878 #[must_use]
880 pub fn index(mut self, index: u32) -> Self {
881 self.index = index;
882 self
883 }
884
885 #[must_use]
887 pub fn codec(mut self, codec: SubtitleCodec) -> Self {
888 self.codec = codec;
889 self
890 }
891
892 #[must_use]
894 pub fn codec_name(mut self, name: impl Into<String>) -> Self {
895 self.codec_name = name.into();
896 self
897 }
898
899 #[must_use]
901 pub fn language(mut self, lang: impl Into<String>) -> Self {
902 self.language = Some(lang.into());
903 self
904 }
905
906 #[must_use]
908 pub fn title(mut self, title: impl Into<String>) -> Self {
909 self.title = Some(title.into());
910 self
911 }
912
913 #[must_use]
915 pub fn duration(mut self, duration: Duration) -> Self {
916 self.duration = Some(duration);
917 self
918 }
919
920 #[must_use]
922 pub fn forced(mut self, forced: bool) -> Self {
923 self.forced = forced;
924 self
925 }
926
927 #[must_use]
929 pub fn build(self) -> SubtitleStreamInfo {
930 SubtitleStreamInfo {
931 index: self.index,
932 codec: self.codec,
933 codec_name: self.codec_name,
934 language: self.language,
935 title: self.title,
936 duration: self.duration,
937 forced: self.forced,
938 }
939 }
940}
941
942#[cfg(test)]
943mod tests {
944 use super::*;
945
946 mod video_stream_info_tests {
947 use super::*;
948
949 #[test]
950 fn test_builder_basic() {
951 let info = VideoStreamInfo::builder()
952 .index(0)
953 .codec(VideoCodec::H264)
954 .codec_name("h264")
955 .width(1920)
956 .height(1080)
957 .frame_rate(Rational::new(30, 1))
958 .pixel_format(PixelFormat::Yuv420p)
959 .build();
960
961 assert_eq!(info.index(), 0);
962 assert_eq!(info.codec(), VideoCodec::H264);
963 assert_eq!(info.codec_name(), "h264");
964 assert_eq!(info.width(), 1920);
965 assert_eq!(info.height(), 1080);
966 assert!((info.fps() - 30.0).abs() < 0.001);
967 assert_eq!(info.pixel_format(), PixelFormat::Yuv420p);
968 }
969
970 #[test]
971 fn test_builder_full() {
972 let info = VideoStreamInfo::builder()
973 .index(0)
974 .codec(VideoCodec::H265)
975 .codec_name("hevc")
976 .width(3840)
977 .height(2160)
978 .frame_rate(Rational::new(60, 1))
979 .pixel_format(PixelFormat::Yuv420p10le)
980 .duration(Duration::from_secs(120))
981 .bitrate(50_000_000)
982 .frame_count(7200)
983 .color_space(ColorSpace::Bt2020)
984 .color_range(ColorRange::Full)
985 .color_primaries(ColorPrimaries::Bt2020)
986 .build();
987
988 assert_eq!(info.codec(), VideoCodec::H265);
989 assert_eq!(info.width(), 3840);
990 assert_eq!(info.height(), 2160);
991 assert_eq!(info.duration(), Some(Duration::from_secs(120)));
992 assert_eq!(info.bitrate(), Some(50_000_000));
993 assert_eq!(info.frame_count(), Some(7200));
994 assert_eq!(info.color_space(), ColorSpace::Bt2020);
995 assert_eq!(info.color_range(), ColorRange::Full);
996 assert_eq!(info.color_primaries(), ColorPrimaries::Bt2020);
997 }
998
999 #[test]
1000 fn test_default() {
1001 let info = VideoStreamInfo::default();
1002 assert_eq!(info.index(), 0);
1003 assert_eq!(info.codec(), VideoCodec::default());
1004 assert_eq!(info.width(), 0);
1005 assert_eq!(info.height(), 0);
1006 assert!(info.duration().is_none());
1007 }
1008
1009 #[test]
1010 fn test_aspect_ratio() {
1011 let info = VideoStreamInfo::builder().width(1920).height(1080).build();
1012 assert!((info.aspect_ratio() - (16.0 / 9.0)).abs() < 0.01);
1013
1014 let info = VideoStreamInfo::builder().width(1280).height(720).build();
1015 assert!((info.aspect_ratio() - (16.0 / 9.0)).abs() < 0.01);
1016
1017 let info = VideoStreamInfo::builder().width(1920).height(0).build();
1019 assert_eq!(info.aspect_ratio(), 0.0);
1020 }
1021
1022 #[test]
1023 fn test_resolution_checks() {
1024 let sd = VideoStreamInfo::builder().width(720).height(480).build();
1026 assert!(!sd.is_hd());
1027 assert!(!sd.is_full_hd());
1028 assert!(!sd.is_4k());
1029
1030 let hd = VideoStreamInfo::builder().width(1280).height(720).build();
1032 assert!(hd.is_hd());
1033 assert!(!hd.is_full_hd());
1034 assert!(!hd.is_4k());
1035
1036 let fhd = VideoStreamInfo::builder().width(1920).height(1080).build();
1038 assert!(fhd.is_hd());
1039 assert!(fhd.is_full_hd());
1040 assert!(!fhd.is_4k());
1041
1042 let uhd = VideoStreamInfo::builder().width(3840).height(2160).build();
1044 assert!(uhd.is_hd());
1045 assert!(uhd.is_full_hd());
1046 assert!(uhd.is_4k());
1047 }
1048
1049 #[test]
1050 fn test_is_hdr() {
1051 let hdr = VideoStreamInfo::builder()
1053 .width(3840)
1054 .height(2160)
1055 .color_primaries(ColorPrimaries::Bt2020)
1056 .pixel_format(PixelFormat::Yuv420p10le)
1057 .build();
1058 assert!(hdr.is_hdr());
1059
1060 let hdr_p010 = VideoStreamInfo::builder()
1062 .width(3840)
1063 .height(2160)
1064 .color_primaries(ColorPrimaries::Bt2020)
1065 .pixel_format(PixelFormat::P010le)
1066 .build();
1067 assert!(hdr_p010.is_hdr());
1068
1069 let sdr_hd = VideoStreamInfo::builder()
1071 .width(1920)
1072 .height(1080)
1073 .color_primaries(ColorPrimaries::Bt709)
1074 .pixel_format(PixelFormat::Yuv420p)
1075 .build();
1076 assert!(!sdr_hd.is_hdr());
1077
1078 let wide_gamut_8bit = VideoStreamInfo::builder()
1080 .width(3840)
1081 .height(2160)
1082 .color_primaries(ColorPrimaries::Bt2020)
1083 .pixel_format(PixelFormat::Yuv420p) .build();
1085 assert!(!wide_gamut_8bit.is_hdr());
1086
1087 let hd_10bit = VideoStreamInfo::builder()
1089 .width(1920)
1090 .height(1080)
1091 .color_primaries(ColorPrimaries::Bt709)
1092 .pixel_format(PixelFormat::Yuv420p10le)
1093 .build();
1094 assert!(!hd_10bit.is_hdr());
1095
1096 let default = VideoStreamInfo::default();
1098 assert!(!default.is_hdr());
1099 }
1100
1101 #[test]
1102 fn test_debug() {
1103 let info = VideoStreamInfo::builder()
1104 .index(0)
1105 .codec(VideoCodec::H264)
1106 .width(1920)
1107 .height(1080)
1108 .build();
1109 let debug = format!("{info:?}");
1110 assert!(debug.contains("VideoStreamInfo"));
1111 assert!(debug.contains("1920"));
1112 assert!(debug.contains("1080"));
1113 }
1114
1115 #[test]
1116 fn test_clone() {
1117 let info = VideoStreamInfo::builder()
1118 .index(0)
1119 .codec(VideoCodec::H264)
1120 .codec_name("h264")
1121 .width(1920)
1122 .height(1080)
1123 .build();
1124 let cloned = info.clone();
1125 assert_eq!(info.width(), cloned.width());
1126 assert_eq!(info.height(), cloned.height());
1127 assert_eq!(info.codec_name(), cloned.codec_name());
1128 }
1129 }
1130
1131 mod audio_stream_info_tests {
1132 use super::*;
1133
1134 #[test]
1135 fn test_builder_basic() {
1136 let info = AudioStreamInfo::builder()
1137 .index(1)
1138 .codec(AudioCodec::Aac)
1139 .codec_name("aac")
1140 .sample_rate(48000)
1141 .channels(2)
1142 .sample_format(SampleFormat::F32)
1143 .build();
1144
1145 assert_eq!(info.index(), 1);
1146 assert_eq!(info.codec(), AudioCodec::Aac);
1147 assert_eq!(info.codec_name(), "aac");
1148 assert_eq!(info.sample_rate(), 48000);
1149 assert_eq!(info.channels(), 2);
1150 assert_eq!(info.sample_format(), SampleFormat::F32);
1151 assert_eq!(info.channel_layout(), ChannelLayout::Stereo);
1152 }
1153
1154 #[test]
1155 fn test_builder_full() {
1156 let info = AudioStreamInfo::builder()
1157 .index(2)
1158 .codec(AudioCodec::Flac)
1159 .codec_name("flac")
1160 .sample_rate(96000)
1161 .channels(6)
1162 .channel_layout(ChannelLayout::Surround5_1)
1163 .sample_format(SampleFormat::I32)
1164 .duration(Duration::from_secs(300))
1165 .bitrate(1_411_200)
1166 .language("jpn")
1167 .build();
1168
1169 assert_eq!(info.codec(), AudioCodec::Flac);
1170 assert_eq!(info.sample_rate(), 96000);
1171 assert_eq!(info.channels(), 6);
1172 assert_eq!(info.channel_layout(), ChannelLayout::Surround5_1);
1173 assert_eq!(info.duration(), Some(Duration::from_secs(300)));
1174 assert_eq!(info.bitrate(), Some(1_411_200));
1175 assert_eq!(info.language(), Some("jpn"));
1176 }
1177
1178 #[test]
1179 fn test_default() {
1180 let info = AudioStreamInfo::default();
1181 assert_eq!(info.index(), 0);
1182 assert_eq!(info.codec(), AudioCodec::default());
1183 assert_eq!(info.sample_rate(), 48000);
1184 assert_eq!(info.channels(), 2);
1185 assert!(info.duration().is_none());
1186 }
1187
1188 #[test]
1189 fn test_auto_channel_layout() {
1190 let mono = AudioStreamInfo::builder().channels(1).build();
1192 assert_eq!(mono.channel_layout(), ChannelLayout::Mono);
1193
1194 let stereo = AudioStreamInfo::builder().channels(2).build();
1195 assert_eq!(stereo.channel_layout(), ChannelLayout::Stereo);
1196
1197 let surround = AudioStreamInfo::builder().channels(6).build();
1198 assert_eq!(surround.channel_layout(), ChannelLayout::Surround5_1);
1199
1200 let custom = AudioStreamInfo::builder()
1202 .channels(6)
1203 .channel_layout(ChannelLayout::Other(6))
1204 .build();
1205 assert_eq!(custom.channel_layout(), ChannelLayout::Other(6));
1206 }
1207
1208 #[test]
1209 fn test_channel_checks() {
1210 let mono = AudioStreamInfo::builder().channels(1).build();
1211 assert!(mono.is_mono());
1212 assert!(!mono.is_stereo());
1213 assert!(!mono.is_surround());
1214
1215 let stereo = AudioStreamInfo::builder().channels(2).build();
1216 assert!(!stereo.is_mono());
1217 assert!(stereo.is_stereo());
1218 assert!(!stereo.is_surround());
1219
1220 let surround = AudioStreamInfo::builder().channels(6).build();
1221 assert!(!surround.is_mono());
1222 assert!(!surround.is_stereo());
1223 assert!(surround.is_surround());
1224 }
1225
1226 #[test]
1227 fn test_debug() {
1228 let info = AudioStreamInfo::builder()
1229 .index(1)
1230 .codec(AudioCodec::Aac)
1231 .sample_rate(48000)
1232 .channels(2)
1233 .build();
1234 let debug = format!("{info:?}");
1235 assert!(debug.contains("AudioStreamInfo"));
1236 assert!(debug.contains("48000"));
1237 }
1238
1239 #[test]
1240 fn test_clone() {
1241 let info = AudioStreamInfo::builder()
1242 .index(1)
1243 .codec(AudioCodec::Aac)
1244 .codec_name("aac")
1245 .sample_rate(48000)
1246 .channels(2)
1247 .language("eng")
1248 .build();
1249 let cloned = info.clone();
1250 assert_eq!(info.sample_rate(), cloned.sample_rate());
1251 assert_eq!(info.channels(), cloned.channels());
1252 assert_eq!(info.language(), cloned.language());
1253 assert_eq!(info.codec_name(), cloned.codec_name());
1254 }
1255 }
1256
1257 mod subtitle_stream_info_tests {
1258 use super::*;
1259
1260 #[test]
1261 fn builder_should_store_all_fields() {
1262 let info = SubtitleStreamInfo::builder()
1263 .index(2)
1264 .codec(SubtitleCodec::Srt)
1265 .codec_name("srt")
1266 .language("eng")
1267 .title("English")
1268 .duration(Duration::from_secs(120))
1269 .forced(true)
1270 .build();
1271
1272 assert_eq!(info.index(), 2);
1273 assert_eq!(info.codec(), &SubtitleCodec::Srt);
1274 assert_eq!(info.codec_name(), "srt");
1275 assert_eq!(info.language(), Some("eng"));
1276 assert_eq!(info.title(), Some("English"));
1277 assert_eq!(info.duration(), Some(Duration::from_secs(120)));
1278 assert!(info.is_forced());
1279 }
1280
1281 #[test]
1282 fn is_forced_should_default_to_false() {
1283 let info = SubtitleStreamInfo::builder()
1284 .codec(SubtitleCodec::Ass)
1285 .build();
1286 assert!(!info.is_forced());
1287 }
1288
1289 #[test]
1290 fn is_text_based_should_delegate_to_codec() {
1291 let text = SubtitleStreamInfo::builder()
1292 .codec(SubtitleCodec::Srt)
1293 .build();
1294 assert!(text.is_text_based());
1295
1296 let bitmap = SubtitleStreamInfo::builder()
1297 .codec(SubtitleCodec::Hdmv)
1298 .build();
1299 assert!(!bitmap.is_text_based());
1300 }
1301
1302 #[test]
1303 fn optional_fields_should_default_to_none() {
1304 let info = SubtitleStreamInfo::builder().build();
1305 assert!(info.language().is_none());
1306 assert!(info.title().is_none());
1307 assert!(info.duration().is_none());
1308 }
1309 }
1310}