use oxideav_core::bits::BitReader;
use oxideav_core::{Error, Result};
#[inline]
pub fn base_sample_rate(fs_index: u32) -> u32 {
if fs_index == 0 {
44_100
} else {
48_000
}
}
pub fn frame_rate_entry(frame_rate_index: u32, fs_index: u32) -> (u32, u32) {
if fs_index == 0 {
if frame_rate_index == 13 {
(21_533, 2_048)
} else {
(0, 0)
}
} else {
match frame_rate_index {
0 => (23_976, 1_920),
1 => (24_000, 1_920),
2 => (25_000, 2_048),
3 => (29_970, 1_536),
4 => (30_000, 1_536),
5 => (47_950, 960),
6 => (48_000, 960),
7 => (50_000, 1_024),
8 => (59_940, 768),
9 => (60_000, 768),
10 => (100_000, 512),
11 => (119_880, 384),
12 => (120_000, 384),
13 => (23_440, 2_048),
_ => (0, 0),
}
}
}
pub fn variable_bits(br: &mut BitReader<'_>, n_bits: u32) -> Result<u32> {
let mut value: u32 = 0;
loop {
let chunk = br.read_u32(n_bits)?;
value = value
.checked_add(chunk)
.ok_or_else(|| Error::invalid("ac4: variable_bits overflow"))?;
let more = br.read_bit()?;
if !more {
return Ok(value);
}
value = value
.checked_shl(n_bits)
.ok_or_else(|| Error::invalid("ac4: variable_bits shift overflow"))?;
value = value
.checked_add(1u32 << n_bits)
.ok_or_else(|| Error::invalid("ac4: variable_bits bias overflow"))?;
}
}
pub fn decode_channel_mode(br: &mut BitReader<'_>) -> Result<(u32, u32)> {
let b0 = br.read_u32(1)?;
if b0 == 0 {
return Ok((1, 1));
}
let b1 = br.read_u32(1)?;
if b1 == 0 {
return Ok((2, 2));
}
let nx = br.read_u32(2)?;
if nx != 0b11 {
return Ok((
match nx {
0b00 => 3,
0b01 => 5,
0b10 => 6,
_ => 0,
},
4,
));
}
let tail = br.read_u32(3)?;
let channels = match tail {
0b000 => 7, 0b001 => 8, 0b010 => 7, 0b011 => 8, 0b100 => 7, 0b101 => 8, 0b110 => 7, 0b111 => {
let _ext = variable_bits(br, 2)?;
return Ok((0, 7 + 3));
}
_ => unreachable!("3-bit tail is 0..=7"),
};
Ok((channels, 7))
}
#[derive(Debug, Clone)]
pub struct Ac4FrameInfo {
pub bitstream_version: u32,
pub sequence_counter: u32,
pub fs_index: u32,
pub base_sample_rate: u32,
pub sample_rate: u32,
pub frame_rate_index: u32,
pub frame_rate_milli: u32,
pub frame_length: u32,
pub b_iframe_global: bool,
pub channels: u16,
pub n_presentations: u32,
pub n_substreams: u32,
pub substream_sizes: Vec<u32>,
pub payload_base: u32,
pub presentations: Vec<PresentationInfo>,
pub toc_size: u32,
}
#[derive(Debug, Clone, Default)]
pub struct PresentationInfo {
pub version: u32,
pub b_single_substream: bool,
pub presentation_config: u32,
pub channels: u16,
pub n_substream_info: u32,
pub n_hsf_ext: u32,
pub n_add_emdf_substreams: u32,
pub b_iframe: bool,
pub sf_multiplier: u32,
}
pub fn parse_ac4_toc(bytes: &[u8]) -> Result<Ac4FrameInfo> {
let mut br = BitReader::new(bytes);
let mut bitstream_version = br.read_u32(2)?;
if bitstream_version == 3 {
bitstream_version += variable_bits(&mut br, 2)?;
}
let sequence_counter = br.read_u32(10)?;
let b_wait_frames = br.read_bit()?;
if b_wait_frames {
let wait_frames = br.read_u32(3)?;
if wait_frames > 0 {
let _reserved = br.read_u32(2)?;
}
}
let fs_index = br.read_u32(1)?;
let frame_rate_index = br.read_u32(4)?;
let b_iframe_global = br.read_bit()?;
let b_single_presentation = br.read_bit()?;
let n_presentations = if b_single_presentation {
1
} else {
let b_more = br.read_bit()?;
if b_more {
variable_bits(&mut br, 2)? + 2
} else {
0
}
};
let b_payload_base = br.read_bit()?;
let payload_base = if b_payload_base {
let base = br.read_u32(5)? + 1;
if base == 0x20 {
base + variable_bits(&mut br, 3)?
} else {
base
}
} else {
0
};
let mut presentations = Vec::with_capacity(n_presentations as usize);
if bitstream_version <= 1 {
for _ in 0..n_presentations {
let pi = parse_presentation_info(&mut br, fs_index, frame_rate_index)?;
presentations.push(pi);
}
} else {
let b_program_id = br.read_bit()?;
if b_program_id {
let _short_program_id = br.read_u32(16)?;
let b_program_uuid_present = br.read_bit()?;
if b_program_uuid_present {
br.skip(16 * 8)?;
}
}
let mut total_n_substream_groups: u32 = 0;
for _ in 0..n_presentations {
let (pi, n_sg) =
parse_presentation_v1_info(&mut br, bitstream_version, fs_index, frame_rate_index)?;
total_n_substream_groups += n_sg;
presentations.push(pi);
}
let mut first_group_channels: u16 = 0;
let mut first_group_sf_mul: u32 = 0;
for j in 0..total_n_substream_groups {
let g =
parse_substream_group_info(&mut br, bitstream_version, fs_index, frame_rate_index)?;
if j == 0 {
first_group_channels = g.first_channels;
first_group_sf_mul = g.first_sf_multiplier;
}
}
if let Some(p) = presentations.first_mut() {
if p.channels == 0 {
p.channels = first_group_channels;
}
if p.sf_multiplier == 0 {
p.sf_multiplier = first_group_sf_mul;
}
}
}
let (n_substreams, substream_sizes) = parse_substream_index_table(&mut br)?;
br.align_to_byte();
let toc_size = br.byte_position() as u32;
let base_sr = base_sample_rate(fs_index);
let sf_mul = presentations.first().map(|p| p.sf_multiplier).unwrap_or(0);
let sample_rate = match (fs_index, sf_mul) {
(1, 1) => 96_000,
(1, 2) => 192_000,
_ => base_sr,
};
let channels = presentations.first().map(|p| p.channels).unwrap_or(0);
let (fps_milli, frame_length) = frame_rate_entry(frame_rate_index, fs_index);
Ok(Ac4FrameInfo {
bitstream_version,
sequence_counter,
fs_index,
base_sample_rate: base_sr,
sample_rate,
frame_rate_index,
frame_rate_milli: fps_milli,
frame_length,
b_iframe_global,
channels,
n_presentations,
n_substreams,
substream_sizes,
payload_base,
presentations,
toc_size,
})
}
fn frame_rate_factor(frame_rate_index: u32, b_multiplier: bool, multiplier_bit: u32) -> u32 {
match frame_rate_index {
2..=4 if b_multiplier => {
if multiplier_bit == 0 {
2
} else {
4
}
}
0 | 1 | 7 | 8 | 9 if b_multiplier => 2,
_ => 1,
}
}
fn parse_frame_rate_multiply_info(
br: &mut BitReader<'_>,
frame_rate_index: u32,
) -> Result<(bool, u32)> {
let mut b_multiplier = false;
let mut multiplier_bit = 0u32;
match frame_rate_index {
2..=4 => {
b_multiplier = br.read_bit()?;
if b_multiplier {
multiplier_bit = br.read_u32(1)?;
}
}
0 | 1 | 7 | 8 | 9 => {
b_multiplier = br.read_bit()?;
}
_ => {}
}
Ok((b_multiplier, multiplier_bit))
}
fn parse_emdf_info(br: &mut BitReader<'_>) -> Result<()> {
let emdf_version = br.read_u32(2)?;
if emdf_version == 3 {
let _ = variable_bits(br, 2)?;
}
let key_id = br.read_u32(3)?;
if key_id == 7 {
let _ = variable_bits(br, 3)?;
}
let b_emdf_payloads_substream_info = br.read_bit()?;
if b_emdf_payloads_substream_info {
parse_emdf_payloads_substream_info(br)?;
}
parse_emdf_reserved(br)?;
Ok(())
}
fn parse_emdf_payloads_substream_info(br: &mut BitReader<'_>) -> Result<()> {
let substream_index = br.read_u32(2)?;
if substream_index == 3 {
let _ = variable_bits(br, 2)?;
}
Ok(())
}
fn parse_emdf_reserved(br: &mut BitReader<'_>) -> Result<()> {
let b_more_bits = br.read_bit()?;
if b_more_bits {
let n_bits = variable_bits(br, 5)?;
if n_bits > 1 << 20 {
return Err(Error::invalid("ac4: emdf_reserved claims too many bits"));
}
br.skip(n_bits)?;
}
Ok(())
}
fn parse_substream_info(
br: &mut BitReader<'_>,
fs_index: u32,
frame_rate_index: u32,
) -> Result<SubstreamInfo> {
let (channels, _mode_bits) = decode_channel_mode(br)?;
let mut sf_multiplier = 0;
if fs_index == 1 {
let b_sf_multiplier = br.read_bit()?;
if b_sf_multiplier {
sf_multiplier = br.read_u32(1)? + 1;
}
}
let b_bitrate_info = br.read_bit()?;
if b_bitrate_info {
let short = br.read_u32(3)?;
if short == 0b111 {
let _ = br.read_u32(2)?;
}
}
if channels == 7 || channels == 8 {
}
let b_content_type = br.read_bit()?;
if b_content_type {
parse_content_type(br)?;
}
let factor = frame_rate_factor(frame_rate_index, false, 0);
let mut b_iframe = false;
for _ in 0..factor.max(1) {
let f = br.read_bit()?;
if !b_iframe {
b_iframe = f;
}
}
let si = br.read_u32(2)?;
if si == 3 {
let _ = variable_bits(br, 2)?;
}
Ok(SubstreamInfo {
channels: channels as u16,
sf_multiplier,
b_iframe,
})
}
struct SubstreamInfo {
channels: u16,
sf_multiplier: u32,
b_iframe: bool,
}
fn parse_content_type(br: &mut BitReader<'_>) -> Result<()> {
let _content_classifier = br.read_u32(3)?;
let b_language_indicator = br.read_bit()?;
if b_language_indicator {
let b_serialized = br.read_bit()?;
if b_serialized {
let _b_start_tag = br.read_bit()?;
let _language_tag_chunk = br.read_u32(16)?;
} else {
let n = br.read_u32(6)?;
br.skip(8 * n)?;
}
}
Ok(())
}
fn parse_hsf_ext_substream_info(br: &mut BitReader<'_>) -> Result<()> {
let si = br.read_u32(2)?;
if si == 3 {
let _ = variable_bits(br, 2)?;
}
Ok(())
}
fn parse_presentation_config_ext_info(br: &mut BitReader<'_>) -> Result<()> {
let mut n_skip_bytes = br.read_u32(5)?;
let b_more = br.read_bit()?;
if b_more {
n_skip_bytes += variable_bits(br, 2)? << 5;
}
if n_skip_bytes > 1 << 20 {
return Err(Error::invalid("ac4: presentation_config_ext_info too big"));
}
br.skip(n_skip_bytes * 8)?;
Ok(())
}
fn parse_presentation_info(
br: &mut BitReader<'_>,
fs_index: u32,
frame_rate_index: u32,
) -> Result<PresentationInfo> {
let mut info = PresentationInfo::default();
let b_single_substream = br.read_bit()?;
info.b_single_substream = b_single_substream;
let mut presentation_config: u32 = 0;
if !b_single_substream {
presentation_config = br.read_u32(3)?;
if presentation_config == 7 {
presentation_config += variable_bits(br, 2)?;
}
}
info.presentation_config = presentation_config;
let mut ver = 0u32;
while br.read_bit()? {
ver += 1;
if ver > 32 {
return Err(Error::invalid("ac4: runaway presentation_version"));
}
}
info.version = ver;
let b_add_emdf_substreams;
if !b_single_substream && presentation_config == 6 {
b_add_emdf_substreams = true;
} else {
let _md_compat = br.read_u32(3)?;
let b_belongs_to_presentation_id = br.read_bit()?;
if b_belongs_to_presentation_id {
let _presentation_id = variable_bits(br, 2)?;
}
let (_b_mult, _mult_bit) = parse_frame_rate_multiply_info(br, frame_rate_index)?;
parse_emdf_info(br)?;
if b_single_substream {
let si = parse_substream_info(br, fs_index, frame_rate_index)?;
info.channels = si.channels;
info.sf_multiplier = si.sf_multiplier;
info.b_iframe = si.b_iframe;
info.n_substream_info = 1;
} else {
let _b_hsf_ext = br.read_bit()?;
let b_hsf_ext = _b_hsf_ext;
match presentation_config {
0..=2 => {
let first = parse_substream_info(br, fs_index, frame_rate_index)?;
info.channels = first.channels;
info.sf_multiplier = first.sf_multiplier;
info.b_iframe = first.b_iframe;
info.n_substream_info = 1;
if b_hsf_ext {
parse_hsf_ext_substream_info(br)?;
info.n_hsf_ext += 1;
}
let _second = parse_substream_info(br, fs_index, frame_rate_index)?;
info.n_substream_info += 1;
}
3 | 4 => {
let first = parse_substream_info(br, fs_index, frame_rate_index)?;
info.channels = first.channels;
info.sf_multiplier = first.sf_multiplier;
info.b_iframe = first.b_iframe;
info.n_substream_info = 1;
if b_hsf_ext {
parse_hsf_ext_substream_info(br)?;
info.n_hsf_ext += 1;
}
let _second = parse_substream_info(br, fs_index, frame_rate_index)?;
let _third = parse_substream_info(br, fs_index, frame_rate_index)?;
info.n_substream_info += 2;
}
5 => {
let first = parse_substream_info(br, fs_index, frame_rate_index)?;
info.channels = first.channels;
info.sf_multiplier = first.sf_multiplier;
info.b_iframe = first.b_iframe;
info.n_substream_info = 1;
if b_hsf_ext {
parse_hsf_ext_substream_info(br)?;
info.n_hsf_ext += 1;
}
}
_ => {
parse_presentation_config_ext_info(br)?;
}
}
}
let _b_pre_virtualized = br.read_bit()?;
b_add_emdf_substreams = br.read_bit()?;
}
if b_add_emdf_substreams {
let mut n = br.read_u32(2)?;
if n == 0 {
n = variable_bits(br, 2)? + 4;
}
for _ in 0..n {
parse_emdf_info(br)?;
}
info.n_add_emdf_substreams = n;
}
Ok(info)
}
fn parse_presentation_v1_info(
br: &mut BitReader<'_>,
bitstream_version: u32,
fs_index: u32,
frame_rate_index: u32,
) -> Result<(PresentationInfo, u32)> {
let mut info = PresentationInfo::default();
let b_single_substream_group = br.read_bit()?;
info.b_single_substream = b_single_substream_group;
let mut presentation_config: u32 = 0;
if !b_single_substream_group {
presentation_config = br.read_u32(3)?;
if presentation_config == 7 {
presentation_config += variable_bits(br, 2)?;
}
}
info.presentation_config = presentation_config;
if bitstream_version != 1 {
let mut ver = 0u32;
while br.read_bit()? {
ver += 1;
if ver > 32 {
return Err(Error::invalid("ac4: runaway presentation_version"));
}
}
info.version = ver;
}
let mut n_substream_groups: u32 = 0;
let b_add_emdf_substreams;
if !b_single_substream_group && presentation_config == 6 {
b_add_emdf_substreams = true;
} else {
if bitstream_version != 1 {
let _mdcompat = br.read_u32(3)?;
}
let b_presentation_id = br.read_bit()?;
if b_presentation_id {
let _presentation_id = variable_bits(br, 2)?;
}
let (_b_mult, _mult_bit) = parse_frame_rate_multiply_info(br, frame_rate_index)?;
parse_frame_rate_fractions_info(br, frame_rate_index)?;
parse_emdf_info(br)?;
let b_presentation_filter = br.read_bit()?;
if b_presentation_filter {
let _b_enable_presentation = br.read_bit()?;
}
if b_single_substream_group {
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
n_substream_groups = 1;
} else {
let _b_multi_pid = br.read_bit()?;
match presentation_config {
0 | 2 => {
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
n_substream_groups = 2;
}
1 => {
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
n_substream_groups = 1;
}
3 => {
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
n_substream_groups = 3;
}
4 => {
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
n_substream_groups = 2;
}
5 => {
let n_minus2 = br.read_u32(2)?;
let mut n = n_minus2 + 2;
if n == 5 {
n += variable_bits(br, 2)?;
}
if n > 64 {
return Err(Error::invalid("ac4: presentation_config=5 n too big"));
}
for _ in 0..n {
parse_sgi_specifier(br, bitstream_version, fs_index, frame_rate_index)?;
}
n_substream_groups = n;
}
_ => {
parse_presentation_config_ext_info(br)?;
}
}
}
let _b_pre_virtualized = br.read_bit()?;
b_add_emdf_substreams = br.read_bit()?;
let _b_alternative = br.read_bit()?;
let b_pres_ndot = br.read_bit()?;
info.b_iframe = !b_pres_ndot; let si = br.read_u32(2)?;
if si == 3 {
let _ = variable_bits(br, 2)?;
}
}
if b_add_emdf_substreams {
let mut n = br.read_u32(2)?;
if n == 0 {
n = variable_bits(br, 2)? + 4;
}
for _ in 0..n {
parse_emdf_info(br)?;
}
info.n_add_emdf_substreams = n;
}
Ok((info, n_substream_groups))
}
fn parse_sgi_specifier(
br: &mut BitReader<'_>,
bitstream_version: u32,
fs_index: u32,
frame_rate_index: u32,
) -> Result<u32> {
if bitstream_version == 1 {
parse_substream_group_info(br, bitstream_version, fs_index, frame_rate_index)?;
Ok(0)
} else {
let mut group_index = br.read_u32(3)?;
if group_index == 7 {
group_index += variable_bits(br, 2)?;
}
Ok(group_index)
}
}
fn parse_substream_group_info(
br: &mut BitReader<'_>,
bitstream_version: u32,
fs_index: u32,
frame_rate_index: u32,
) -> Result<SubstreamGroupSummary> {
let mut summary = SubstreamGroupSummary::default();
let b_substreams_present = br.read_bit()?;
let b_hsf_ext = br.read_bit()?;
let b_single_substream = br.read_bit()?;
let n_lf_substreams = if b_single_substream {
1
} else {
let n_minus2 = br.read_u32(2)?;
let mut n = n_minus2 + 2;
if n == 5 {
n += variable_bits(br, 2)?;
}
n
};
if n_lf_substreams > 64 {
return Err(Error::invalid("ac4: n_lf_substreams too big"));
}
let b_channel_coded = br.read_bit()?;
if b_channel_coded {
for sus in 0..n_lf_substreams {
if bitstream_version == 1 {
let _sus_ver = br.read_bit()?;
}
let chan =
parse_substream_info_chan(br, fs_index, frame_rate_index, b_substreams_present)?;
if sus == 0 {
summary.first_channels = chan.channels;
summary.first_sf_multiplier = chan.sf_multiplier;
}
if b_hsf_ext && b_substreams_present {
let si = br.read_u32(2)?;
if si == 3 {
let _ = variable_bits(br, 2)?;
}
}
}
} else {
let b_oamd_substream = br.read_bit()?;
if b_oamd_substream {
let _b_oamd_ndot = br.read_bit()?;
if b_substreams_present {
let si = br.read_u32(2)?;
if si == 3 {
let _ = variable_bits(br, 2)?;
}
}
}
if n_lf_substreams > 0 {
let _b_ajoc = br.read_bit()?;
return Err(Error::unsupported(
"ac4: ajoc / object substream parsing not implemented",
));
}
}
let b_content_type = br.read_bit()?;
if b_content_type {
parse_content_type(br)?;
}
Ok(summary)
}
fn parse_substream_info_chan(
br: &mut BitReader<'_>,
fs_index: u32,
frame_rate_index: u32,
b_substreams_present: bool,
) -> Result<SubstreamInfoChan> {
let (channels, _mode_bits) = decode_channel_mode(br)?;
let mut sf_multiplier = 0;
if fs_index == 1 {
let b_sf_multiplier = br.read_bit()?;
if b_sf_multiplier {
sf_multiplier = br.read_u32(1)? + 1;
}
}
let b_bitrate_info = br.read_bit()?;
if b_bitrate_info {
let short = br.read_u32(3)?;
if short == 0b111 {
let _ = br.read_u32(2)?;
}
}
let factor = frame_rate_factor(frame_rate_index, false, 0);
for _ in 0..factor.max(1) {
let _b_audio_ndot = br.read_bit()?;
}
if b_substreams_present {
let si = br.read_u32(2)?;
if si == 3 {
let _ = variable_bits(br, 2)?;
}
}
Ok(SubstreamInfoChan {
channels: channels as u16,
sf_multiplier,
})
}
#[derive(Debug, Clone, Copy, Default)]
struct SubstreamInfoChan {
channels: u16,
sf_multiplier: u32,
}
#[derive(Debug, Clone, Copy, Default)]
struct SubstreamGroupSummary {
first_channels: u16,
first_sf_multiplier: u32,
}
fn parse_frame_rate_fractions_info(br: &mut BitReader<'_>, frame_rate_index: u32) -> Result<()> {
match frame_rate_index {
5..=9 => {
let _b_frame_rate_fraction = br.read_bit()?;
}
10..=12 => {
let b_frame_rate_fraction = br.read_bit()?;
if b_frame_rate_fraction {
let _b_frame_rate_fraction_is_4 = br.read_bit()?;
}
}
_ => {}
}
Ok(())
}
fn parse_substream_index_table(br: &mut BitReader<'_>) -> Result<(u32, Vec<u32>)> {
let mut n_substreams = br.read_u32(2)?;
if n_substreams == 0 {
n_substreams = variable_bits(br, 2)? + 4;
}
let b_size_present = if n_substreams == 1 {
br.read_bit()?
} else {
true
};
let mut sizes = Vec::new();
if b_size_present {
for _ in 0..n_substreams {
let b_more_bits = br.read_bit()?;
let mut size = br.read_u32(10)?;
if b_more_bits {
size += variable_bits(br, 2)? << 10;
}
sizes.push(size);
}
}
Ok((n_substreams, sizes))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn variable_bits_single_chunk() {
#[allow(clippy::unusual_byte_groupings)] let bytes = [0b10_0_00000];
let mut br = BitReader::new(&bytes);
let v = variable_bits(&mut br, 2).unwrap();
assert_eq!(v, 0b10);
}
#[test]
fn variable_bits_multi_chunk() {
let bytes = [0b1110_1000];
let mut br = BitReader::new(&bytes);
let v = variable_bits(&mut br, 2).unwrap();
assert_eq!(v, 17);
}
#[test]
fn frame_rate_entry_table() {
assert_eq!(frame_rate_entry(1, 1), (24_000, 1_920));
assert_eq!(frame_rate_entry(6, 1), (48_000, 960));
assert_eq!(frame_rate_entry(13, 0), (21_533, 2_048));
assert_eq!(frame_rate_entry(14, 1), (0, 0));
}
#[test]
fn channel_mode_mono_stereo_51() {
let bytes = [0b0_0000000];
let mut br = BitReader::new(&bytes);
assert_eq!(decode_channel_mode(&mut br).unwrap(), (1, 1));
let bytes = [0b10_000000];
let mut br = BitReader::new(&bytes);
assert_eq!(decode_channel_mode(&mut br).unwrap(), (2, 2));
let bytes = [0b1110_0000];
let mut br = BitReader::new(&bytes);
assert_eq!(decode_channel_mode(&mut br).unwrap(), (6, 4));
}
}