1use crate::error::{Error, Result};
47
48pub const AC3_SYNC_WORD: u16 = 0x0B77;
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum Ac3SampleRate {
59 Hz48000,
61 Hz44100,
63 Hz32000,
65 Reserved,
67}
68
69impl Ac3SampleRate {
70 fn from_code(code: u8) -> Self {
71 match code & 0b11 {
72 0 => Self::Hz48000,
73 1 => Self::Hz44100,
74 2 => Self::Hz32000,
75 _ => Self::Reserved,
76 }
77 }
78
79 pub fn hz(self) -> Option<u32> {
82 match self {
83 Self::Hz48000 => Some(48_000),
84 Self::Hz44100 => Some(44_100),
85 Self::Hz32000 => Some(32_000),
86 Self::Reserved => None,
87 }
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum Ac3AudioCodingMode {
98 DualMono,
100 Mono,
102 Stereo,
104 ThreeZero,
106 TwoOne,
108 ThreeOne,
110 TwoTwo,
112 ThreeTwo,
115}
116
117impl Ac3AudioCodingMode {
118 fn from_code(code: u8) -> Self {
119 match code & 0b111 {
120 0 => Self::DualMono,
121 1 => Self::Mono,
122 2 => Self::Stereo,
123 3 => Self::ThreeZero,
124 4 => Self::TwoOne,
125 5 => Self::ThreeOne,
126 6 => Self::TwoTwo,
127 _ => Self::ThreeTwo,
128 }
129 }
130
131 pub fn code(self) -> u8 {
133 match self {
134 Self::DualMono => 0,
135 Self::Mono => 1,
136 Self::Stereo => 2,
137 Self::ThreeZero => 3,
138 Self::TwoOne => 4,
139 Self::ThreeOne => 5,
140 Self::TwoTwo => 6,
141 Self::ThreeTwo => 7,
142 }
143 }
144
145 pub fn channel_count(self) -> u8 {
149 match self {
150 Self::DualMono => 2,
151 Self::Mono => 1,
152 Self::Stereo => 2,
153 Self::ThreeZero => 3,
154 Self::TwoOne => 3,
155 Self::ThreeOne => 4,
156 Self::TwoTwo => 4,
157 Self::ThreeTwo => 5,
158 }
159 }
160
161 pub fn has_center_mix_level(self) -> bool {
165 let code = self.code();
166 (code & 0x1) != 0 && code != 0x1
167 }
168
169 pub fn has_surround_mix_level(self) -> bool {
173 (self.code() & 0x4) != 0
174 }
175
176 pub fn has_dolby_surround_mode(self) -> bool {
179 self.code() == 0x2
180 }
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189pub enum Ac3BitstreamMode {
190 CompleteMain,
192 MusicAndEffects,
194 VisuallyImpaired,
196 HearingImpaired,
198 Dialogue,
200 Commentary,
202 Emergency,
204 VoiceOverOrKaraoke,
207}
208
209impl Ac3BitstreamMode {
210 fn from_code(code: u8) -> Self {
211 match code & 0b111 {
212 0 => Self::CompleteMain,
213 1 => Self::MusicAndEffects,
214 2 => Self::VisuallyImpaired,
215 3 => Self::HearingImpaired,
216 4 => Self::Dialogue,
217 5 => Self::Commentary,
218 6 => Self::Emergency,
219 _ => Self::VoiceOverOrKaraoke,
220 }
221 }
222}
223
224#[derive(Debug, Clone, Copy, PartialEq, Eq)]
227struct FrmSizeRow {
228 bitrate_kbps: u16,
230 words_32k: u16,
232 words_44k: u16,
234 words_48k: u16,
236}
237
238const FRM_SIZE_TABLE: [FrmSizeRow; 38] = [
243 FrmSizeRow {
244 bitrate_kbps: 32,
245 words_32k: 96,
246 words_44k: 69,
247 words_48k: 64,
248 },
249 FrmSizeRow {
250 bitrate_kbps: 32,
251 words_32k: 96,
252 words_44k: 70,
253 words_48k: 64,
254 },
255 FrmSizeRow {
256 bitrate_kbps: 40,
257 words_32k: 120,
258 words_44k: 87,
259 words_48k: 80,
260 },
261 FrmSizeRow {
262 bitrate_kbps: 40,
263 words_32k: 120,
264 words_44k: 88,
265 words_48k: 80,
266 },
267 FrmSizeRow {
268 bitrate_kbps: 48,
269 words_32k: 144,
270 words_44k: 104,
271 words_48k: 96,
272 },
273 FrmSizeRow {
274 bitrate_kbps: 48,
275 words_32k: 144,
276 words_44k: 105,
277 words_48k: 96,
278 },
279 FrmSizeRow {
280 bitrate_kbps: 56,
281 words_32k: 168,
282 words_44k: 121,
283 words_48k: 112,
284 },
285 FrmSizeRow {
286 bitrate_kbps: 56,
287 words_32k: 168,
288 words_44k: 122,
289 words_48k: 112,
290 },
291 FrmSizeRow {
292 bitrate_kbps: 64,
293 words_32k: 192,
294 words_44k: 139,
295 words_48k: 128,
296 },
297 FrmSizeRow {
298 bitrate_kbps: 64,
299 words_32k: 192,
300 words_44k: 140,
301 words_48k: 128,
302 },
303 FrmSizeRow {
304 bitrate_kbps: 80,
305 words_32k: 240,
306 words_44k: 174,
307 words_48k: 160,
308 },
309 FrmSizeRow {
310 bitrate_kbps: 80,
311 words_32k: 240,
312 words_44k: 175,
313 words_48k: 160,
314 },
315 FrmSizeRow {
316 bitrate_kbps: 96,
317 words_32k: 288,
318 words_44k: 208,
319 words_48k: 192,
320 },
321 FrmSizeRow {
322 bitrate_kbps: 96,
323 words_32k: 288,
324 words_44k: 209,
325 words_48k: 192,
326 },
327 FrmSizeRow {
328 bitrate_kbps: 112,
329 words_32k: 336,
330 words_44k: 243,
331 words_48k: 224,
332 },
333 FrmSizeRow {
334 bitrate_kbps: 112,
335 words_32k: 336,
336 words_44k: 244,
337 words_48k: 224,
338 },
339 FrmSizeRow {
340 bitrate_kbps: 128,
341 words_32k: 384,
342 words_44k: 278,
343 words_48k: 256,
344 },
345 FrmSizeRow {
346 bitrate_kbps: 128,
347 words_32k: 384,
348 words_44k: 279,
349 words_48k: 256,
350 },
351 FrmSizeRow {
352 bitrate_kbps: 160,
353 words_32k: 480,
354 words_44k: 348,
355 words_48k: 320,
356 },
357 FrmSizeRow {
358 bitrate_kbps: 160,
359 words_32k: 480,
360 words_44k: 349,
361 words_48k: 320,
362 },
363 FrmSizeRow {
364 bitrate_kbps: 192,
365 words_32k: 576,
366 words_44k: 417,
367 words_48k: 384,
368 },
369 FrmSizeRow {
370 bitrate_kbps: 192,
371 words_32k: 576,
372 words_44k: 418,
373 words_48k: 384,
374 },
375 FrmSizeRow {
376 bitrate_kbps: 224,
377 words_32k: 672,
378 words_44k: 487,
379 words_48k: 448,
380 },
381 FrmSizeRow {
382 bitrate_kbps: 224,
383 words_32k: 672,
384 words_44k: 488,
385 words_48k: 448,
386 },
387 FrmSizeRow {
388 bitrate_kbps: 256,
389 words_32k: 768,
390 words_44k: 557,
391 words_48k: 512,
392 },
393 FrmSizeRow {
394 bitrate_kbps: 256,
395 words_32k: 768,
396 words_44k: 558,
397 words_48k: 512,
398 },
399 FrmSizeRow {
400 bitrate_kbps: 320,
401 words_32k: 960,
402 words_44k: 696,
403 words_48k: 640,
404 },
405 FrmSizeRow {
406 bitrate_kbps: 320,
407 words_32k: 960,
408 words_44k: 697,
409 words_48k: 640,
410 },
411 FrmSizeRow {
412 bitrate_kbps: 384,
413 words_32k: 1152,
414 words_44k: 835,
415 words_48k: 768,
416 },
417 FrmSizeRow {
418 bitrate_kbps: 384,
419 words_32k: 1152,
420 words_44k: 836,
421 words_48k: 768,
422 },
423 FrmSizeRow {
424 bitrate_kbps: 448,
425 words_32k: 1344,
426 words_44k: 975,
427 words_48k: 896,
428 },
429 FrmSizeRow {
430 bitrate_kbps: 448,
431 words_32k: 1344,
432 words_44k: 976,
433 words_48k: 896,
434 },
435 FrmSizeRow {
436 bitrate_kbps: 512,
437 words_32k: 1536,
438 words_44k: 1114,
439 words_48k: 1024,
440 },
441 FrmSizeRow {
442 bitrate_kbps: 512,
443 words_32k: 1536,
444 words_44k: 1115,
445 words_48k: 1024,
446 },
447 FrmSizeRow {
448 bitrate_kbps: 576,
449 words_32k: 1728,
450 words_44k: 1253,
451 words_48k: 1152,
452 },
453 FrmSizeRow {
454 bitrate_kbps: 576,
455 words_32k: 1728,
456 words_44k: 1254,
457 words_48k: 1152,
458 },
459 FrmSizeRow {
460 bitrate_kbps: 640,
461 words_32k: 1920,
462 words_44k: 1393,
463 words_48k: 1280,
464 },
465 FrmSizeRow {
466 bitrate_kbps: 640,
467 words_32k: 1920,
468 words_44k: 1394,
469 words_48k: 1280,
470 },
471];
472
473#[derive(Debug, Clone, Copy, PartialEq, Eq)]
494pub struct Ac3Header {
495 pub crc1: u16,
498 pub sample_rate: Ac3SampleRate,
500 pub frame_size_code: u8,
502 pub bsid: u8,
505 pub bitstream_mode: Ac3BitstreamMode,
507 pub audio_coding_mode: Ac3AudioCodingMode,
509 pub center_mix_level: Option<u8>,
512 pub surround_mix_level: Option<u8>,
515 pub dolby_surround_mode: Option<u8>,
518 pub lfe_on: bool,
521}
522
523impl Ac3Header {
524 pub fn parse(frame: &[u8]) -> Result<Self> {
532 if frame.len() < 7 {
536 return Err(Error::InvalidUdf("AC-3 sync frame truncated (< 7 bytes)"));
537 }
538 let syncword = u16::from_be_bytes([frame[0], frame[1]]);
539 if syncword != AC3_SYNC_WORD {
540 return Err(Error::InvalidUdf(
541 "AC-3 sync frame: sync word is not 0x0B77",
542 ));
543 }
544 let crc1 = u16::from_be_bytes([frame[2], frame[3]]);
545
546 let byte4 = frame[4];
547 let sample_rate = Ac3SampleRate::from_code(byte4 >> 6);
548 let frame_size_code = byte4 & 0b0011_1111;
549
550 let mut bits = BitReader::new(&frame[5..]);
553 let bsid = bits.read(5);
554 let bitstream_mode = Ac3BitstreamMode::from_code(bits.read(3));
555 let audio_coding_mode = Ac3AudioCodingMode::from_code(bits.read(3));
556
557 let center_mix_level = if audio_coding_mode.has_center_mix_level() {
558 Some(bits.read(2))
559 } else {
560 None
561 };
562 let surround_mix_level = if audio_coding_mode.has_surround_mix_level() {
563 Some(bits.read(2))
564 } else {
565 None
566 };
567 let dolby_surround_mode = if audio_coding_mode.has_dolby_surround_mode() {
568 Some(bits.read(2))
569 } else {
570 None
571 };
572 let lfe_on = bits.read(1) != 0;
573
574 Ok(Self {
575 crc1,
576 sample_rate,
577 frame_size_code,
578 bsid,
579 bitstream_mode,
580 audio_coding_mode,
581 center_mix_level,
582 surround_mix_level,
583 dolby_surround_mode,
584 lfe_on,
585 })
586 }
587
588 pub fn sample_rate_hz(self) -> Option<u32> {
591 self.sample_rate.hz()
592 }
593
594 pub fn nominal_bitrate_kbps(self) -> Option<u16> {
597 FRM_SIZE_TABLE
598 .get(self.frame_size_code as usize)
599 .map(|r| r.bitrate_kbps)
600 }
601
602 pub fn frame_size_words(self) -> Option<u16> {
606 let row = FRM_SIZE_TABLE.get(self.frame_size_code as usize)?;
607 match self.sample_rate {
608 Ac3SampleRate::Hz32000 => Some(row.words_32k),
609 Ac3SampleRate::Hz44100 => Some(row.words_44k),
610 Ac3SampleRate::Hz48000 => Some(row.words_48k),
611 Ac3SampleRate::Reserved => None,
612 }
613 }
614
615 pub fn frame_size_bytes(self) -> Option<u32> {
618 self.frame_size_words().map(|w| w as u32 * 2)
619 }
620
621 pub fn total_channel_count(self) -> u8 {
624 self.audio_coding_mode.channel_count() + u8::from(self.lfe_on)
625 }
626}
627
628struct BitReader<'a> {
634 data: &'a [u8],
635 bit_pos: usize,
636}
637
638impl<'a> BitReader<'a> {
639 fn new(data: &'a [u8]) -> Self {
640 Self { data, bit_pos: 0 }
641 }
642
643 fn read(&mut self, n: usize) -> u8 {
645 let mut out = 0u8;
646 for _ in 0..n {
647 let byte_idx = self.bit_pos >> 3;
648 let bit_idx = 7 - (self.bit_pos & 7);
649 let bit = self
650 .data
651 .get(byte_idx)
652 .map(|b| (b >> bit_idx) & 1)
653 .unwrap_or(0);
654 out = (out << 1) | bit;
655 self.bit_pos += 1;
656 }
657 out
658 }
659}
660
661#[cfg(test)]
662mod tests {
663 use super::*;
664
665 fn build_frame(
670 fscod: u8,
671 frmsizecod: u8,
672 bsid: u8,
673 bsmod: u8,
674 acmod: u8,
675 lfe: bool,
676 ) -> Vec<u8> {
677 let mut f = vec![0x0B, 0x77, 0x12, 0x34];
678 f.push((fscod << 6) | (frmsizecod & 0b0011_1111));
679
680 let mut bitbuf: Vec<bool> = Vec::new();
682 let push = |bb: &mut Vec<bool>, val: u8, n: usize| {
683 for i in (0..n).rev() {
684 bb.push((val >> i) & 1 != 0);
685 }
686 };
687 push(&mut bitbuf, bsid, 5);
688 push(&mut bitbuf, bsmod, 3);
689 push(&mut bitbuf, acmod, 3);
690 let acm = Ac3AudioCodingMode::from_code(acmod);
691 if acm.has_center_mix_level() {
692 push(&mut bitbuf, 0b01, 2);
693 }
694 if acm.has_surround_mix_level() {
695 push(&mut bitbuf, 0b10, 2);
696 }
697 if acm.has_dolby_surround_mode() {
698 push(&mut bitbuf, 0b10, 2);
699 }
700 bitbuf.push(lfe);
701 while bitbuf.len() % 8 != 0 {
703 bitbuf.push(false);
704 }
705 for chunk in bitbuf.chunks(8) {
706 let mut b = 0u8;
707 for (i, &bit) in chunk.iter().enumerate() {
708 if bit {
709 b |= 1 << (7 - i);
710 }
711 }
712 f.push(b);
713 }
714 while f.len() < 7 {
716 f.push(0);
717 }
718 f
719 }
720
721 #[test]
722 fn parse_stereo_48k() {
723 let f = build_frame(0, 0, 8, 0, 2, false);
725 let h = Ac3Header::parse(&f).unwrap();
726 assert_eq!(h.crc1, 0x1234);
727 assert_eq!(h.sample_rate, Ac3SampleRate::Hz48000);
728 assert_eq!(h.sample_rate_hz(), Some(48_000));
729 assert_eq!(h.frame_size_code, 0);
730 assert_eq!(h.bsid, 8);
731 assert_eq!(h.bitstream_mode, Ac3BitstreamMode::CompleteMain);
732 assert_eq!(h.audio_coding_mode, Ac3AudioCodingMode::Stereo);
733 assert_eq!(h.center_mix_level, None);
735 assert_eq!(h.surround_mix_level, None);
736 assert_eq!(h.dolby_surround_mode, Some(0b10));
737 assert!(!h.lfe_on);
738 assert_eq!(h.total_channel_count(), 2);
739 assert_eq!(h.nominal_bitrate_kbps(), Some(32));
740 assert_eq!(h.frame_size_words(), Some(64));
741 assert_eq!(h.frame_size_bytes(), Some(128));
742 }
743
744 #[test]
745 fn parse_five_one_48k() {
746 let f = build_frame(0, 24, 8, 0, 7, true);
748 let h = Ac3Header::parse(&f).unwrap();
749 assert_eq!(h.audio_coding_mode, Ac3AudioCodingMode::ThreeTwo);
750 assert_eq!(h.center_mix_level, Some(0b01));
752 assert_eq!(h.surround_mix_level, Some(0b10));
753 assert_eq!(h.dolby_surround_mode, None);
754 assert!(h.lfe_on);
755 assert_eq!(h.audio_coding_mode.channel_count(), 5);
756 assert_eq!(h.total_channel_count(), 6);
757 assert_eq!(h.nominal_bitrate_kbps(), Some(256));
758 assert_eq!(h.frame_size_words(), Some(512));
759 }
760
761 #[test]
762 fn frmsizecod_table_sample_rate_columns() {
763 let at = |fscod: u8| Ac3Header::parse(&build_frame(fscod, 26, 8, 0, 2, false)).unwrap();
765 let h48 = at(0);
766 assert_eq!(h48.sample_rate_hz(), Some(48_000));
767 assert_eq!(h48.frame_size_words(), Some(640));
768 let h44 = at(1);
769 assert_eq!(h44.sample_rate_hz(), Some(44_100));
770 assert_eq!(h44.frame_size_words(), Some(696));
771 let h32 = at(2);
772 assert_eq!(h32.sample_rate_hz(), Some(32_000));
773 assert_eq!(h32.frame_size_words(), Some(960));
774 }
775
776 #[test]
777 fn reserved_fscod_yields_none() {
778 let h = Ac3Header::parse(&build_frame(3, 0, 8, 0, 2, false)).unwrap();
779 assert_eq!(h.sample_rate, Ac3SampleRate::Reserved);
780 assert_eq!(h.sample_rate_hz(), None);
781 assert_eq!(h.frame_size_words(), None);
783 assert_eq!(h.nominal_bitrate_kbps(), Some(32));
785 }
786
787 #[test]
788 fn reserved_frmsizecod_yields_none() {
789 let h = Ac3Header::parse(&build_frame(0, 38, 8, 0, 2, false)).unwrap();
791 assert_eq!(h.frame_size_code, 38);
792 assert_eq!(h.nominal_bitrate_kbps(), None);
793 assert_eq!(h.frame_size_words(), None);
794 assert_eq!(h.frame_size_bytes(), None);
795 }
796
797 #[test]
798 fn all_acmod_channel_counts() {
799 let expected = [2u8, 1, 2, 3, 3, 4, 4, 5];
800 for (code, &n) in expected.iter().enumerate() {
801 assert_eq!(Ac3AudioCodingMode::from_code(code as u8).channel_count(), n);
802 }
803 }
804
805 #[test]
806 fn conditional_field_presence_by_acmod() {
807 for code in [3u8, 5, 7] {
809 assert!(Ac3AudioCodingMode::from_code(code).has_center_mix_level());
810 }
811 for code in [0u8, 1, 2, 4, 6] {
812 assert!(!Ac3AudioCodingMode::from_code(code).has_center_mix_level());
813 }
814 for code in [4u8, 5, 6, 7] {
816 assert!(Ac3AudioCodingMode::from_code(code).has_surround_mix_level());
817 }
818 for code in [0u8, 1, 2, 3] {
819 assert!(!Ac3AudioCodingMode::from_code(code).has_surround_mix_level());
820 }
821 assert!(Ac3AudioCodingMode::from_code(2).has_dolby_surround_mode());
823 for code in [0u8, 1, 3, 4, 5, 6, 7] {
824 assert!(!Ac3AudioCodingMode::from_code(code).has_dolby_surround_mode());
825 }
826 }
827
828 #[test]
829 fn bitstream_mode_table() {
830 let modes = [
831 Ac3BitstreamMode::CompleteMain,
832 Ac3BitstreamMode::MusicAndEffects,
833 Ac3BitstreamMode::VisuallyImpaired,
834 Ac3BitstreamMode::HearingImpaired,
835 Ac3BitstreamMode::Dialogue,
836 Ac3BitstreamMode::Commentary,
837 Ac3BitstreamMode::Emergency,
838 Ac3BitstreamMode::VoiceOverOrKaraoke,
839 ];
840 for (code, &m) in modes.iter().enumerate() {
841 let h = Ac3Header::parse(&build_frame(0, 0, 8, code as u8, 2, false)).unwrap();
842 assert_eq!(h.bitstream_mode, m);
843 }
844 }
845
846 #[test]
847 fn rejects_bad_syncword() {
848 let mut f = build_frame(0, 0, 8, 0, 2, false);
849 f[0] = 0x00;
850 let err = Ac3Header::parse(&f).unwrap_err();
851 assert!(matches!(err, Error::InvalidUdf(_)));
852 }
853
854 #[test]
855 fn rejects_short_buffer() {
856 let err = Ac3Header::parse(&[0x0B, 0x77, 0, 0, 0, 0]).unwrap_err();
857 assert!(matches!(err, Error::InvalidUdf(_)));
858 }
859
860 #[test]
861 fn frmsizecod_table_is_complete() {
862 assert_eq!(FRM_SIZE_TABLE.len(), 38);
864 assert_eq!(FRM_SIZE_TABLE[0].bitrate_kbps, 32);
866 assert_eq!(FRM_SIZE_TABLE[37].bitrate_kbps, 640);
867 }
868}