use crate::bitreader::read_bits_checked as read_bits;
use crate::error::{Error, Result};
use alloc::vec::Vec;
use broadcast_common::error::{BufferTooShort, InvalidValue};
use broadcast_common::len::{need_in, need_out};
use broadcast_common::{Parse, Serialize};
pub(crate) const AC3_SYNCWORD: u16 = 0x0B77;
const BYTES_PER_WORD: usize = 2;
pub(crate) const SAMPLES_PER_AUDIO_BLOCK: u32 = 256;
const AC3_BLOCKS_PER_SYNCFRAME: u32 = 6;
pub const AC3_SAMPLES_PER_SYNCFRAME: u32 = AC3_BLOCKS_PER_SYNCFRAME * SAMPLES_PER_AUDIO_BLOCK;
const AC3_MAX_BSID: u8 = 8;
const AC3_CRC_POLY: u16 = 0x8005;
const CRC16_TOP_BIT: u16 = 0x8000;
const AC3_SYNCWORD_BYTES: usize = 2;
fn ac3_crc16(bytes: &[u8]) -> u16 {
let mut crc = 0u16;
for &b in bytes {
crc ^= u16::from(b) << 8;
for _ in 0..8 {
crc = if crc & CRC16_TOP_BIT != 0 {
(crc << 1) ^ AC3_CRC_POLY
} else {
crc << 1
};
}
}
crc
}
pub(crate) fn eac3_frame_crc_ok(frame: &[u8]) -> bool {
frame.len() > AC3_SYNCWORD_BYTES && ac3_crc16(&frame[AC3_SYNCWORD_BYTES..]) == 0
}
pub(crate) const EAC3_STRMTYP_DEPENDENT: u8 = 1;
const EAC3_STRMTYP_INDEPENDENT: u8 = 0;
#[rustfmt::skip]
const AC3_FRAME_SIZE_WORDS: [[u16; 3]; 38] = [
[ 96, 69, 64], [ 96, 70, 64],
[ 120, 87, 80], [ 120, 88, 80],
[ 144, 104, 96], [ 144, 105, 96],
[ 168, 121, 112], [ 168, 122, 112],
[ 192, 139, 128], [ 192, 140, 128],
[ 240, 174, 160], [ 240, 175, 160],
[ 288, 208, 192], [ 288, 209, 192],
[ 336, 243, 224], [ 336, 244, 224],
[ 384, 278, 256], [ 384, 279, 256],
[ 480, 348, 320], [ 480, 349, 320],
[ 576, 417, 384], [ 576, 418, 384],
[ 672, 487, 448], [ 672, 488, 448],
[ 768, 557, 512], [ 768, 558, 512],
[ 960, 696, 640], [ 960, 697, 640],
[1152, 835, 768], [1152, 836, 768],
[1344, 975, 896], [1344, 976, 896],
[1536, 1114, 1024], [1536, 1115, 1024],
[1728, 1253, 1152], [1728, 1254, 1152],
[1920, 1393, 1280], [1920, 1394, 1280],
];
fn ac3_frame_words(fscod: u8, frmsizecod: u8) -> Option<u16> {
let row = AC3_FRAME_SIZE_WORDS.get(frmsizecod as usize)?;
let col = match fscod {
0 => 2, 1 => 1, 2 => 0, _ => return None,
};
Some(row[col])
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ac3SyncframeInfo {
pub fscod: u8,
pub frmsizecod: u8,
pub bsid: u8,
pub bsmod: u8,
pub acmod: u8,
pub lfeon: bool,
pub sample_rate: u32,
}
impl Ac3SyncframeInfo {
pub fn from_es(data: &[u8]) -> Result<Self> {
let off = find_syncword(data)?;
Self::parse_at(data, off)
}
pub(crate) fn parse_at(data: &[u8], off: usize) -> Result<Self> {
let need = off + 5;
need_in(data, need, "AC-3 syncinfo")?;
let es = &data[off..];
let mut bit_pos = 0usize;
bit_pos += 16;
bit_pos += 16;
let fscod = read_bits(es, &mut bit_pos, 2, "fscod")? as u8;
let frmsizecod = read_bits(es, &mut bit_pos, 6, "frmsizecod")? as u8;
let bsid = read_bits(es, &mut bit_pos, 5, "bsid")? as u8;
let bsmod = read_bits(es, &mut bit_pos, 3, "bsmod")? as u8;
let acmod = read_bits(es, &mut bit_pos, 3, "acmod")? as u8;
if (acmod & 0x1) != 0 && acmod != 0x1 {
bit_pos += 2; }
if (acmod & 0x4) != 0 {
bit_pos += 2; }
if acmod == 0x2 {
bit_pos += 2; }
let lfeon = read_bits(es, &mut bit_pos, 1, "lfeon")? != 0;
let sample_rate = ac3_sample_rate(fscod);
Ok(Self {
fscod,
frmsizecod,
bsid,
bsmod,
acmod,
lfeon,
sample_rate,
})
}
pub fn into_dac3(self) -> Ac3SpecificBox {
Ac3SpecificBox {
fscod: self.fscod,
bsid: self.bsid,
bsmod: self.bsmod,
acmod: self.acmod,
lfeon: self.lfeon,
bit_rate_code: self.frmsizecod >> 1,
}
}
pub fn channel_count(&self) -> u8 {
acmod_channels(self.acmod)
}
pub fn frame_len_bytes(&self) -> Option<usize> {
ac3_frame_words(self.fscod, self.frmsizecod).map(|w| w as usize * BYTES_PER_WORD)
}
}
pub fn split_ac3_syncframes(payload: &[u8]) -> Vec<&[u8]> {
split_ac3_syncframes_resyncing(payload, false)
}
pub fn split_ac3_syncframes_resyncing(payload: &[u8], resync: bool) -> Vec<&[u8]> {
split_ac3_syncframe_ranges(payload, resync)
.into_iter()
.map(|(start, end)| &payload[start..end])
.collect()
}
pub fn split_ac3_syncframe_ranges(payload: &[u8], resync: bool) -> Vec<(usize, usize)> {
let mut ranges = Vec::new();
let mut off = 0usize;
while off + 2 <= payload.len() {
if u16::from_be_bytes([payload[off], payload[off + 1]]) != AC3_SYNCWORD {
if !resync {
break;
}
off += 1;
continue;
}
let ok = Ac3SyncframeInfo::parse_at(payload, off)
.ok()
.and_then(|info| info.frame_len_bytes())
.filter(|&len| len != 0 && off + len <= payload.len());
match ok {
Some(len) => {
ranges.push((off, off + len));
off += len;
}
None if resync => off += 1,
None => break,
}
}
ranges
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ec3SyncframeInfo {
pub strmtyp: u8,
pub substreamid: u8,
pub frmsiz: u16,
pub fscod: u8,
pub numblks: u8,
pub acmod: u8,
pub lfeon: bool,
pub bsid: u8,
pub sample_rate: u32,
pub sample_rate_khz: u32,
pub chanmap: Option<u16>,
pub bsmod: u8,
}
impl Ec3SyncframeInfo {
pub fn from_es(data: &[u8]) -> Result<Self> {
let off = find_syncword(data)?;
Self::parse_at(data, off)
}
pub(crate) fn parse_at(data: &[u8], off: usize) -> Result<Self> {
Self::parse_at_with_bsmod_presence(data, off).map(|(info, _)| info)
}
fn parse_at_with_bsmod_presence(data: &[u8], off: usize) -> Result<(Self, bool)> {
let need = off + 6;
need_in(data, need, "E-AC-3 syncframe")?;
if data[off + 5] >> 3 <= AC3_MAX_BSID {
return Self::from_ac3_independent(data, off).map(|info| (info, true));
}
let es = &data[off..];
let mut bit_pos = 0usize;
bit_pos += 16;
let strmtyp = read_bits(es, &mut bit_pos, 2, "strmtyp")? as u8;
let substreamid = read_bits(es, &mut bit_pos, 3, "substreamid")? as u8;
let frmsiz = read_bits(es, &mut bit_pos, 11, "frmsiz")? as u16;
let fscod = read_bits(es, &mut bit_pos, 2, "fscod")? as u8;
let (sample_rate, sample_rate_khz, numblks) = if fscod == 3 {
let sr_code2 = read_bits(es, &mut bit_pos, 2, "sr_code2")? as u8;
let sr = eac3_half_sample_rate(sr_code2);
(sr, eac3_half_sample_rate_khz(sr_code2), 6u8)
} else {
let numblkscod = read_bits(es, &mut bit_pos, 2, "numblkscod")? as u8;
let nb = eac3_num_blocks(numblkscod);
let sr = ac3_sample_rate(fscod);
(sr, ac3_sample_rate_khz(fscod), nb)
};
let acmod = read_bits(es, &mut bit_pos, 3, "acmod")? as u8;
let lfeon = read_bits(es, &mut bit_pos, 1, "lfeon")? != 0;
let bsid = read_bits(es, &mut bit_pos, 5, "bsid")? as u8;
let (chanmap, bsmod) =
parse_eac3_bsi_tail(es, &mut bit_pos, strmtyp, acmod, lfeon, numblks)?;
let bsmod_present = bsmod.is_some();
let bsmod = bsmod.unwrap_or(0);
Ok((
Self {
strmtyp,
substreamid,
frmsiz,
fscod,
numblks,
acmod,
lfeon,
bsid,
sample_rate,
sample_rate_khz,
chanmap,
bsmod,
},
bsmod_present,
))
}
fn from_ac3_independent(data: &[u8], off: usize) -> Result<Self> {
let ac3 = Ac3SyncframeInfo::parse_at(data, off)?;
let words = ac3_frame_words(ac3.fscod, ac3.frmsizecod).ok_or(Error::InvalidValue(
InvalidValue {
field: "AC-3 frmsizecod/fscod",
value: u64::from(ac3.frmsizecod),
reason: "reserved fscod or frmsizecod (Tables 4.1 / 4.13)",
},
))?;
let len = usize::from(words) * BYTES_PER_WORD;
if let Some(frame) = data.get(off..off + len)
&& ac3_crc16(&frame[AC3_SYNCWORD_BYTES..]) != 0
{
return Err(Error::InvalidValue(InvalidValue {
field: "AC-3 crc2",
value: u64::from(ac3_crc16(&frame[AC3_SYNCWORD_BYTES..])),
reason: "syncframe CRC does not check out (false sync or damaged frame)",
}));
}
Ok(Self {
strmtyp: EAC3_STRMTYP_INDEPENDENT,
substreamid: 0,
frmsiz: words - 1,
fscod: ac3.fscod,
numblks: AC3_BLOCKS_PER_SYNCFRAME as u8,
acmod: ac3.acmod,
lfeon: ac3.lfeon,
bsid: ac3.bsid,
sample_rate: ac3.sample_rate,
sample_rate_khz: ac3_sample_rate_khz(ac3.fscod),
chanmap: None,
bsmod: ac3.bsmod,
})
}
pub fn from_es_first_au(data: &[u8]) -> Vec<Self> {
Self::first_au(data, false).0
}
pub fn from_es_first_complete_au(data: &[u8]) -> Option<Vec<Self>> {
let (frames, end) = Self::first_au(data, false);
(end == AuEnd::NextAu).then_some(frames)
}
pub fn from_es_first_au_or_broken_chain(data: &[u8]) -> Option<Vec<Self>> {
let (frames, end) = Self::first_au(data, false);
(end != AuEnd::Open && !frames.is_empty()).then_some(frames)
}
pub fn from_es_first_au_crc_verified(
data: &[u8],
accept_broken_chain: bool,
) -> Option<Vec<Self>> {
let (frames, end) = Self::first_au(data, true);
let ok = match end {
AuEnd::NextAu => true,
AuEnd::BrokenChain => accept_broken_chain,
AuEnd::Open => false,
};
(ok && !frames.is_empty()).then_some(frames)
}
pub fn first_crc_verified_bsmod(data: &[u8], substreamid: u8) -> Option<u8> {
let mut off = 0usize;
while off + AC3_SYNCWORD_BYTES <= data.len() {
if u16::from_be_bytes([data[off], data[off + 1]]) == AC3_SYNCWORD
&& let Ok((info, true)) = Self::parse_at_with_bsmod_presence(data, off)
&& info.strmtyp != EAC3_STRMTYP_DEPENDENT
&& info.substreamid == substreamid
&& let Some(frame) =
data.get(off..off + (info.frmsiz as usize + 1) * BYTES_PER_WORD)
&& eac3_frame_crc_ok(frame)
{
return Some(info.bsmod);
}
off += 1;
}
None
}
fn first_au(data: &[u8], crc: bool) -> (Vec<Self>, AuEnd) {
let (frames, broken) = Self::walk(data, crc);
let mut end = if broken {
AuEnd::BrokenChain
} else {
AuEnd::Open
};
let mut seen_independent: Vec<u8> = Vec::new();
let mut out = Vec::new();
for f in frames {
if f.strmtyp != EAC3_STRMTYP_DEPENDENT {
if seen_independent.contains(&f.substreamid) {
end = AuEnd::NextAu;
break;
}
seen_independent.push(f.substreamid);
}
out.push(f);
}
(out, end)
}
pub fn from_es_all(data: &[u8]) -> Vec<Self> {
Self::walk(data, false).0
}
fn walk(data: &[u8], crc: bool) -> (Vec<Self>, bool) {
let mut out = Vec::new();
let mut off = 0usize;
while off + 2 <= data.len() {
if u16::from_be_bytes([data[off], data[off + 1]]) != AC3_SYNCWORD {
return (out, true);
}
let info = match Self::parse_at(data, off) {
Ok(info) => info,
Err(Error::BufferTooShort(..)) => return (out, false),
Err(_) => return (out, true),
};
let len = (info.frmsiz as usize + 1) * BYTES_PER_WORD;
if len == 0 {
return (out, true);
}
if off + len > data.len() {
return (out, false);
}
if crc && !eac3_frame_crc_ok(&data[off..off + len]) {
return (out, true);
}
out.push(info);
off += len;
}
(out, false)
}
pub fn into_dec3(self) -> Result<Ec3SpecificBox> {
Ec3SpecificBox::from_syncframes(&[self])
}
pub fn channel_count(&self) -> u8 {
acmod_channels(self.acmod)
}
pub fn samples_per_frame(&self) -> u32 {
self.numblks as u32 * SAMPLES_PER_AUDIO_BLOCK
}
}
#[derive(Debug, Clone)]
pub struct Ec3SplitFrame {
pub data: Vec<u8>,
pub info: Ec3SyncframeInfo,
}
pub fn split_eac3_syncframes(payload: &[u8]) -> Vec<Ec3SplitFrame> {
let mut out: Vec<Ec3SplitFrame> = Vec::new();
let mut off = 0usize;
while off + 2 <= payload.len() {
if u16::from_be_bytes([payload[off], payload[off + 1]]) != AC3_SYNCWORD {
break;
}
let Ok(info) = Ec3SyncframeInfo::parse_at(payload, off) else {
break;
};
let len = (info.frmsiz as usize + 1) * BYTES_PER_WORD;
if len == 0 || off + len > payload.len() {
break;
}
let frame_bytes = &payload[off..off + len];
if info.strmtyp == EAC3_STRMTYP_DEPENDENT {
if let Some(last) = out.last_mut() {
last.data.extend_from_slice(frame_bytes);
}
off += len;
continue;
}
out.push(Ec3SplitFrame {
data: frame_bytes.to_vec(),
info,
});
off += len;
}
out
}
pub fn split_eac3_syncframe_ranges(payload: &[u8]) -> Vec<(usize, usize)> {
let mut ranges = Vec::new();
let mut off = 0usize;
while off + 2 <= payload.len() {
if u16::from_be_bytes([payload[off], payload[off + 1]]) != AC3_SYNCWORD {
break;
}
let Ok(info) = Ec3SyncframeInfo::parse_at(payload, off) else {
break;
};
let len = (info.frmsiz as usize + 1) * BYTES_PER_WORD;
if len == 0 || off + len > payload.len() {
break;
}
ranges.push((off, off + len));
off += len;
}
ranges
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Ac3SpecificBox {
pub fscod: u8,
pub bsid: u8,
pub bsmod: u8,
pub acmod: u8,
pub lfeon: bool,
pub bit_rate_code: u8,
}
impl Ac3SpecificBox {
pub fn rfc6381(&self) -> &'static str {
"ac-3"
}
pub fn channel_count(&self) -> u8 {
acmod_channels(self.acmod)
}
pub fn channels_with_lfe(&self) -> u8 {
self.channel_count() + u8::from(self.lfeon)
}
pub fn nominal_bitrate_kbps(&self) -> Option<u32> {
AC3_BIT_RATE_KBPS
.get(self.bit_rate_code as usize)
.copied()
.map(u32::from)
}
}
const AC3_BIT_RATE_KBPS: [u16; 19] = [
32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640,
];
impl<'a> Parse<'a> for Ac3SpecificBox {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
need_in(bytes, 3, "dac3 body")?;
let mut bit_pos = 0usize;
let fscod = read_bits(bytes, &mut bit_pos, 2, "fscod")? as u8;
let bsid = read_bits(bytes, &mut bit_pos, 5, "bsid")? as u8;
let bsmod = read_bits(bytes, &mut bit_pos, 3, "bsmod")? as u8;
let acmod = read_bits(bytes, &mut bit_pos, 3, "acmod")? as u8;
let lfeon = read_bits(bytes, &mut bit_pos, 1, "lfeon")? != 0;
let bit_rate_code = read_bits(bytes, &mut bit_pos, 5, "bit_rate_code")? as u8;
let _reserved = read_bits(bytes, &mut bit_pos, 5, "reserved")?;
Ok(Self {
fscod,
bsid,
bsmod,
acmod,
lfeon,
bit_rate_code,
})
}
}
impl Serialize for Ac3SpecificBox {
type Error = Error;
fn serialized_len(&self) -> usize {
3
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
need_out(buf, 3)?;
let mut bit_pos = 0usize;
write_bits(buf, &mut bit_pos, 2, self.fscod as u64);
write_bits(buf, &mut bit_pos, 5, self.bsid as u64);
write_bits(buf, &mut bit_pos, 3, self.bsmod as u64);
write_bits(buf, &mut bit_pos, 3, self.acmod as u64);
write_bits(buf, &mut bit_pos, 1, self.lfeon as u64);
write_bits(buf, &mut bit_pos, 5, self.bit_rate_code as u64);
write_bits(buf, &mut bit_pos, 5, 0); Ok(3)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Ec3Substream {
pub fscod: u8,
pub bsid: u8,
pub asvc: bool,
pub bsmod: u8,
pub acmod: u8,
pub lfeon: bool,
pub num_dep_sub: u8,
pub chan_loc: Option<u16>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Ec3SpecificBox {
pub data_rate: u16,
pub num_ind_sub: u8,
pub substreams: Vec<Ec3Substream>,
pub reserved_tail: Vec<u8>,
}
impl Ec3SpecificBox {
pub fn rfc6381(&self) -> &'static str {
"ec-3"
}
pub fn channel_count(&self) -> u8 {
self.substreams
.first()
.map(|s| acmod_channels(s.acmod))
.unwrap_or(0)
}
pub fn channels_with_lfe(&self) -> u8 {
let Some(s) = self.substreams.first() else {
return 0;
};
let extra: u8 = s.chan_loc.map_or(0, |loc| {
EAC3_CHAN_LOC_CHANNELS
.iter()
.enumerate()
.filter(|(bit, _)| loc & (1 << bit) != 0)
.map(|(_, n)| *n)
.sum()
});
acmod_channels(s.acmod) + u8::from(s.lfeon) + extra
}
pub fn nominal_bitrate_kbps(&self) -> u32 {
u32::from(self.data_rate)
}
pub fn from_syncframes(frames: &[Ec3SyncframeInfo]) -> Result<Self> {
let mut data_rate: u32 = 0;
let mut substreams: Vec<Ec3Substream> = Vec::new();
for f in frames {
let num = (f.frmsiz as u32 + 1) * f.sample_rate;
let den = (f.numblks as u32) * 16 * 1000;
if den > 0 {
data_rate = data_rate.saturating_add(num.div_ceil(den));
}
if f.strmtyp == EAC3_STRMTYP_DEPENDENT {
if let Some(prev) = substreams.last_mut() {
prev.num_dep_sub = prev.num_dep_sub.max(f.substreamid + 1);
prev.chan_loc = Some(match prev.chan_loc {
Some(loc) => loc | chan_loc_for_frame(f),
None => chan_loc_for_frame(f),
});
}
continue;
}
substreams.push(Ec3Substream {
fscod: f.fscod,
bsid: f.bsid,
asvc: false,
bsmod: f.bsmod,
acmod: f.acmod,
lfeon: f.lfeon,
num_dep_sub: 0,
chan_loc: None,
});
}
let Some(last_ind) = frames
.iter()
.filter(|f| f.strmtyp != EAC3_STRMTYP_DEPENDENT)
.map(|f| f.substreamid)
.next_back()
else {
return Err(Error::InvalidValue(InvalidValue {
field: "dec3 num_ind_sub",
value: frames.len() as u64,
reason: "an E-AC-3 access unit must contain an independent frame",
}));
};
let num_ind_sub = broadcast_common::len::fit_bits(last_ind as u64, 3, "num_ind_sub")? as u8;
if substreams.len() != usize::from(num_ind_sub) + 1 {
return Err(Error::InvalidValue(InvalidValue {
field: "dec3 num_ind_sub",
value: u64::from(num_ind_sub),
reason: "independent substreams must be numbered contiguously from 0",
}));
}
let data_rate =
broadcast_common::len::fit_bits(data_rate as u64, 13, "dec3 data_rate")? as u16;
Ok(Self {
data_rate,
num_ind_sub,
substreams,
reserved_tail: Vec::new(),
})
}
}
fn chan_loc_for_frame(f: &Ec3SyncframeInfo) -> u16 {
let Some(chanmap) = f.chanmap else {
return 0;
};
let mut loc: u16 = 0;
for (chanmap_bit, loc_bit) in EAC3_CHANMAP_TO_CHAN_LOC {
if chanmap & (1u16 << (EAC3_CHANMAP_BITS - 1 - chanmap_bit)) != 0 {
loc |= 1 << loc_bit;
}
}
loc
}
const EAC3_CHANMAP_BITS: u8 = 16;
const EAC3_CHANMAP_TO_CHAN_LOC: [(u8, u8); 9] = [
(5, 0), (6, 1), (7, 2), (8, 3), (9, 4), (10, 5), (11, 6), (12, 7), (14, 8), ];
const EAC3_CHAN_LOC_CHANNELS: [u8; 9] = [
2, 2, 1, 1, 2, 2, 2, 1, 1, ];
fn ec3_substream_serialized_len(sub: &Ec3Substream) -> usize {
if sub.num_dep_sub > 0 { 4 } else { 3 }
}
impl<'a> Parse<'a> for Ec3SpecificBox {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
need_in(bytes, 2, "dec3 body")?;
let mut bit_pos = 0usize;
let data_rate = read_bits(bytes, &mut bit_pos, 13, "data_rate")? as u16;
let num_ind_sub = read_bits(bytes, &mut bit_pos, 3, "num_ind_sub")? as u8;
let num_sub = num_ind_sub as usize + 1;
let need_bytes = 2 + num_sub
* ec3_substream_serialized_len(&Ec3Substream {
fscod: 0,
bsid: 0,
asvc: false,
bsmod: 0,
acmod: 0,
lfeon: false,
num_dep_sub: 0,
chan_loc: None,
});
need_in(bytes, need_bytes, "dec3 substreams")?;
let mut substreams = Vec::with_capacity(num_sub);
for _ in 0..num_sub {
let fscod = read_bits(bytes, &mut bit_pos, 2, "fscod")? as u8;
let bsid = read_bits(bytes, &mut bit_pos, 5, "bsid")? as u8;
let _res1 = read_bits(bytes, &mut bit_pos, 1, "reserved")?;
let asvc = read_bits(bytes, &mut bit_pos, 1, "asvc")? != 0;
let bsmod = read_bits(bytes, &mut bit_pos, 3, "bsmod")? as u8;
let acmod = read_bits(bytes, &mut bit_pos, 3, "acmod")? as u8;
let lfeon = read_bits(bytes, &mut bit_pos, 1, "lfeon")? != 0;
let _res3 = read_bits(bytes, &mut bit_pos, 3, "reserved")?;
let num_dep_sub = read_bits(bytes, &mut bit_pos, 4, "num_dep_sub")? as u8;
let chan_loc = if num_dep_sub > 0 {
Some(read_bits(bytes, &mut bit_pos, 9, "chan_loc")? as u16)
} else {
let _res1 = read_bits(bytes, &mut bit_pos, 1, "reserved")?;
None
};
substreams.push(Ec3Substream {
fscod,
bsid,
asvc,
bsmod,
acmod,
lfeon,
num_dep_sub,
chan_loc,
});
}
let tail_start = (bit_pos.div_ceil(8)).min(bytes.len());
let reserved_tail = bytes[tail_start..].to_vec();
Ok(Self {
data_rate,
num_ind_sub,
substreams,
reserved_tail,
})
}
}
impl Serialize for Ec3SpecificBox {
type Error = Error;
fn serialized_len(&self) -> usize {
2 + self
.substreams
.iter()
.map(ec3_substream_serialized_len)
.sum::<usize>()
+ self.reserved_tail.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let expected = self.num_ind_sub as usize + 1;
if self.substreams.len() != expected {
return Err(Error::InvalidValue(InvalidValue {
field: "dec3 num_ind_sub",
value: self.num_ind_sub as u64,
reason: "substreams.len() must equal num_ind_sub + 1",
}));
}
let need = self.serialized_len();
need_out(buf, need)?;
let mut bit_pos = 0usize;
write_bits(buf, &mut bit_pos, 13, self.data_rate as u64);
write_bits(buf, &mut bit_pos, 3, self.num_ind_sub as u64);
for sub in &self.substreams {
write_bits(buf, &mut bit_pos, 2, sub.fscod as u64);
write_bits(buf, &mut bit_pos, 5, sub.bsid as u64);
write_bits(buf, &mut bit_pos, 1, 0); write_bits(buf, &mut bit_pos, 1, sub.asvc as u64);
write_bits(buf, &mut bit_pos, 3, sub.bsmod as u64);
write_bits(buf, &mut bit_pos, 3, sub.acmod as u64);
write_bits(buf, &mut bit_pos, 1, sub.lfeon as u64);
write_bits(buf, &mut bit_pos, 3, 0); write_bits(buf, &mut bit_pos, 4, sub.num_dep_sub as u64);
if sub.num_dep_sub > 0 {
write_bits(buf, &mut bit_pos, 9, sub.chan_loc.unwrap_or(0) as u64);
} else {
write_bits(buf, &mut bit_pos, 1, 0); }
}
let tail_start = bit_pos.div_ceil(8);
buf[tail_start..tail_start + self.reserved_tail.len()].copy_from_slice(&self.reserved_tail);
Ok(need)
}
}
fn parse_eac3_bsi_tail(
es: &[u8],
bit_pos: &mut usize,
strmtyp: u8,
acmod: u8,
lfeon: bool,
numblks: u8,
) -> Result<(Option<u16>, Option<u8>)> {
let dependent = strmtyp == EAC3_STRMTYP_DEPENDENT;
let _dialnorm = read_bits(es, bit_pos, 5, "dialnorm")?;
let compre = read_bits(es, bit_pos, 1, "compre")? != 0;
if compre {
let _compr = read_bits(es, bit_pos, 8, "compr")?;
}
if acmod == 0x0 {
let _dialnorm2 = read_bits(es, bit_pos, 5, "dialnorm2")?;
let compr2e = read_bits(es, bit_pos, 1, "compr2e")? != 0;
if compr2e {
let _compr2 = read_bits(es, bit_pos, 8, "compr2")?;
}
}
let mut chanmap = None;
if dependent {
let chanmape = read_bits(es, bit_pos, 1, "chanmape")? != 0;
if chanmape {
chanmap = Some(read_bits(es, bit_pos, 16, "chanmap")? as u16);
}
}
let mixmdate = read_bits(es, bit_pos, 1, "mixmdate")? != 0;
if mixmdate {
if acmod > 0x2 {
let _dmixmod = read_bits(es, bit_pos, 2, "dmixmod")?;
}
if (acmod & 0x1) != 0 && acmod > 0x2 {
let _ltrtcmixlev = read_bits(es, bit_pos, 3, "ltrtcmixlev")?;
let _lorocmixlev = read_bits(es, bit_pos, 3, "lorocmixlev")?;
}
if (acmod & 0x4) != 0 {
let _ltrtsurmixlev = read_bits(es, bit_pos, 3, "ltrtsurmixlev")?;
let _lorosurmixlev = read_bits(es, bit_pos, 3, "lorosurmixlev")?;
}
if lfeon {
let lfemixlevcode = read_bits(es, bit_pos, 1, "lfemixlevcode")? != 0;
if lfemixlevcode {
let _lfemixlevcod = read_bits(es, bit_pos, 5, "lfemixlevcod")?;
}
}
if strmtyp == EAC3_STRMTYP_INDEPENDENT {
let pgmscle = read_bits(es, bit_pos, 1, "pgmscle")? != 0;
if pgmscle {
let _pgmscl = read_bits(es, bit_pos, 6, "pgmscl")?;
}
if acmod == 0x0 {
let pgmscl2e = read_bits(es, bit_pos, 1, "pgmscl2e")? != 0;
if pgmscl2e {
let _pgmscl2 = read_bits(es, bit_pos, 6, "pgmscl2")?;
}
}
let extpgmscle = read_bits(es, bit_pos, 1, "extpgmscle")? != 0;
if extpgmscle {
let _extpgmscl = read_bits(es, bit_pos, 6, "extpgmscl")?;
}
let mixdef = read_bits(es, bit_pos, 2, "mixdef")? as u8;
match mixdef {
0x1 => {
let _premixcmpsel = read_bits(es, bit_pos, 1, "premixcmpsel")?;
let _drcsrc = read_bits(es, bit_pos, 1, "drcsrc")?;
let _premixcmpscl = read_bits(es, bit_pos, 3, "premixcmpscl")?;
}
0x2 => {
let _mixdata = read_bits(es, bit_pos, 12, "mixdata")?;
}
0x3 => {
let mixdeflen = read_bits(es, bit_pos, 5, "mixdeflen")? as usize;
let mixdata2e = read_bits(es, bit_pos, 1, "mixdata2e")? != 0;
if mixdata2e {
skip_eac3_mixdata2(es, bit_pos)?;
}
let mixdata3e = read_bits(es, bit_pos, 1, "mixdata3e")? != 0;
if mixdata3e {
let _spchdat = read_bits(es, bit_pos, 5, "spchdat")?;
let addspchdate = read_bits(es, bit_pos, 1, "addspchdate")? != 0;
if addspchdate {
let _spchdat1 = read_bits(es, bit_pos, 5, "spchdat1")?;
let _spchan1att = read_bits(es, bit_pos, 2, "spchan1att")?;
let addspchdat1e = read_bits(es, bit_pos, 1, "addspchdat1e")? != 0;
if addspchdat1e {
let _spchdat2 = read_bits(es, bit_pos, 5, "spchdat2")?;
let _spchan2att = read_bits(es, bit_pos, 3, "spchan2att")?;
}
}
}
skip_eac3_bits(es, bit_pos, 8 * (mixdeflen + 2))?;
if !bit_pos.is_multiple_of(8) {
*bit_pos += (8 - (*bit_pos).rem_euclid(8)) % 8;
}
}
_ => {}
}
if acmod < 0x2 {
let paninfoe = read_bits(es, bit_pos, 1, "paninfoe")? != 0;
if paninfoe {
let _panmean = read_bits(es, bit_pos, 8, "panmean")?;
let _paninfo = read_bits(es, bit_pos, 6, "paninfo")?;
}
if acmod == 0x0 {
let paninfo2e = read_bits(es, bit_pos, 1, "paninfo2e")? != 0;
if paninfo2e {
let _panmean2 = read_bits(es, bit_pos, 8, "panmean2")?;
let _paninfo2 = read_bits(es, bit_pos, 6, "paninfo2")?;
}
}
}
let frmmixcfginfoe = read_bits(es, bit_pos, 1, "frmmixcfginfoe")? != 0;
if frmmixcfginfoe {
if numblks == 1 {
let _blkmixcfginfo = read_bits(es, bit_pos, 5, "blkmixcfginfo")?;
} else {
for _ in 0..numblks {
let blkmixcfginfoe = read_bits(es, bit_pos, 1, "blkmixcfginfoe")? != 0;
if blkmixcfginfoe {
let _blkmixcfginfo = read_bits(es, bit_pos, 5, "blkmixcfginfo")?;
}
}
}
}
}
}
let infomdate = read_bits(es, bit_pos, 1, "infomdate")? != 0;
let bsmod = if infomdate {
Some(read_bits(es, bit_pos, 3, "bsmod")? as u8)
} else {
None
};
Ok((chanmap, bsmod))
}
fn skip_eac3_mixdata2(es: &[u8], bit_pos: &mut usize) -> Result<()> {
let _premixcmpsel = read_bits(es, bit_pos, 1, "premixcmpsel")?;
let _drcsrc = read_bits(es, bit_pos, 1, "drcsrc")?;
let _premixcmpscl = read_bits(es, bit_pos, 3, "premixcmpscl")?;
for what in [
"extpgmlscle",
"extpgmcscle",
"extpgmrscle",
"extpgmlsscle",
"extpgmrsscle",
"extpgmlfescle",
"dmixscle",
] {
let present = read_bits(es, bit_pos, 1, what)? != 0;
if present {
let _value = read_bits(es, bit_pos, 4, what)?;
}
}
let addche = read_bits(es, bit_pos, 1, "addche")? != 0;
if addche {
for what in ["extpgmaux1scle", "extpgmaux2scle"] {
let present = read_bits(es, bit_pos, 1, what)? != 0;
if present {
let _value = read_bits(es, bit_pos, 4, what)?;
}
}
}
Ok(())
}
fn skip_eac3_bits(es: &[u8], bit_pos: &mut usize, n: usize) -> Result<()> {
let end = bit_pos.saturating_add(n);
if end > es.len() * 8 {
return Err(Error::BufferTooShort(BufferTooShort {
need: end.div_ceil(8),
have: es.len(),
what: "E-AC-3 bsi",
}));
}
*bit_pos = end;
Ok(())
}
fn find_syncword(data: &[u8]) -> Result<usize> {
for i in 0..data.len().saturating_sub(1) {
let word = u16::from_be_bytes([data[i], data[i + 1]]);
if word == AC3_SYNCWORD {
return Ok(i);
}
}
Err(Error::InvalidInput(
"AC-3/E-AC-3 syncword (0x0B77) not found in elementary stream",
))
}
fn write_bits(buf: &mut [u8], bit_pos: &mut usize, n: usize, val: u64) {
for i in (0..n).rev() {
let byte_idx = *bit_pos / 8;
let bit_in_byte = 7 - (*bit_pos % 8);
let bit = ((val >> i) & 1) as u8;
buf[byte_idx] = (buf[byte_idx] & !(1 << bit_in_byte)) | (bit << bit_in_byte);
*bit_pos += 1;
}
}
fn ac3_sample_rate(fscod: u8) -> u32 {
match fscod {
0 => 48000,
1 => 44100,
2 => 32000,
_ => 44100, }
}
fn ac3_sample_rate_khz(fscod: u8) -> u32 {
match fscod {
0 => 48,
1 => 44,
2 => 32,
_ => 44,
}
}
fn eac3_half_sample_rate(sr_code2: u8) -> u32 {
match sr_code2 {
0 => 24000,
1 => 22050,
2 => 16000,
_ => 22050,
}
}
fn eac3_half_sample_rate_khz(sr_code2: u8) -> u32 {
match sr_code2 {
0 => 24,
1 => 22,
2 => 16,
_ => 22,
}
}
fn eac3_num_blocks(numblkscod: u8) -> u8 {
match numblkscod {
0 => 1,
1 => 2,
2 => 3,
_ => 6,
}
}
fn acmod_channels(acmod: u8) -> u8 {
match acmod {
0 => 2, 1 => 1, 2 => 2, 3 => 3, 4 => 3, 5 => 4, 6 => 4, _ => 5, }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AuEnd {
NextAu,
BrokenChain,
Open,
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use alloc::vec;
#[test]
fn ec3_channels_with_lfe_counts_chan_loc_dependents() {
let mut b = Ec3SpecificBox {
data_rate: 0,
num_ind_sub: 0,
substreams: vec![Ec3Substream {
fscod: 0,
bsid: 16,
asvc: false,
bsmod: 0,
acmod: 7,
lfeon: true,
num_dep_sub: 1,
chan_loc: Some(1 << 1),
}],
reserved_tail: vec![],
};
assert_eq!(b.channel_count(), 5);
assert_eq!(b.channels_with_lfe(), 8);
b.substreams[0].chan_loc = Some((1 << 1) | (1 << 8));
assert_eq!(b.channels_with_lfe(), 9);
b.substreams[0].chan_loc = None;
assert_eq!(b.channels_with_lfe(), 6);
}
const DAC3_ORACLE: [u8; 3] = [0x50, 0x09, 0x40];
const DEC3_ORACLE: [u8; 5] = [0x06, 0x00, 0x60, 0x02, 0x00];
#[test]
fn dac3_round_trip() {
let box1 = Ac3SpecificBox::parse(&DAC3_ORACLE).unwrap();
assert_eq!(box1.fscod, 1);
assert_eq!(box1.bsid, 8);
assert_eq!(box1.bsmod, 0);
assert_eq!(box1.acmod, 1);
assert!(!box1.lfeon);
assert_eq!(box1.bit_rate_code, 10);
let mut buf = [0u8; 3];
let n = box1.serialize_into(&mut buf).unwrap();
assert_eq!(n, 3);
assert_eq!(&buf[..], &DAC3_ORACLE[..], "dac3 round-trip mismatch");
}
#[test]
fn dac3_mutate_acmod_changes_bytes() {
let mut box1 = Ac3SpecificBox::parse(&DAC3_ORACLE).unwrap();
box1.acmod = 2; let mut buf1 = [0u8; 3];
box1.serialize_into(&mut buf1).unwrap();
assert_ne!(
&buf1[..],
&DAC3_ORACLE[..],
"mutating acmod did not change bytes"
);
}
#[test]
fn dec3_round_trip() {
let box1 = Ec3SpecificBox::parse(&DEC3_ORACLE).unwrap();
assert_eq!(box1.data_rate, 192);
assert_eq!(box1.num_ind_sub, 0);
assert_eq!(box1.substreams.len(), 1);
let s0 = &box1.substreams[0];
assert_eq!(s0.fscod, 1);
assert_eq!(s0.bsid, 16);
assert!(!s0.asvc);
assert_eq!(s0.bsmod, 0);
assert_eq!(s0.acmod, 1);
assert!(!s0.lfeon);
assert_eq!(s0.num_dep_sub, 0);
assert!(s0.chan_loc.is_none());
let mut buf = vec![0u8; box1.serialized_len()];
let n = box1.serialize_into(&mut buf).unwrap();
assert_eq!(n, DEC3_ORACLE.len());
assert_eq!(&buf[..], &DEC3_ORACLE[..], "dec3 round-trip mismatch");
}
const DEC3_DEP_SUB_ORACLE: [u8; 6] = [0x06, 0x00, 0x60, 0x02, 0x02, 0x05];
#[test]
fn dec3_dependent_substream_round_trip() {
let box1 = Ec3SpecificBox::parse(&DEC3_DEP_SUB_ORACLE).unwrap();
assert_eq!(box1.data_rate, 192);
assert_eq!(box1.num_ind_sub, 0);
assert_eq!(box1.substreams.len(), 1);
let s0 = &box1.substreams[0];
assert_eq!(s0.fscod, 1);
assert_eq!(s0.bsid, 16);
assert!(!s0.asvc);
assert_eq!(s0.bsmod, 0);
assert_eq!(s0.acmod, 1);
assert!(!s0.lfeon);
assert_eq!(s0.num_dep_sub, 1, "one dependent substream");
assert_eq!(s0.chan_loc, Some(0b0_0000_0101), "9-bit chan_loc");
assert_eq!(
box1.serialized_len(),
DEC3_DEP_SUB_ORACLE.len(),
"serialized_len must count the 4th chan_loc byte for a dependent substream"
);
let mut buf = vec![0u8; box1.serialized_len()];
let n = box1.serialize_into(&mut buf).unwrap();
assert_eq!(n, DEC3_DEP_SUB_ORACLE.len());
assert_eq!(
&buf[..],
&DEC3_DEP_SUB_ORACLE[..],
"dec3 dependent-substream round-trip mismatch"
);
}
#[test]
fn dec3_mutate_acmod_changes_bytes() {
let mut box1 = Ec3SpecificBox::parse(&DEC3_ORACLE).unwrap();
let orig = {
let mut b = vec![0u8; box1.serialized_len()];
box1.serialize_into(&mut b).unwrap();
b
};
box1.substreams[0].acmod = 2;
let mut b2 = vec![0u8; box1.serialized_len()];
box1.serialize_into(&mut b2).unwrap();
assert_ne!(&b2[..], &orig[..], "mutating acmod did not change bytes");
}
#[test]
fn rfc6381() {
let dac3 = Ac3SpecificBox::parse(&DAC3_ORACLE).unwrap();
assert_eq!(dac3.rfc6381(), "ac-3");
let dec3 = Ec3SpecificBox::parse(&DEC3_ORACLE).unwrap();
assert_eq!(dec3.rfc6381(), "ec-3");
}
#[derive(Default)]
struct BitBuf {
bytes: Vec<u8>,
bit: usize,
}
impl BitBuf {
fn put(&mut self, n: usize, val: u64) -> &mut Self {
for i in (0..n).rev() {
let b = (val >> i) & 1;
if self.bit.is_multiple_of(8) {
self.bytes.push(0);
}
if b == 1 {
let idx = self.bytes.len() - 1;
self.bytes[idx] |= 1 << (7 - (self.bit % 8));
}
self.bit += 1;
}
self
}
fn align(&mut self) -> &mut Self {
while !self.bit.is_multiple_of(8) {
self.put(1, 0);
}
self
}
fn take(&mut self) -> Vec<u8> {
self.bit = 0;
core::mem::take(&mut self.bytes)
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn eac3_bsi(
strmtyp: u8,
substreamid: u8,
frmsiz: u16,
acmod: u8,
lfeon: bool,
fscod: u8,
numblkscod: u8,
chanmape: Option<u16>,
bsmod: Option<u8>,
) -> Vec<u8> {
let mut w = BitBuf::default();
w.put(16, AC3_SYNCWORD as u64);
w.put(2, strmtyp as u64);
w.put(3, substreamid as u64);
w.put(11, frmsiz as u64);
w.put(2, fscod as u64);
w.put(2, numblkscod as u64);
w.put(3, acmod as u64);
w.put(1, lfeon as u64);
w.put(5, 16); w.put(5, 31); w.put(1, 0); if acmod == 0 {
w.put(5, 31); w.put(1, 0); }
if strmtyp == 1 {
w.put(1, u64::from(chanmape.is_some()));
if let Some(map) = chanmape {
w.put(16, map as u64);
}
}
w.put(1, 0); w.put(1, u64::from(bsmod.is_some())); if let Some(m) = bsmod {
w.put(3, m as u64);
}
let _ = numblkscod;
w.align();
let mut bytes = w.take();
let frame_len = (frmsiz as usize + 1) * BYTES_PER_WORD;
assert!(
bytes.len() <= frame_len,
"synthetic bsi ({} bytes) exceeds its declared frame size ({frame_len})",
bytes.len()
);
bytes.resize(frame_len, 0);
stamp_crc2(&mut bytes);
bytes
}
pub(crate) fn stamp_crc2(frame: &mut [u8]) {
let n = frame.len();
let crc = ac3_crc16(&frame[AC3_SYNCWORD_BYTES..n - 2]);
frame[n - 2..].copy_from_slice(&crc.to_be_bytes());
}
pub(crate) fn seven_one_eac3_es() -> Vec<u8> {
let mut es = Vec::new();
es.extend_from_slice(&eac3_bsi(0, 0, 64, 7, true, 0, 3, None, Some(0)));
es.extend_from_slice(&eac3_bsi(
1,
0, 64,
2,
false,
0,
3,
Some(wire_chanmap(&[6, 7])),
None,
));
es
}
fn scan_eac3_frames(es: &[u8]) -> Vec<Ec3SyncframeInfo> {
split_eac3_syncframes(es)
.iter()
.flat_map(|au| {
let mut off = 0usize;
let mut v = Vec::new();
while off + 2 <= au.data.len() {
match Ec3SyncframeInfo::parse_at(&au.data, off) {
Ok(info) => {
let len = (info.frmsiz as usize + 1) * BYTES_PER_WORD;
v.push(info);
off += len;
}
Err(_) => break,
}
}
v
})
.collect()
}
#[test]
fn dec3_from_syncframes_folds_dependent_substreams() {
let es = seven_one_eac3_es();
let frames = scan_eac3_frames(&es);
assert_eq!(frames.len(), 2, "independent frame + its dependent frame");
assert_eq!(frames[1].strmtyp, EAC3_STRMTYP_DEPENDENT);
let bx = Ec3SpecificBox::from_syncframes(&frames).unwrap();
assert_eq!(bx.num_ind_sub, 0, "last independent substream id");
assert_eq!(bx.substreams.len(), 1);
let s0 = bx.substreams[0];
assert_eq!(
s0.num_dep_sub, 1,
"one dependent substream (its substreamid is 0)"
);
assert_eq!(
s0.chan_loc,
Some(0b110),
"Lrs/Rrs + Cs contribute chan_loc bits 1 and 2"
);
assert_eq!(bx.serialized_len(), 2 + 4);
let mut buf = vec![0u8; bx.serialized_len()];
bx.serialize_into(&mut buf).unwrap();
let back = Ec3SpecificBox::parse(&buf).unwrap();
assert_eq!(back, bx, "dec3 must round-trip");
}
#[test]
fn leading_orphan_dependent_frame_is_dropped() {
let orphan = eac3_bsi(1, 2, 64, 1, false, 0, 3, Some(0x0100), None);
let independent = eac3_bsi(0, 0, 64, 1, false, 0, 3, None, None);
let dependent = eac3_bsi(1, 0, 64, 1, false, 0, 3, Some(0x0100), None);
let es = [orphan.clone(), independent.clone(), dependent.clone()].concat();
let units = split_eac3_syncframes(&es);
assert_eq!(units.len(), 1, "the orphan is not its own access unit");
assert_eq!(
units[0].info.strmtyp, 0,
"the unit starts at the independent frame"
);
assert_eq!(
units[0].data,
[independent, dependent].concat(),
"the independent frame keeps its own dependent"
);
assert!(split_eac3_syncframes(&orphan).is_empty());
}
#[test]
fn num_dep_sub_counts_dependent_substreams() {
let ind = eac3_bsi(0, 0, 64, 7, true, 0, 3, None, None);
let dep0 = eac3_bsi(1, 0, 64, 2, false, 0, 3, Some(wire_chanmap(&[6])), None);
let dep1 = eac3_bsi(1, 1, 64, 2, false, 0, 3, Some(wire_chanmap(&[7])), None);
let one = scan_eac3_frames(&[ind.clone(), dep0.clone()].concat());
let b1 = Ec3SpecificBox::from_syncframes(&one).unwrap();
assert_eq!(b1.substreams[0].num_dep_sub, 1);
assert_eq!(b1.substreams[0].chan_loc, Some(0b010), "Lrs/Rrs");
let two = scan_eac3_frames(&[ind, dep0, dep1].concat());
let b2 = Ec3SpecificBox::from_syncframes(&two).unwrap();
assert_eq!(b2.substreams[0].num_dep_sub, 2);
assert_eq!(b2.substreams[0].chan_loc, Some(0b110), "Lrs/Rrs + Cs");
assert_eq!(
Ec3SpecificBox::parse(&b2.try_to_bytes().unwrap()).unwrap(),
b2
);
}
#[test]
fn ac3_core_frame_is_independent_substream_zero() {
let mut core = vec![0x0B, 0x77, 0xFD, 0x0D, 0x22, 0x30, 0xE1, 0xFC];
core.resize(2304, 0);
let crc2 = ac3_crc16(&core[AC3_SYNCWORD_BYTES..core.len() - 2]);
let n = core.len();
core[n - 2..].copy_from_slice(&crc2.to_be_bytes());
assert_eq!(
ac3_crc16(&core[AC3_SYNCWORD_BYTES..]),
0,
"crc2 closes the frame"
);
let info = Ec3SyncframeInfo::parse_at(&core, 0).unwrap();
assert_eq!((info.strmtyp, info.substreamid, info.bsid), (0, 0, 6));
assert_eq!(info.frmsiz as usize + 1, 1152);
assert_eq!((info.acmod, info.lfeon, info.fscod), (7, true, 0));
assert_eq!(info.samples_per_frame(), 1536);
let dep = eac3_bsi(
1,
0,
1151,
5,
false,
0,
3,
Some(wire_chanmap(&[3, 4, 6])),
None,
);
let units = split_eac3_syncframes(&[core.clone(), dep.clone()].concat());
assert_eq!(units.len(), 1);
assert_eq!(units[0].data, [core.clone(), dep].concat());
let mut damaged = core.clone();
damaged[100] ^= 0x01;
assert!(matches!(
Ec3SyncframeInfo::parse_at(&damaged, 0),
Err(Error::InvalidValue(InvalidValue {
field: "AC-3 crc2",
..
}))
));
assert!(Ec3SyncframeInfo::parse_at(&damaged[..1000], 0).is_ok());
}
#[test]
fn dec3_from_dependent_only_input_errors() {
let es = eac3_bsi(1, 2, 64, 1, false, 0, 3, Some(0x0100), None);
let frames = vec![Ec3SyncframeInfo::parse_at(&es, 0).unwrap()];
assert_eq!(frames[0].strmtyp, EAC3_STRMTYP_DEPENDENT);
let err = Ec3SpecificBox::from_syncframes(&frames).unwrap_err();
assert!(
matches!(
err,
Error::InvalidValue(InvalidValue {
field: "dec3 num_ind_sub",
..
})
),
"expected InvalidValue for a dependent-only AU, got {err:?}"
);
assert!(matches!(
Ec3SpecificBox::from_syncframes(&[]),
Err(Error::InvalidValue(..))
));
assert!(matches!(
frames[0].into_dec3(),
Err(Error::InvalidValue(InvalidValue {
field: "dec3 num_ind_sub",
..
}))
));
}
#[test]
fn dec3_from_a_full_backlog_matches_one_access_unit() {
let one_au = seven_one_eac3_es();
let mut backlog = Vec::new();
for _ in 0..3 {
backlog.extend_from_slice(&one_au);
}
let expected = Ec3SpecificBox::from_syncframes(&scan_eac3_frames(&one_au)).unwrap();
let frames = Ec3SyncframeInfo::from_es_first_au(&backlog);
assert_eq!(frames.len(), 2, "one AU: independent + dependent frame");
let bx = Ec3SpecificBox::from_syncframes(&frames).unwrap();
assert_eq!(bx, expected, "backlog must not inflate the substream list");
assert_eq!(bx.num_ind_sub, 0);
assert_eq!(bx.substreams.len(), 1);
assert_eq!(bx.substreams[0].num_dep_sub, 1);
let bytes = bx.try_to_bytes().unwrap();
assert_eq!(Ec3SpecificBox::parse(&bytes).unwrap(), bx);
let all = Ec3SyncframeInfo::from_es_all(&backlog);
assert_eq!(all.len(), 6);
assert!(
matches!(
Ec3SpecificBox::from_syncframes(&all),
Err(Error::InvalidValue(InvalidValue {
field: "dec3 num_ind_sub",
..
}))
),
"a repeated-AU substream list must be rejected at construction"
);
}
#[test]
fn first_au_end_is_next_au_or_broken_chain_never_missing_data() {
let one_au = seven_one_eac3_es();
let strict = |d: &[u8]| Ec3SyncframeInfo::from_es_first_complete_au(d).map(|f| f.len());
let lenient =
|d: &[u8]| Ec3SyncframeInfo::from_es_first_au_or_broken_chain(d).map(|f| f.len());
for (name, d) in [
("data just ends", one_au.clone()),
("cut mid-frame", one_au[..one_au.len() - 3].to_vec()),
(
"one trailing byte",
[one_au.clone(), alloc::vec![0x43]].concat(),
),
] {
assert_eq!(strict(&d), None, "{name}");
assert_eq!(lenient(&d), None, "{name}");
}
let two = [one_au.clone(), one_au.clone()].concat();
assert_eq!(strict(&two), Some(2));
assert_eq!(lenient(&two), Some(2));
let broken = [
one_au.clone(),
alloc::vec![0x43, 0x8B, 0x88, 0xBC, 0xA9, 0xB4, 0x00, 0x15],
]
.concat();
assert_eq!(strict(&broken), None);
assert_eq!(lenient(&broken), Some(2));
let junk = alloc::vec![0x43u8, 0x8B, 0x88, 0xBC, 0xA9, 0xB4, 0x00, 0x15];
assert_eq!(lenient(&junk), None, "no sync at frame 0");
let mut bad_hdr = one_au[..8].to_vec();
bad_hdr.extend_from_slice(&one_au);
bad_hdr[2] = 0xFF;
bad_hdr[3] = 0xFF;
assert_eq!(lenient(&bad_hdr), None, "frame 0 header does not parse");
}
#[test]
fn dec3_carries_bsmod_from_infomdate() {
let es = eac3_bsi(0, 0, 64, 7, true, 0, 3, None, Some(7));
let info = Ec3SyncframeInfo::from_es(&es).unwrap();
assert_eq!(info.bsmod, 7, "bsmod parsed from bsi()");
let dec3 = info.into_dec3().unwrap();
assert_eq!(dec3.substreams[0].bsmod, 7);
let es0 = eac3_bsi(0, 0, 64, 7, true, 0, 3, None, None);
assert_eq!(Ec3SyncframeInfo::from_es(&es0).unwrap().bsmod, 0);
}
pub(crate) fn damaged_for_tests(mut frame: Vec<u8>) -> Vec<u8> {
let mid = frame.len() / 2;
frame[mid] ^= 0x10;
frame
}
#[test]
fn eac3_bsi_bit_vector_is_hand_computed() {
let f = eac3_bsi(0, 0, 63, 2, false, 0, 3, None, Some(2));
assert_eq!(f.len(), 128);
assert_eq!(f[..7], [0x0B, 0x77, 0x00, 0x3F, 0x34, 0x87, 0xCA]);
assert!(eac3_frame_crc_ok(&f), "builder stamps a valid crc2");
assert!(
!eac3_frame_crc_ok(&damaged_for_tests(f)),
"a flipped byte fails it"
);
}
#[test]
fn crc_verified_first_au_rejects_damaged_frames() {
let good = eac3_bsi(0, 0, 63, 2, false, 0, 3, None, Some(2));
let bad = damaged_for_tests(good.clone());
let verified = |d: &[u8], broken: bool| {
Ec3SyncframeInfo::from_es_first_au_crc_verified(d, broken).map(|f| f.len())
};
assert_eq!(
verified(&[good.clone(), good.clone()].concat(), false),
Some(1)
);
assert_eq!(verified(&[bad.clone(), good.clone()].concat(), true), None);
let then_bad = [good.clone(), bad.clone()].concat();
assert_eq!(verified(&then_bad, false), None);
assert_eq!(verified(&then_bad, true), Some(1));
assert_eq!(verified(&good, true), None);
assert_eq!(verified(&good[..good.len() - 1], true), None);
assert_eq!(
Ec3SyncframeInfo::from_es_first_au_or_broken_chain(&[bad.clone(), good].concat())
.map(|f| f.len()),
Some(1)
);
}
#[test]
fn first_crc_verified_bsmod_skips_absent_and_damaged() {
let none = eac3_bsi(0, 0, 63, 2, false, 0, 3, None, None);
let two = eac3_bsi(0, 0, 63, 2, false, 0, 3, None, Some(2));
let five_bad = damaged_for_tests(eac3_bsi(0, 0, 63, 2, false, 0, 3, None, Some(5)));
let other_substream = eac3_bsi(0, 1, 63, 2, false, 0, 3, None, Some(6));
let es = [none.clone(), five_bad.clone(), other_substream, two].concat();
assert_eq!(Ec3SyncframeInfo::first_crc_verified_bsmod(&es, 0), Some(2));
assert_eq!(Ec3SyncframeInfo::first_crc_verified_bsmod(&es, 1), Some(6));
assert_eq!(Ec3SyncframeInfo::first_crc_verified_bsmod(&es, 2), None);
let no_carrier = [none, five_bad].concat();
assert_eq!(
Ec3SyncframeInfo::first_crc_verified_bsmod(&no_carrier, 0),
None
);
}
#[test]
fn dec3_bsmod_reached_through_mixmdate_variants() {
for (mixdef, mixdata_bits) in [(1u64, 5usize), (2, 12)] {
let mut w = BitBuf::default();
w.put(16, AC3_SYNCWORD as u64);
w.put(2, 0); w.put(3, 0); w.put(11, 64); w.put(2, 0); w.put(2, 3); w.put(3, 7); w.put(1, 1); w.put(5, 16); w.put(5, 31); w.put(1, 0); w.put(1, 1); w.put(2, 0); w.put(3, 0); w.put(3, 0); w.put(3, 0); w.put(3, 0); w.put(1, 0); w.put(1, 0); w.put(1, 0); w.put(2, mixdef);
if mixdef == 1 {
w.put(1, 0); w.put(1, 0); w.put(3, 0); } else {
w.put(mixdata_bits, 0); }
w.put(1, 0); w.put(1, 1); w.put(3, 5); w.align();
let es = w.take();
let info = Ec3SyncframeInfo::from_es(&es).unwrap();
assert_eq!(info.bsmod, 5, "mixdef {mixdef}: bsmod landed correctly");
}
}
#[test]
fn dec3_reserved_tail_is_preserved() {
let mut body = DEC3_ORACLE.to_vec();
body.extend_from_slice(&[0x01, 0x00, 0x10]);
let bx = Ec3SpecificBox::parse(&body).unwrap();
assert_eq!(bx.reserved_tail, vec![0x01, 0x00, 0x10]);
assert_eq!(bx.serialized_len(), body.len());
let mut out = vec![0u8; bx.serialized_len()];
bx.serialize_into(&mut out).unwrap();
assert_eq!(out, body, "reserved tail must round-trip byte-exactly");
assert!(
Ec3SpecificBox::parse(&DEC3_ORACLE)
.unwrap()
.reserved_tail
.is_empty()
);
}
#[test]
fn dec3_substream_count_must_match_num_ind_sub() {
let mut bx = Ec3SpecificBox::parse(&DEC3_ORACLE).unwrap();
assert_eq!(bx.num_ind_sub, 0);
bx.substreams.push(bx.substreams[0]);
let err = bx.try_to_bytes().unwrap_err();
assert!(
matches!(
err,
Error::InvalidValue(InvalidValue {
field: "dec3 num_ind_sub",
..
})
),
"expected InvalidValue for the count mismatch, got {err:?}"
);
bx.num_ind_sub = 1;
let bytes = bx.try_to_bytes().unwrap();
assert_eq!(Ec3SpecificBox::parse(&bytes).unwrap(), bx);
}
fn wire_chanmap(bits: &[u8]) -> u16 {
let mut v: u16 = 0;
for &b in bits {
assert!(b < 16, "Table E.1.4 only defines bits 0..=15");
v |= 1u16 << (15 - b);
}
v
}
#[test]
fn bsmod_is_reached_for_a_strmtyp_two_frame() {
let mut w = BitBuf::default();
w.put(16, AC3_SYNCWORD as u64);
w.put(2, 2); w.put(3, 0); w.put(11, 64); w.put(2, 0); w.put(2, 3); w.put(3, 2); w.put(1, 0); w.put(5, 16); w.put(5, 31); w.put(1, 0); w.put(1, 1); w.put(1, 1); w.put(3, 6); w.align();
let mut es = w.take();
es.resize(130, 0);
let info = Ec3SyncframeInfo::from_es(&es).unwrap();
assert_eq!(info.strmtyp, 2);
assert_eq!(
info.bsmod, 6,
"strmtyp-2 frame has no programme-mix block, so bsmod follows mixmdate directly"
);
assert_eq!(
Ec3SyncframeInfo::from_es(&eac3_bsi(0, 0, 64, 2, false, 0, 3, None, Some(6)))
.unwrap()
.bsmod,
6
);
}
#[test]
fn mixdef_option_four_walks_to_the_right_bit() {
for (mixdata2e, mixdata3e, addspchdate, addspchdat1e) in [
(false, false, false, false),
(true, false, false, false),
(true, true, true, true),
(false, true, false, false),
] {
for mixdeflen in [0u64, 1, 7] {
let mut w = BitBuf::default();
w.put(16, AC3_SYNCWORD as u64);
w.put(2, 0); w.put(3, 0);
w.put(11, 64);
w.put(2, 0); w.put(2, 3); w.put(3, 7); w.put(1, 1); w.put(5, 16); w.put(5, 31); w.put(1, 0); w.put(1, 1); w.put(2, 0); w.put(3, 0); w.put(3, 0); w.put(3, 0); w.put(3, 0); w.put(1, 0); w.put(1, 0); w.put(1, 0); w.put(2, 3); w.put(5, mixdeflen);
w.put(1, u64::from(mixdata2e));
if mixdata2e {
w.put(1, 0); w.put(1, 0); w.put(3, 0); for _ in 0..7 {
w.put(1, 0); }
w.put(1, 0); }
w.put(1, u64::from(mixdata3e));
if mixdata3e {
w.put(5, 0); w.put(1, u64::from(addspchdate));
if addspchdate {
w.put(5, 0); w.put(2, 0); w.put(1, u64::from(addspchdat1e));
if addspchdat1e {
w.put(5, 0); w.put(3, 0); }
}
}
for _ in 0..8 * (mixdeflen + 2) {
w.put(1, 0);
}
w.align(); w.put(1, 0); w.put(1, 1); w.put(3, 5); w.align();
let mut es = w.take();
es.resize(130, 0);
let info = Ec3SyncframeInfo::from_es(&es).unwrap();
assert_eq!(
info.bsmod, 5,
"mixdeflen {mixdeflen}, mixdata2e {mixdata2e}, mixdata3e {mixdata3e}: bsmod landed on the wrong bit"
);
}
}
}
#[test]
fn chanmap_to_chan_loc_covers_the_spec_locations() {
let info = Ec3SyncframeInfo {
strmtyp: EAC3_STRMTYP_DEPENDENT,
substreamid: 1,
frmsiz: 1,
fscod: 0,
numblks: 6,
acmod: 2,
lfeon: false,
bsid: 16,
sample_rate: 48000,
sample_rate_khz: 48,
chanmap: Some(0xFFFF),
bsmod: 0,
};
assert_eq!(chan_loc_for_frame(&info), 0b1_1111_1111);
let base = Ec3SyncframeInfo {
chanmap: Some(wire_chanmap(&[0, 1, 2, 3, 4, 15])),
..info
};
assert_eq!(
chan_loc_for_frame(&base),
0,
"5.1 locations are not chan_loc"
);
let lrs_rrs = Ec3SyncframeInfo {
chanmap: Some(wire_chanmap(&[0, 1, 2, 3, 4, 6, 15])),
..info
};
assert_eq!(
chan_loc_for_frame(&lrs_rrs),
0b10,
"E.1.4 bit 6 -> F.6.1 bit 1"
);
let ends = Ec3SyncframeInfo {
chanmap: Some(wire_chanmap(&[5, 14])),
..info
};
assert_eq!(chan_loc_for_frame(&ends), 0b1_0000_0001);
assert_eq!(
chan_loc_for_frame(&Ec3SyncframeInfo {
chanmap: None,
..info
}),
0
);
}
}