use crate::bitstream::TsStreamBuffer;
use crate::stream::{TsAudioMode, TsAudioStream};
const MPA_BITRATE: [[[i32; 16]; 4]; 4] = [
[
[0; 16],
[0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
[0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
[0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],
],
[[0; 16], [0; 16], [0; 16], [0; 16]],
[
[0; 16],
[0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
[0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
[0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],
],
[
[0; 16],
[0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0],
[0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0],
[0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0],
],
];
const MPA_SAMPLE_RATE: [[i32; 4]; 4] = [
[11_025, 12_000, 8_000, 0], [0, 0, 0, 0], [22_050, 24_000, 16_000, 0], [44_100, 48_000, 32_000, 0], ];
const MPA_CHANNEL_MODES: [TsAudioMode; 4] =
[TsAudioMode::Stereo, TsAudioMode::JointStereo, TsAudioMode::DualMono, TsAudioMode::Mono];
const MPA_VERSION: [&str; 4] = ["MPEG 2.5", "Unknown MPEG", "MPEG 2", "MPEG 1"];
const MPA_LAYER: [&str; 4] = ["Unknown Layer", "Layer III", "Layer II", "Layer I"];
const MPA_CHANNELS: [i32; 4] = [2, 2, 2, 1];
pub fn scan(stream: &mut TsAudioStream, buffer: &mut TsStreamBuffer, tag: &mut Option<String>) {
let _ = tag;
if stream.base.is_initialized {
return;
}
let sync_word = u32::from(buffer.read_bits2(11, false)).wrapping_shl(5);
if sync_word != 0b1111_1111_1110_0000 {
return;
}
let audio_version_id = usize::from(buffer.read_bits2(2, false));
let layer_index = usize::from(buffer.read_bits2(2, false));
let _ = buffer.read_bool(false); let bitrate_index = usize::from(buffer.read_bits2(4, false));
let sampling_rate_index = usize::from(buffer.read_bits2(2, false));
let _ = buffer.read_bool(false); let _ = buffer.read_bool(false); let channel_mode = usize::from(buffer.read_bits2(2, false));
let _ = buffer.read_bits2(2, false); let _ = buffer.read_bool(false); let _ = buffer.read_bool(false); let _ = buffer.read_bits2(2, false);
let bitrate_kbps = MPA_BITRATE
.get(audio_version_id)
.and_then(|layers| layers.get(layer_index))
.and_then(|rates| rates.get(bitrate_index))
.copied()
.unwrap_or(0);
stream.base.bit_rate = i64::from(bitrate_kbps.wrapping_mul(1000));
stream.sample_rate = MPA_SAMPLE_RATE
.get(audio_version_id)
.and_then(|rates| rates.get(sampling_rate_index))
.copied()
.unwrap_or(0);
stream.audio_mode =
MPA_CHANNEL_MODES.get(channel_mode).copied().unwrap_or(TsAudioMode::Unknown);
stream.channel_count = MPA_CHANNELS.get(channel_mode).copied().unwrap_or(0);
stream.lfe = 0;
let version = MPA_VERSION.get(audio_version_id).copied().unwrap_or("");
let layer = MPA_LAYER.get(layer_index).copied().unwrap_or("");
stream.ext_data = Some(format!("{version} {layer}"));
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::{TsAudioMode, TsAudioStream, TsStreamType};
fn buf(data: &[u8]) -> TsStreamBuffer {
let mut b = TsStreamBuffer::new();
b.add(data, 0, data.len());
b.begin_read();
b
}
fn pack(fields: &[(u64, u32)]) -> Vec<u8> {
let mut bytes = Vec::new();
let mut cur: u8 = 0;
let mut nbits: u32 = 0;
for &(val, width) in fields {
let mut b = width;
while b > 0 {
b = b.wrapping_sub(1);
let bit = u8::try_from(val.wrapping_shr(b) & 1).unwrap_or(0);
cur = cur.wrapping_shl(1).wrapping_add(bit);
nbits = nbits.wrapping_add(1);
if nbits == 8 {
bytes.push(cur);
cur = 0;
nbits = 0;
}
}
}
if nbits > 0 {
bytes.push(cur.wrapping_shl(8_u32.wrapping_sub(nbits)));
}
bytes
}
fn mpa_frame(version: u64, layer: u64, bitrate: u64, sr: u64, chan: u64) -> Vec<u8> {
pack(&[
(0x7FF, 11), (version, 2), (layer, 2), (0, 1), (bitrate, 4), (sr, 2), (0, 1), (0, 1), (chan, 2), (0, 2), (0, 1), (0, 1), (0, 2), (0, 13), ])
}
fn run(
stream_type: TsStreamType,
version: u64,
layer: u64,
bitrate: u64,
sr: u64,
chan: u64,
) -> TsAudioStream {
let mut s = TsAudioStream::default();
s.base.stream_type = stream_type;
let mut b = buf(&mpa_frame(version, layer, bitrate, sr, chan));
let mut tag = None;
scan(&mut s, &mut b, &mut tag);
assert_eq!(tag, None, "MPA never sets the tag");
s
}
#[test]
fn decodes_mpeg1_layer3_stereo() {
let s = run(TsStreamType::Mpeg1Audio, 3, 1, 9, 1, 0);
assert_eq!(s.base.bit_rate, 128_000);
assert_eq!(s.sample_rate, 48_000);
assert_eq!(s.channel_count, 2);
assert_eq!(s.lfe, 0);
assert_eq!(s.audio_mode, TsAudioMode::Stereo);
assert_eq!(s.ext_data.as_deref(), Some("MPEG 1 Layer III"));
assert!(!s.base.is_vbr);
assert!(s.base.is_initialized);
assert_eq!(s.codec_short_name(), "MP1");
assert_eq!(s.codec_name(), "MPEG 1 Layer III");
assert_eq!(s.description(), "2.0 / 48 kHz / 128 kbps");
}
#[test]
fn version_and_layer_select_rates_and_labels() {
let s = run(TsStreamType::Mpeg2Audio, 0, 3, 1, 0, 3);
assert_eq!(s.base.bit_rate, 32_000);
assert_eq!(s.sample_rate, 11_025);
assert_eq!(s.ext_data.as_deref(), Some("MPEG 2.5 Layer I"));
assert_eq!(s.codec_short_name(), "MP2");
let s = run(TsStreamType::Mpeg2Audio, 2, 2, 14, 2, 0);
assert_eq!(s.base.bit_rate, 160_000);
assert_eq!(s.sample_rate, 16_000);
assert_eq!(s.ext_data.as_deref(), Some("MPEG 2 Layer II"));
let s = run(TsStreamType::Mpeg1Audio, 1, 0, 5, 1, 0);
assert_eq!(s.base.bit_rate, 0);
assert_eq!(s.sample_rate, 0);
assert_eq!(s.ext_data.as_deref(), Some("Unknown MPEG Unknown Layer"));
}
#[test]
fn channel_mode_maps_count_mode_and_description_tags() {
let cases = [
(0_u64, 2_i32, TsAudioMode::Stereo, "2.0 / 48 kHz / 128 kbps"),
(1, 2, TsAudioMode::JointStereo, "2.0 / 48 kHz / 128 kbps / Joint Stereo"),
(2, 2, TsAudioMode::DualMono, "2.0 / 48 kHz / 128 kbps / Dual Mono"),
(3, 1, TsAudioMode::Mono, "1.0 / 48 kHz / 128 kbps"),
];
for (chan, count, mode, desc) in cases {
let s = run(TsStreamType::Mpeg1Audio, 3, 1, 9, 1, chan);
assert_eq!(s.channel_count, count, "channel mode {chan}");
assert_eq!(s.lfe, 0, "channel mode {chan}");
assert_eq!(s.audio_mode, mode, "channel mode {chan}");
assert_eq!(s.description(), desc, "channel mode {chan}");
}
}
#[test]
fn rejects_bad_sync_and_initialized_streams() {
let mut s = TsAudioStream::default();
s.base.stream_type = TsStreamType::Mpeg1Audio;
let mut b = buf(&pack(&[(0x7FE, 11), (0, 21)])); scan(&mut s, &mut b, &mut None);
assert!(!s.base.is_initialized);
assert_eq!(s.ext_data, None);
let mut s = TsAudioStream::default();
s.base.stream_type = TsStreamType::Mpeg1Audio;
s.base.is_initialized = true;
let mut b = buf(&mpa_frame(3, 1, 9, 1, 0));
scan(&mut s, &mut b, &mut None);
assert_eq!(s.channel_count, 0); assert_eq!(s.ext_data, None);
}
proptest! {
#[test]
fn scan_never_panics_on_arbitrary_bytes(data in any::<Vec<u8>>()) {
for ty in [TsStreamType::Mpeg1Audio, TsStreamType::Mpeg2Audio] {
let mut s = TsAudioStream::default();
s.base.stream_type = ty;
let mut b = buf(&data);
let mut tag = None;
scan(&mut s, &mut b, &mut tag);
}
}
}
}