use crate::error::{Error, Result};
use alloc::vec::Vec;
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;
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;
if data.len() < need {
return Err(Error::BufferTooShort {
need,
have: data.len(),
what: "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> {
let need = off + 6;
if data.len() < need {
return Err(Error::BufferTooShort {
need,
have: data.len(),
what: "E-AC-3 syncframe",
});
}
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)?;
Ok(Self {
strmtyp,
substreamid,
frmsiz,
fscod,
numblks,
acmod,
lfeon,
bsid,
sample_rate,
sample_rate_khz,
chanmap,
bsmod,
})
}
pub fn from_es_first_au(data: &[u8]) -> Vec<Self> {
let frames = Self::from_es_all(data);
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) {
break;
}
seen_independent.push(f.substreamid);
}
out.push(f);
}
out
}
pub fn from_es_all(data: &[u8]) -> Vec<Self> {
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 {
break;
}
let Ok(info) = Self::parse_at(data, off) else {
break;
};
let len = (info.frmsiz as usize + 1) * BYTES_PER_WORD;
if len == 0 || off + len > data.len() {
break;
}
out.push(info);
off += len;
}
out
}
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
&& 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> {
if bytes.len() < 3 {
return Err(Error::BufferTooShort {
need: 3,
have: bytes.len(),
what: "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> {
if buf.len() < 3 {
return Err(Error::OutputBufferTooSmall {
need: 3,
have: buf.len(),
});
}
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 = f.substreamid;
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 {
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;
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> {
if bytes.len() < 2 {
return Err(Error::BufferTooShort {
need: 2,
have: bytes.len(),
what: "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,
});
if bytes.len() < need_bytes {
return Err(Error::BufferTooShort {
need: need_bytes,
have: bytes.len(),
what: "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 {
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();
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
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>, 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 {
read_bits(es, bit_pos, 3, "bsmod")? as u8
} else {
0
};
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 {
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 read_bits(data: &[u8], bit_pos: &mut usize, n: usize, what: &'static str) -> Result<u64> {
crate::bitreader::read_bits_checked(data, bit_pos, n, what)
}
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, }
}
#[cfg(test)]
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)]
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);
bytes
}
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,
1,
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, "the dependent frame's substreamid");
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 dec3_from_dependent_only_input_errors() {
let es = eac3_bsi(1, 2, 64, 1, false, 0, 3, Some(0x0100), None);
let frames = scan_eac3_frames(&es);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].strmtyp, EAC3_STRMTYP_DEPENDENT);
let err = Ec3SpecificBox::from_syncframes(&frames).unwrap_err();
assert!(
matches!(
err,
Error::InvalidValue {
field: "dec3 num_ind_sub",
..
}
),
"expected InvalidValue for a dependent-only AU, got {err:?}"
);
assert!(Ec3SpecificBox::from_syncframes(&[]).is_err());
assert!(frames[0].into_dec3().is_err());
}
#[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);
let bad = Ec3SpecificBox::from_syncframes(&all).unwrap();
assert_eq!(
bad.substreams.len(),
3,
"one substream per repeated AU frame"
);
assert!(
bad.try_to_bytes().is_err(),
"a repeated-AU substream list must not serialize as if it were valid"
);
}
#[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);
}
#[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 {
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
);
}
}