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]
560 #[inline]
561 pub const fn channels(&self) -> u32 {
562 self.channels
563 }
564
565 #[must_use]
567 #[inline]
568 pub const fn channel_layout(&self) -> ChannelLayout {
569 self.channel_layout
570 }
571
572 #[must_use]
574 #[inline]
575 pub const fn sample_format(&self) -> SampleFormat {
576 self.sample_format
577 }
578
579 #[must_use]
581 #[inline]
582 pub const fn duration(&self) -> Option<Duration> {
583 self.duration
584 }
585
586 #[must_use]
588 #[inline]
589 pub const fn bitrate(&self) -> Option<u64> {
590 self.bitrate
591 }
592
593 #[must_use]
595 #[inline]
596 pub fn language(&self) -> Option<&str> {
597 self.language.as_deref()
598 }
599
600 #[must_use]
602 #[inline]
603 pub const fn is_mono(&self) -> bool {
604 self.channels == 1
605 }
606
607 #[must_use]
609 #[inline]
610 pub const fn is_stereo(&self) -> bool {
611 self.channels == 2
612 }
613
614 #[must_use]
616 #[inline]
617 pub const fn is_surround(&self) -> bool {
618 self.channels > 2
619 }
620}
621
622impl Default for AudioStreamInfo {
623 fn default() -> Self {
624 Self {
625 index: 0,
626 codec: AudioCodec::default(),
627 codec_name: String::new(),
628 sample_rate: 48000,
629 channels: 2,
630 channel_layout: ChannelLayout::default(),
631 sample_format: SampleFormat::default(),
632 duration: None,
633 bitrate: None,
634 language: None,
635 }
636 }
637}
638
639#[derive(Debug, Clone, Default)]
641pub struct AudioStreamInfoBuilder {
642 index: u32,
643 codec: AudioCodec,
644 codec_name: String,
645 sample_rate: u32,
646 channels: u32,
647 channel_layout: Option<ChannelLayout>,
648 sample_format: SampleFormat,
649 duration: Option<Duration>,
650 bitrate: Option<u64>,
651 language: Option<String>,
652}
653
654impl AudioStreamInfoBuilder {
655 #[must_use]
657 pub fn index(mut self, index: u32) -> Self {
658 self.index = index;
659 self
660 }
661
662 #[must_use]
664 pub fn codec(mut self, codec: AudioCodec) -> Self {
665 self.codec = codec;
666 self
667 }
668
669 #[must_use]
671 pub fn codec_name(mut self, name: impl Into<String>) -> Self {
672 self.codec_name = name.into();
673 self
674 }
675
676 #[must_use]
678 pub fn sample_rate(mut self, rate: u32) -> Self {
679 self.sample_rate = rate;
680 self
681 }
682
683 #[must_use]
687 pub fn channels(mut self, channels: u32) -> Self {
688 self.channels = channels;
689 self
690 }
691
692 #[must_use]
694 pub fn channel_layout(mut self, layout: ChannelLayout) -> Self {
695 self.channel_layout = Some(layout);
696 self
697 }
698
699 #[must_use]
701 pub fn sample_format(mut self, format: SampleFormat) -> Self {
702 self.sample_format = format;
703 self
704 }
705
706 #[must_use]
708 pub fn duration(mut self, duration: Duration) -> Self {
709 self.duration = Some(duration);
710 self
711 }
712
713 #[must_use]
715 pub fn bitrate(mut self, bitrate: u64) -> Self {
716 self.bitrate = Some(bitrate);
717 self
718 }
719
720 #[must_use]
722 pub fn language(mut self, lang: impl Into<String>) -> Self {
723 self.language = Some(lang.into());
724 self
725 }
726
727 #[must_use]
729 pub fn build(self) -> AudioStreamInfo {
730 let channel_layout = self.channel_layout.unwrap_or_else(|| {
731 log::warn!(
732 "channel_layout not set, deriving from channel count \
733 channels={} fallback=from_channels",
734 self.channels
735 );
736 ChannelLayout::from_channels(self.channels)
737 });
738
739 AudioStreamInfo {
740 index: self.index,
741 codec: self.codec,
742 codec_name: self.codec_name,
743 sample_rate: self.sample_rate,
744 channels: self.channels,
745 channel_layout,
746 sample_format: self.sample_format,
747 duration: self.duration,
748 bitrate: self.bitrate,
749 language: self.language,
750 }
751 }
752}
753
754#[derive(Debug, Clone)]
775pub struct SubtitleStreamInfo {
776 index: u32,
778 codec: SubtitleCodec,
780 codec_name: String,
782 language: Option<String>,
784 title: Option<String>,
786 duration: Option<Duration>,
788 forced: bool,
790}
791
792impl SubtitleStreamInfo {
793 #[must_use]
795 pub fn builder() -> SubtitleStreamInfoBuilder {
796 SubtitleStreamInfoBuilder::default()
797 }
798
799 #[must_use]
801 #[inline]
802 pub const fn index(&self) -> u32 {
803 self.index
804 }
805
806 #[must_use]
808 #[inline]
809 pub fn codec(&self) -> &SubtitleCodec {
810 &self.codec
811 }
812
813 #[must_use]
815 #[inline]
816 pub fn codec_name(&self) -> &str {
817 &self.codec_name
818 }
819
820 #[must_use]
822 #[inline]
823 pub fn language(&self) -> Option<&str> {
824 self.language.as_deref()
825 }
826
827 #[must_use]
829 #[inline]
830 pub fn title(&self) -> Option<&str> {
831 self.title.as_deref()
832 }
833
834 #[must_use]
836 #[inline]
837 pub const fn duration(&self) -> Option<Duration> {
838 self.duration
839 }
840
841 #[must_use]
843 #[inline]
844 pub const fn is_forced(&self) -> bool {
845 self.forced
846 }
847
848 #[must_use]
850 #[inline]
851 pub fn is_text_based(&self) -> bool {
852 self.codec.is_text_based()
853 }
854}
855
856#[derive(Debug, Clone)]
858pub struct SubtitleStreamInfoBuilder {
859 index: u32,
860 codec: SubtitleCodec,
861 codec_name: String,
862 language: Option<String>,
863 title: Option<String>,
864 duration: Option<Duration>,
865 forced: bool,
866}
867
868impl Default for SubtitleStreamInfoBuilder {
869 fn default() -> Self {
870 Self {
871 index: 0,
872 codec: SubtitleCodec::Other(String::new()),
873 codec_name: String::new(),
874 language: None,
875 title: None,
876 duration: None,
877 forced: false,
878 }
879 }
880}
881
882impl SubtitleStreamInfoBuilder {
883 #[must_use]
885 pub fn index(mut self, index: u32) -> Self {
886 self.index = index;
887 self
888 }
889
890 #[must_use]
892 pub fn codec(mut self, codec: SubtitleCodec) -> Self {
893 self.codec = codec;
894 self
895 }
896
897 #[must_use]
899 pub fn codec_name(mut self, name: impl Into<String>) -> Self {
900 self.codec_name = name.into();
901 self
902 }
903
904 #[must_use]
906 pub fn language(mut self, lang: impl Into<String>) -> Self {
907 self.language = Some(lang.into());
908 self
909 }
910
911 #[must_use]
913 pub fn title(mut self, title: impl Into<String>) -> Self {
914 self.title = Some(title.into());
915 self
916 }
917
918 #[must_use]
920 pub fn duration(mut self, duration: Duration) -> Self {
921 self.duration = Some(duration);
922 self
923 }
924
925 #[must_use]
927 pub fn forced(mut self, forced: bool) -> Self {
928 self.forced = forced;
929 self
930 }
931
932 #[must_use]
934 pub fn build(self) -> SubtitleStreamInfo {
935 SubtitleStreamInfo {
936 index: self.index,
937 codec: self.codec,
938 codec_name: self.codec_name,
939 language: self.language,
940 title: self.title,
941 duration: self.duration,
942 forced: self.forced,
943 }
944 }
945}
946
947#[cfg(test)]
948mod tests {
949 use super::*;
950
951 mod video_stream_info_tests {
952 use super::*;
953
954 #[test]
955 fn test_builder_basic() {
956 let info = VideoStreamInfo::builder()
957 .index(0)
958 .codec(VideoCodec::H264)
959 .codec_name("h264")
960 .width(1920)
961 .height(1080)
962 .frame_rate(Rational::new(30, 1))
963 .pixel_format(PixelFormat::Yuv420p)
964 .build();
965
966 assert_eq!(info.index(), 0);
967 assert_eq!(info.codec(), VideoCodec::H264);
968 assert_eq!(info.codec_name(), "h264");
969 assert_eq!(info.width(), 1920);
970 assert_eq!(info.height(), 1080);
971 assert!((info.fps() - 30.0).abs() < 0.001);
972 assert_eq!(info.pixel_format(), PixelFormat::Yuv420p);
973 }
974
975 #[test]
976 fn test_builder_full() {
977 let info = VideoStreamInfo::builder()
978 .index(0)
979 .codec(VideoCodec::H265)
980 .codec_name("hevc")
981 .width(3840)
982 .height(2160)
983 .frame_rate(Rational::new(60, 1))
984 .pixel_format(PixelFormat::Yuv420p10le)
985 .duration(Duration::from_secs(120))
986 .bitrate(50_000_000)
987 .frame_count(7200)
988 .color_space(ColorSpace::Bt2020)
989 .color_range(ColorRange::Full)
990 .color_primaries(ColorPrimaries::Bt2020)
991 .build();
992
993 assert_eq!(info.codec(), VideoCodec::H265);
994 assert_eq!(info.width(), 3840);
995 assert_eq!(info.height(), 2160);
996 assert_eq!(info.duration(), Some(Duration::from_secs(120)));
997 assert_eq!(info.bitrate(), Some(50_000_000));
998 assert_eq!(info.frame_count(), Some(7200));
999 assert_eq!(info.color_space(), ColorSpace::Bt2020);
1000 assert_eq!(info.color_range(), ColorRange::Full);
1001 assert_eq!(info.color_primaries(), ColorPrimaries::Bt2020);
1002 }
1003
1004 #[test]
1005 fn test_default() {
1006 let info = VideoStreamInfo::default();
1007 assert_eq!(info.index(), 0);
1008 assert_eq!(info.codec(), VideoCodec::default());
1009 assert_eq!(info.width(), 0);
1010 assert_eq!(info.height(), 0);
1011 assert!(info.duration().is_none());
1012 }
1013
1014 #[test]
1015 fn test_aspect_ratio() {
1016 let info = VideoStreamInfo::builder().width(1920).height(1080).build();
1017 assert!((info.aspect_ratio() - (16.0 / 9.0)).abs() < 0.01);
1018
1019 let info = VideoStreamInfo::builder().width(1280).height(720).build();
1020 assert!((info.aspect_ratio() - (16.0 / 9.0)).abs() < 0.01);
1021
1022 let info = VideoStreamInfo::builder().width(1920).height(0).build();
1024 assert_eq!(info.aspect_ratio(), 0.0);
1025 }
1026
1027 #[test]
1028 fn test_resolution_checks() {
1029 let sd = VideoStreamInfo::builder().width(720).height(480).build();
1031 assert!(!sd.is_hd());
1032 assert!(!sd.is_full_hd());
1033 assert!(!sd.is_4k());
1034
1035 let hd = VideoStreamInfo::builder().width(1280).height(720).build();
1037 assert!(hd.is_hd());
1038 assert!(!hd.is_full_hd());
1039 assert!(!hd.is_4k());
1040
1041 let fhd = VideoStreamInfo::builder().width(1920).height(1080).build();
1043 assert!(fhd.is_hd());
1044 assert!(fhd.is_full_hd());
1045 assert!(!fhd.is_4k());
1046
1047 let uhd = VideoStreamInfo::builder().width(3840).height(2160).build();
1049 assert!(uhd.is_hd());
1050 assert!(uhd.is_full_hd());
1051 assert!(uhd.is_4k());
1052 }
1053
1054 #[test]
1055 fn test_is_hdr() {
1056 let hdr = VideoStreamInfo::builder()
1058 .width(3840)
1059 .height(2160)
1060 .color_primaries(ColorPrimaries::Bt2020)
1061 .pixel_format(PixelFormat::Yuv420p10le)
1062 .build();
1063 assert!(hdr.is_hdr());
1064
1065 let hdr_p010 = VideoStreamInfo::builder()
1067 .width(3840)
1068 .height(2160)
1069 .color_primaries(ColorPrimaries::Bt2020)
1070 .pixel_format(PixelFormat::P010le)
1071 .build();
1072 assert!(hdr_p010.is_hdr());
1073
1074 let sdr_hd = VideoStreamInfo::builder()
1076 .width(1920)
1077 .height(1080)
1078 .color_primaries(ColorPrimaries::Bt709)
1079 .pixel_format(PixelFormat::Yuv420p)
1080 .build();
1081 assert!(!sdr_hd.is_hdr());
1082
1083 let wide_gamut_8bit = VideoStreamInfo::builder()
1085 .width(3840)
1086 .height(2160)
1087 .color_primaries(ColorPrimaries::Bt2020)
1088 .pixel_format(PixelFormat::Yuv420p) .build();
1090 assert!(!wide_gamut_8bit.is_hdr());
1091
1092 let hd_10bit = VideoStreamInfo::builder()
1094 .width(1920)
1095 .height(1080)
1096 .color_primaries(ColorPrimaries::Bt709)
1097 .pixel_format(PixelFormat::Yuv420p10le)
1098 .build();
1099 assert!(!hd_10bit.is_hdr());
1100
1101 let default = VideoStreamInfo::default();
1103 assert!(!default.is_hdr());
1104 }
1105
1106 #[test]
1107 fn test_debug() {
1108 let info = VideoStreamInfo::builder()
1109 .index(0)
1110 .codec(VideoCodec::H264)
1111 .width(1920)
1112 .height(1080)
1113 .build();
1114 let debug = format!("{info:?}");
1115 assert!(debug.contains("VideoStreamInfo"));
1116 assert!(debug.contains("1920"));
1117 assert!(debug.contains("1080"));
1118 }
1119
1120 #[test]
1121 fn test_clone() {
1122 let info = VideoStreamInfo::builder()
1123 .index(0)
1124 .codec(VideoCodec::H264)
1125 .codec_name("h264")
1126 .width(1920)
1127 .height(1080)
1128 .build();
1129 let cloned = info.clone();
1130 assert_eq!(info.width(), cloned.width());
1131 assert_eq!(info.height(), cloned.height());
1132 assert_eq!(info.codec_name(), cloned.codec_name());
1133 }
1134 }
1135
1136 mod audio_stream_info_tests {
1137 use super::*;
1138
1139 #[test]
1140 fn test_builder_basic() {
1141 let info = AudioStreamInfo::builder()
1142 .index(1)
1143 .codec(AudioCodec::Aac)
1144 .codec_name("aac")
1145 .sample_rate(48000)
1146 .channels(2)
1147 .sample_format(SampleFormat::F32)
1148 .build();
1149
1150 assert_eq!(info.index(), 1);
1151 assert_eq!(info.codec(), AudioCodec::Aac);
1152 assert_eq!(info.codec_name(), "aac");
1153 assert_eq!(info.sample_rate(), 48000);
1154 assert_eq!(info.channels(), 2);
1155 assert_eq!(info.sample_format(), SampleFormat::F32);
1156 assert_eq!(info.channel_layout(), ChannelLayout::Stereo);
1157 }
1158
1159 #[test]
1160 fn test_builder_full() {
1161 let info = AudioStreamInfo::builder()
1162 .index(2)
1163 .codec(AudioCodec::Flac)
1164 .codec_name("flac")
1165 .sample_rate(96000)
1166 .channels(6)
1167 .channel_layout(ChannelLayout::Surround5_1)
1168 .sample_format(SampleFormat::I32)
1169 .duration(Duration::from_secs(300))
1170 .bitrate(1_411_200)
1171 .language("jpn")
1172 .build();
1173
1174 assert_eq!(info.codec(), AudioCodec::Flac);
1175 assert_eq!(info.sample_rate(), 96000);
1176 assert_eq!(info.channels(), 6);
1177 assert_eq!(info.channel_layout(), ChannelLayout::Surround5_1);
1178 assert_eq!(info.duration(), Some(Duration::from_secs(300)));
1179 assert_eq!(info.bitrate(), Some(1_411_200));
1180 assert_eq!(info.language(), Some("jpn"));
1181 }
1182
1183 #[test]
1184 fn test_default() {
1185 let info = AudioStreamInfo::default();
1186 assert_eq!(info.index(), 0);
1187 assert_eq!(info.codec(), AudioCodec::default());
1188 assert_eq!(info.sample_rate(), 48000);
1189 assert_eq!(info.channels(), 2);
1190 assert!(info.duration().is_none());
1191 }
1192
1193 #[test]
1194 fn test_auto_channel_layout() {
1195 let mono = AudioStreamInfo::builder().channels(1).build();
1197 assert_eq!(mono.channel_layout(), ChannelLayout::Mono);
1198
1199 let stereo = AudioStreamInfo::builder().channels(2).build();
1200 assert_eq!(stereo.channel_layout(), ChannelLayout::Stereo);
1201
1202 let surround = AudioStreamInfo::builder().channels(6).build();
1203 assert_eq!(surround.channel_layout(), ChannelLayout::Surround5_1);
1204
1205 let custom = AudioStreamInfo::builder()
1207 .channels(6)
1208 .channel_layout(ChannelLayout::Other(6))
1209 .build();
1210 assert_eq!(custom.channel_layout(), ChannelLayout::Other(6));
1211 }
1212
1213 #[test]
1214 fn test_channel_checks() {
1215 let mono = AudioStreamInfo::builder().channels(1).build();
1216 assert!(mono.is_mono());
1217 assert!(!mono.is_stereo());
1218 assert!(!mono.is_surround());
1219
1220 let stereo = AudioStreamInfo::builder().channels(2).build();
1221 assert!(!stereo.is_mono());
1222 assert!(stereo.is_stereo());
1223 assert!(!stereo.is_surround());
1224
1225 let surround = AudioStreamInfo::builder().channels(6).build();
1226 assert!(!surround.is_mono());
1227 assert!(!surround.is_stereo());
1228 assert!(surround.is_surround());
1229 }
1230
1231 #[test]
1232 fn test_debug() {
1233 let info = AudioStreamInfo::builder()
1234 .index(1)
1235 .codec(AudioCodec::Aac)
1236 .sample_rate(48000)
1237 .channels(2)
1238 .build();
1239 let debug = format!("{info:?}");
1240 assert!(debug.contains("AudioStreamInfo"));
1241 assert!(debug.contains("48000"));
1242 }
1243
1244 #[test]
1245 fn test_clone() {
1246 let info = AudioStreamInfo::builder()
1247 .index(1)
1248 .codec(AudioCodec::Aac)
1249 .codec_name("aac")
1250 .sample_rate(48000)
1251 .channels(2)
1252 .language("eng")
1253 .build();
1254 let cloned = info.clone();
1255 assert_eq!(info.sample_rate(), cloned.sample_rate());
1256 assert_eq!(info.channels(), cloned.channels());
1257 assert_eq!(info.language(), cloned.language());
1258 assert_eq!(info.codec_name(), cloned.codec_name());
1259 }
1260 }
1261
1262 mod subtitle_stream_info_tests {
1263 use super::*;
1264
1265 #[test]
1266 fn builder_should_store_all_fields() {
1267 let info = SubtitleStreamInfo::builder()
1268 .index(2)
1269 .codec(SubtitleCodec::Srt)
1270 .codec_name("srt")
1271 .language("eng")
1272 .title("English")
1273 .duration(Duration::from_secs(120))
1274 .forced(true)
1275 .build();
1276
1277 assert_eq!(info.index(), 2);
1278 assert_eq!(info.codec(), &SubtitleCodec::Srt);
1279 assert_eq!(info.codec_name(), "srt");
1280 assert_eq!(info.language(), Some("eng"));
1281 assert_eq!(info.title(), Some("English"));
1282 assert_eq!(info.duration(), Some(Duration::from_secs(120)));
1283 assert!(info.is_forced());
1284 }
1285
1286 #[test]
1287 fn is_forced_should_default_to_false() {
1288 let info = SubtitleStreamInfo::builder()
1289 .codec(SubtitleCodec::Ass)
1290 .build();
1291 assert!(!info.is_forced());
1292 }
1293
1294 #[test]
1295 fn is_text_based_should_delegate_to_codec() {
1296 let text = SubtitleStreamInfo::builder()
1297 .codec(SubtitleCodec::Srt)
1298 .build();
1299 assert!(text.is_text_based());
1300
1301 let bitmap = SubtitleStreamInfo::builder()
1302 .codec(SubtitleCodec::Hdmv)
1303 .build();
1304 assert!(!bitmap.is_text_based());
1305 }
1306
1307 #[test]
1308 fn optional_fields_should_default_to_none() {
1309 let info = SubtitleStreamInfo::builder().build();
1310 assert!(info.language().is_none());
1311 assert!(info.title().is_none());
1312 assert!(info.duration().is_none());
1313 }
1314 }
1315}