use crate::bitstream::TsStreamBuffer;
use crate::stream::TsAudioStream;
pub fn scan(stream: &mut TsAudioStream, buffer: &mut TsStreamBuffer, tag: &mut Option<String>) {
let _ = tag;
if stream.base.is_initialized {
return;
}
let Some(header) = buffer.read_bytes(4) else {
return;
};
let b2 = header.get(2).copied().unwrap_or(0);
let b3 = header.get(3).copied().unwrap_or(0);
let flags = u16::from_be_bytes([b2, b3]);
match (flags & 0xF000).wrapping_shr(12) {
1 => {
stream.channel_count = 1;
stream.lfe = 0;
}
3 => {
stream.channel_count = 2;
stream.lfe = 0;
}
4 => {
stream.channel_count = 3;
stream.lfe = 0;
}
5 => {
stream.channel_count = 3;
stream.lfe = 0;
}
6 => {
stream.channel_count = 4;
stream.lfe = 0;
}
7 => {
stream.channel_count = 4;
stream.lfe = 0;
}
8 => {
stream.channel_count = 5;
stream.lfe = 0;
}
9 => {
stream.channel_count = 5;
stream.lfe = 1;
}
10 => {
stream.channel_count = 7;
stream.lfe = 0;
}
11 => {
stream.channel_count = 7;
stream.lfe = 1;
}
_ => {
stream.channel_count = 0;
stream.lfe = 0;
}
}
match (flags & 0xC0).wrapping_shr(6) {
1 => stream.bit_depth = 16,
2 => stream.bit_depth = 20,
3 => stream.bit_depth = 24,
_ => stream.bit_depth = 0,
}
match (flags & 0xF00).wrapping_shr(8) {
1 => stream.sample_rate = 48_000,
4 => stream.sample_rate = 96_000,
5 => stream.sample_rate = 192_000,
_ => stream.sample_rate = 0,
}
let product = stream
.sample_rate
.wrapping_mul(stream.bit_depth)
.wrapping_mul(stream.channel_count.wrapping_add(stream.lfe));
stream.base.bit_rate = i64::from(product.cast_unsigned());
stream.base.is_vbr = false;
stream.base.is_initialized = true;
}
#[cfg(test)]
mod tests {
use proptest::prelude::{any, proptest};
use super::scan;
use crate::bitstream::TsStreamBuffer;
use crate::stream::{TsAudioStream, TsStreamType};
fn buf(data: &[u8]) -> TsStreamBuffer {
let mut b = TsStreamBuffer::new();
b.add(data, 0, data.len());
b.begin_read();
b
}
fn lpcm(channel: u16, rate: u16, depth: u16) -> Vec<u8> {
let flags = channel.wrapping_shl(12) | rate.wrapping_shl(8) | depth.wrapping_shl(6);
let [b2, b3] = flags.to_be_bytes();
vec![0x00, 0x00, b2, b3, 0x00]
}
fn run(channel: u16, rate: u16, depth: u16) -> TsAudioStream {
let mut s = TsAudioStream::default();
s.base.stream_type = TsStreamType::LpcmAudio;
let mut b = buf(&lpcm(channel, rate, depth));
let mut tag = None;
scan(&mut s, &mut b, &mut tag);
assert_eq!(tag, None, "LPCM never sets the tag");
s
}
#[test]
fn channel_assignment_maps_count_and_lfe() {
let cases = [
(0_u16, 0_i32, 0_i32),
(1, 1, 0),
(2, 0, 0),
(3, 2, 0),
(4, 3, 0),
(5, 3, 0),
(6, 4, 0),
(7, 4, 0),
(8, 5, 0),
(9, 5, 1),
(10, 7, 0),
(11, 7, 1),
(12, 0, 0),
(15, 0, 0),
];
for (channel, count, lfe) in cases {
let s = run(channel, 1, 1);
assert_eq!((s.channel_count, s.lfe), (count, lfe), "channel nibble {channel}");
}
}
#[test]
fn bit_depth_code_maps_depth() {
assert_eq!(run(3, 1, 0).bit_depth, 0);
assert_eq!(run(3, 1, 1).bit_depth, 16);
assert_eq!(run(3, 1, 2).bit_depth, 20);
assert_eq!(run(3, 1, 3).bit_depth, 24);
}
#[test]
fn sample_rate_nibble_maps_rate() {
assert_eq!(run(3, 0, 1).sample_rate, 0);
assert_eq!(run(3, 1, 1).sample_rate, 48_000);
assert_eq!(run(3, 2, 1).sample_rate, 0);
assert_eq!(run(3, 4, 1).sample_rate, 96_000);
assert_eq!(run(3, 5, 1).sample_rate, 192_000);
assert_eq!(run(3, 6, 1).sample_rate, 0);
}
#[test]
fn matches_the_expected_lpcm_stream_line() {
let s = run(3, 1, 1);
assert_eq!(s.channel_count, 2);
assert_eq!(s.lfe, 0);
assert_eq!(s.sample_rate, 48_000);
assert_eq!(s.bit_depth, 16);
assert_eq!(s.base.bit_rate, 1_536_000);
assert!(!s.base.is_vbr);
assert!(s.base.is_initialized);
assert_eq!(s.codec_short_name(), "LPCM");
assert_eq!(s.codec_name(), "LPCM Audio");
assert_eq!(s.description(), "2.0 / 48 kHz / 1536 kbps / 16-bit");
}
#[test]
fn bit_rate_folds_lfe_into_the_channel_count() {
let s = run(9, 1, 3);
assert_eq!((s.channel_count, s.lfe, s.sample_rate, s.bit_depth), (5, 1, 48_000, 24));
assert_eq!(s.base.bit_rate, 6_912_000);
assert_eq!(run(9, 0, 3).base.bit_rate, 0);
}
#[test]
fn short_buffer_and_initialized_stream_are_left_untouched() {
let mut s = TsAudioStream::default();
s.base.stream_type = TsStreamType::LpcmAudio;
let mut b = buf(&[0x00, 0x00, 0x31, 0x40]);
scan(&mut s, &mut b, &mut None);
assert!(!s.base.is_initialized);
assert_eq!(s.channel_count, 0);
let mut s = TsAudioStream::default();
s.base.stream_type = TsStreamType::LpcmAudio;
s.base.is_initialized = true;
let mut b = buf(&lpcm(9, 5, 3));
scan(&mut s, &mut b, &mut None);
assert_eq!(s.channel_count, 0); }
proptest! {
#[test]
fn scan_never_panics_on_arbitrary_bytes(data in any::<Vec<u8>>()) {
let mut s = TsAudioStream::default();
s.base.stream_type = TsStreamType::LpcmAudio;
let mut b = buf(&data);
let mut tag = None;
scan(&mut s, &mut b, &mut tag);
}
}
}