use oxideav_core::bits::BitReader;
use oxideav_core::{Error, Result};
use crate::de::{parse_dialog_enhancement, DeConfig, DialogEnhancement};
use crate::drc::{
nr_drc_channels, nr_drc_subframes, parse_drc_frame, DrcChannelInfo, DrcConfig, DrcFrame,
};
use crate::emdf::{parse_emdf_payloads_substream, EmdfPayloadsSubstream};
use crate::toc::variable_bits;
pub mod channel_mode {
pub const MONO: u32 = 0;
pub const STEREO: u32 = 1;
pub const C_LR: u32 = 2;
pub const FIVE_0: u32 = 3;
pub const FIVE_1: u32 = 4;
pub const SEVEN_X_FIRST: u32 = 5;
pub const SEVEN_X_LAST: u32 = 10;
pub const SEVEN_X_FOUR_FIRST: u32 = 11;
}
fn channel_mode_contains_c(channel_mode: u32) -> bool {
channel_mode != channel_mode::MONO && channel_mode != channel_mode::STEREO
}
fn channel_mode_contains_lr(channel_mode: u32) -> bool {
channel_mode >= channel_mode::STEREO
}
fn channel_mode_contains_lsrs(channel_mode: u32) -> bool {
channel_mode >= channel_mode::FIVE_0
}
fn channel_mode_contains_lbrb(channel_mode: u32) -> bool {
channel_mode >= channel_mode::SEVEN_X_FIRST
}
fn channel_mode_contains_lwrw(channel_mode: u32) -> bool {
channel_mode >= channel_mode::SEVEN_X_FOUR_FIRST
}
fn channel_mode_contains_tfltfr(channel_mode: u32) -> bool {
channel_mode >= channel_mode::SEVEN_X_FOUR_FIRST
}
fn channel_mode_contains_lfe(channel_mode: u32) -> bool {
matches!(channel_mode, 4 | 6 | 8 | 10 | 12 | 14)
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct FurtherLoudnessInfo {
pub loudness_version: u8,
pub loud_prac_type: u8,
pub dialgate_prac_type: Option<u8>,
pub loudcorr_type: Option<bool>,
pub loudrelgat: Option<u16>,
pub loudspchgat: Option<u16>,
pub loudspchgat_dialgate_prac_type: Option<u8>,
pub loudstrm3s: Option<u16>,
pub max_loudstrm3s: Option<u16>,
pub truepk: Option<u16>,
pub max_truepk: Option<u16>,
pub prgmbndy: Option<u32>,
pub b_end_or_start: Option<bool>,
pub prgmbndy_offset: Option<u16>,
pub lra: Option<u16>,
pub lra_prac_type: Option<u8>,
pub loudmntry: Option<u16>,
pub max_loudmntry: Option<u16>,
}
fn parse_further_loudness_info(br: &mut BitReader<'_>) -> Result<FurtherLoudnessInfo> {
let mut v = FurtherLoudnessInfo::default();
let mut lv = br.read_u32(2)? as u8;
if lv == 3 {
let extra = br.read_u32(4)? as u8;
lv = lv.wrapping_add(extra);
}
v.loudness_version = lv;
v.loud_prac_type = br.read_u32(4)? as u8;
if v.loud_prac_type != 0 {
if br.read_bit()? {
v.dialgate_prac_type = Some(br.read_u32(3)? as u8);
}
v.loudcorr_type = Some(br.read_bit()?);
}
if br.read_bit()? {
v.loudrelgat = Some(br.read_u32(11)? as u16);
}
if br.read_bit()? {
v.loudspchgat = Some(br.read_u32(11)? as u16);
v.loudspchgat_dialgate_prac_type = Some(br.read_u32(3)? as u8);
}
if br.read_bit()? {
v.loudstrm3s = Some(br.read_u32(11)? as u16);
}
if br.read_bit()? {
v.max_loudstrm3s = Some(br.read_u32(11)? as u16);
}
if br.read_bit()? {
v.truepk = Some(br.read_u32(11)? as u16);
}
if br.read_bit()? {
v.max_truepk = Some(br.read_u32(11)? as u16);
}
if br.read_bit()? {
let mut prgmbndy: u32 = 1;
loop {
let bit = br.read_u32(1)?;
if bit == 1 {
break;
}
prgmbndy <<= 1;
if prgmbndy > (1u32 << 30) {
return Err(Error::invalid(
"ac4: further_loudness_info prgmbndy unary overflow",
));
}
}
v.prgmbndy = Some(prgmbndy);
v.b_end_or_start = Some(br.read_bit()?);
if br.read_bit()? {
v.prgmbndy_offset = Some(br.read_u32(11)? as u16);
}
}
if br.read_bit()? {
v.lra = Some(br.read_u32(10)? as u16);
v.lra_prac_type = Some(br.read_u32(3)? as u8);
}
if br.read_bit()? {
v.loudmntry = Some(br.read_u32(11)? as u16);
}
if br.read_bit()? {
v.max_loudmntry = Some(br.read_u32(11)? as u16);
}
if br.read_bit()? {
let mut sz = br.read_u32(5)?;
if sz == 31 {
sz = sz.checked_add(variable_bits(br, 4)?).ok_or_else(|| {
Error::invalid("ac4: further_loudness_info extension size overflow")
})?;
}
skip_n_bits(br, sz)?;
}
Ok(v)
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct BasicMetadata {
pub dialnorm_bits: u8,
pub more_basic_metadata: bool,
pub further_loudness_info: Option<FurtherLoudnessInfo>,
pub pre_dmixtyp_2ch: Option<u8>,
pub phase90_info_2ch: Option<u8>,
pub loro_centre_mixgain: Option<u8>,
pub loro_surround_mixgain: Option<u8>,
pub loro_dmx_loud_corr: Option<u8>,
pub ltrt_centre_mixgain: Option<u8>,
pub ltrt_surround_mixgain: Option<u8>,
pub ltrt_dmx_loud_corr: Option<u8>,
pub lfe_mixgain: Option<u8>,
pub preferred_dmx_method: Option<u8>,
pub pre_dmixtyp_5ch: Option<u8>,
pub pre_upmixtyp_5ch: Option<u8>,
pub pre_upmixtyp_3_4: Option<u8>,
pub pre_upmixtyp_3_2_2: Option<u8>,
pub phase90_info_mc: Option<u8>,
pub b_surround_attenuation_known: Option<bool>,
pub b_lfe_attenuation_known: Option<bool>,
pub dc_block_on: Option<bool>,
}
pub fn parse_basic_metadata(br: &mut BitReader<'_>, channel_mode: u32) -> Result<BasicMetadata> {
let mut v = BasicMetadata {
dialnorm_bits: br.read_u32(7)? as u8,
..Default::default()
};
let b_more_basic_metadata = br.read_bit()?;
v.more_basic_metadata = b_more_basic_metadata;
if !b_more_basic_metadata {
return Ok(v);
}
if br.read_bit()? {
v.further_loudness_info = Some(parse_further_loudness_info(br)?);
}
if channel_mode == channel_mode::STEREO {
if br.read_bit()? {
v.pre_dmixtyp_2ch = Some(br.read_u32(3)? as u8);
v.phase90_info_2ch = Some(br.read_u32(2)? as u8);
}
} else if channel_mode > channel_mode::STEREO {
if br.read_bit()? {
v.loro_centre_mixgain = Some(br.read_u32(3)? as u8);
v.loro_surround_mixgain = Some(br.read_u32(3)? as u8);
if br.read_bit()? {
v.loro_dmx_loud_corr = Some(br.read_u32(5)? as u8);
}
if br.read_bit()? {
v.ltrt_centre_mixgain = Some(br.read_u32(3)? as u8);
v.ltrt_surround_mixgain = Some(br.read_u32(3)? as u8);
}
if br.read_bit()? {
v.ltrt_dmx_loud_corr = Some(br.read_u32(5)? as u8);
}
if channel_mode_contains_lfe(channel_mode) && br.read_bit()? {
v.lfe_mixgain = Some(br.read_u32(5)? as u8);
}
v.preferred_dmx_method = Some(br.read_u32(2)? as u8);
}
if matches!(channel_mode, channel_mode::FIVE_0 | channel_mode::FIVE_1) {
if br.read_bit()? {
v.pre_dmixtyp_5ch = Some(br.read_u32(3)? as u8);
}
if br.read_bit()? {
v.pre_upmixtyp_5ch = Some(br.read_u32(4)? as u8);
}
}
if (channel_mode::SEVEN_X_FIRST..=channel_mode::SEVEN_X_LAST).contains(&channel_mode)
&& br.read_bit()?
{
if channel_mode <= 6 {
v.pre_upmixtyp_3_4 = Some(br.read_u32(2)? as u8);
} else if (9..=10).contains(&channel_mode) {
v.pre_upmixtyp_3_2_2 = Some(br.read_u32(1)? as u8);
}
}
v.phase90_info_mc = Some(br.read_u32(2)? as u8);
v.b_surround_attenuation_known = Some(br.read_bit()?);
v.b_lfe_attenuation_known = Some(br.read_bit()?);
}
if br.read_bit()? {
v.dc_block_on = Some(br.read_bit()?);
}
Ok(v)
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ExtendedMetadata {
pub scale_main: Option<u8>,
pub scale_main_centre: Option<u8>,
pub scale_main_front: Option<u8>,
pub pan_associated: Option<u8>,
pub dialog_max_gain: Option<u8>,
pub pan_dialog: Option<u8>,
pub pan_dialog_pair: Option<(u8, u8)>,
pub pan_signal_selector: Option<u8>,
pub b_c_active: Option<bool>,
pub b_c_has_dialog: Option<bool>,
pub b_l_active: Option<bool>,
pub b_l_has_dialog: Option<bool>,
pub b_r_active: Option<bool>,
pub b_r_has_dialog: Option<bool>,
pub b_ls_active: Option<bool>,
pub b_rs_active: Option<bool>,
pub b_lb_active: Option<bool>,
pub b_rb_active: Option<bool>,
pub b_lw_active: Option<bool>,
pub b_rw_active: Option<bool>,
pub b_vhl_active: Option<bool>,
pub b_vhr_active: Option<bool>,
pub b_lfe_active: Option<bool>,
pub event_probability: Option<u8>,
}
pub fn parse_extended_metadata(
br: &mut BitReader<'_>,
channel_mode: u32,
b_associated: bool,
b_dialog: bool,
) -> Result<ExtendedMetadata> {
let mut v = ExtendedMetadata::default();
if b_associated {
if br.read_bit()? {
v.scale_main = Some(br.read_u32(8)? as u8);
}
if br.read_bit()? {
v.scale_main_centre = Some(br.read_u32(8)? as u8);
}
if br.read_bit()? {
v.scale_main_front = Some(br.read_u32(8)? as u8);
}
if channel_mode == channel_mode::MONO {
v.pan_associated = Some(br.read_u32(8)? as u8);
}
}
if b_dialog {
if br.read_bit()? {
v.dialog_max_gain = Some(br.read_u32(2)? as u8);
}
if br.read_bit()? {
if channel_mode == channel_mode::MONO {
v.pan_dialog = Some(br.read_u32(8)? as u8);
} else {
let a = br.read_u32(8)? as u8;
let b = br.read_u32(8)? as u8;
v.pan_dialog_pair = Some((a, b));
v.pan_signal_selector = Some(br.read_u32(2)? as u8);
}
}
}
if br.read_bit()? {
if channel_mode_contains_c(channel_mode) && br.read_bit()? {
v.b_c_active = Some(true);
v.b_c_has_dialog = Some(br.read_bit()?);
}
if channel_mode_contains_lr(channel_mode) {
if br.read_bit()? {
v.b_l_active = Some(true);
v.b_l_has_dialog = Some(br.read_bit()?);
}
if br.read_bit()? {
v.b_r_active = Some(true);
v.b_r_has_dialog = Some(br.read_bit()?);
}
}
if channel_mode_contains_lsrs(channel_mode) {
v.b_ls_active = Some(br.read_bit()?);
v.b_rs_active = Some(br.read_bit()?);
}
if channel_mode_contains_lbrb(channel_mode) {
v.b_lb_active = Some(br.read_bit()?);
v.b_rb_active = Some(br.read_bit()?);
}
if channel_mode_contains_lwrw(channel_mode) {
v.b_lw_active = Some(br.read_bit()?);
v.b_rw_active = Some(br.read_bit()?);
}
if channel_mode_contains_tfltfr(channel_mode) {
v.b_vhl_active = Some(br.read_bit()?);
v.b_vhr_active = Some(br.read_bit()?);
}
if channel_mode_contains_lfe(channel_mode) {
v.b_lfe_active = Some(br.read_bit()?);
}
}
if br.read_bit()? {
v.event_probability = Some(br.read_u32(4)? as u8);
}
Ok(v)
}
#[derive(Debug, Clone, Default)]
pub struct MetadataState {
pub prev_drc_config: Option<DrcConfig>,
pub prev_de_config: Option<DeConfig>,
}
#[derive(Debug, Clone)]
pub struct Metadata {
pub basic: BasicMetadata,
pub extended: ExtendedMetadata,
pub tools_metadata_size: u32,
pub drc: DrcFrame,
pub dialog_enhancement: DialogEnhancement,
pub emdf_payloads_substream_present: bool,
pub emdf_payloads_substream: Option<EmdfPayloadsSubstream>,
pub tools_metadata_trailing_bits: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct MetadataContext {
pub channel_mode: u32,
pub b_iframe: bool,
pub b_associated: bool,
pub b_dialog: bool,
pub frame_length: u32,
}
pub fn parse_metadata(
br: &mut BitReader<'_>,
ctx: MetadataContext,
prev_state: &MetadataState,
) -> Result<Metadata> {
let basic = parse_basic_metadata(br, ctx.channel_mode)?;
let extended = parse_extended_metadata(br, ctx.channel_mode, ctx.b_associated, ctx.b_dialog)?;
let mut tools_size = br.read_u32(7)?;
if br.read_bit()? {
let extra = variable_bits(br, 3)?;
tools_size = tools_size
.checked_add(
extra
.checked_shl(7)
.ok_or_else(|| Error::invalid("ac4: tools_metadata_size shift overflow"))?,
)
.ok_or_else(|| Error::invalid("ac4: tools_metadata_size overflow"))?;
}
let tools_start_bit = br.bit_position();
let drc_chan_info = DrcChannelInfo::new(
nr_drc_channels(ctx.channel_mode),
nr_drc_subframes(ctx.frame_length).unwrap_or(1),
);
let drc = parse_drc_frame(
br,
ctx.b_iframe,
drc_chan_info,
prev_state.prev_drc_config.as_ref(),
)?;
let dialog_enhancement = parse_dialog_enhancement(br, ctx.b_iframe, prev_state.prev_de_config)?;
let consumed = (br.bit_position() - tools_start_bit) as u32;
if consumed > tools_size {
return Err(Error::invalid(
"ac4: drc_frame + dialog_enhancement consumed more than tools_metadata_size bits",
));
}
let trailing = tools_size - consumed;
skip_n_bits(br, trailing)?;
let emdf_payloads_substream_present = br.read_bit()?;
let emdf_payloads_substream = if emdf_payloads_substream_present {
Some(parse_emdf_payloads_substream(br)?)
} else {
None
};
Ok(Metadata {
basic,
extended,
tools_metadata_size: tools_size,
drc,
dialog_enhancement,
emdf_payloads_substream_present,
emdf_payloads_substream,
tools_metadata_trailing_bits: trailing,
})
}
fn skip_n_bits(br: &mut BitReader<'_>, mut n: u32) -> Result<()> {
while n >= 16 {
br.skip(16)?;
n -= 16;
}
if n > 0 {
br.skip(n)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use oxideav_core::bits::BitWriter;
#[test]
fn channel_mode_lfe_mapping() {
for &m in &[4u32, 6, 8, 10, 12, 14] {
assert!(
channel_mode_contains_lfe(m),
"channel_mode {m} should have LFE"
);
}
for &m in &[0u32, 1, 2, 3, 5, 7, 9, 11] {
assert!(
!channel_mode_contains_lfe(m),
"channel_mode {m} should not have LFE"
);
}
}
#[test]
fn channel_mode_centre_mapping() {
assert!(!channel_mode_contains_c(0));
assert!(!channel_mode_contains_c(1));
for &m in &[2u32, 3, 4, 5, 6, 7, 8, 9, 10, 11] {
assert!(
channel_mode_contains_c(m),
"channel_mode {m} should carry C"
);
}
}
#[test]
fn basic_metadata_minimal_no_extra_bits() {
let mut bw = BitWriter::new();
bw.write_u32(0x40, 7);
bw.write_bit(false);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let bm = parse_basic_metadata(&mut br, channel_mode::MONO).unwrap();
assert_eq!(bm.dialnorm_bits, 0x40);
assert!(!bm.more_basic_metadata);
assert!(bm.further_loudness_info.is_none());
assert!(bm.dc_block_on.is_none());
}
#[test]
fn basic_metadata_stereo_with_prev_dmx_info() {
let mut bw = BitWriter::new();
bw.write_u32(0, 7); bw.write_bit(true); bw.write_bit(false); bw.write_bit(true); bw.write_u32(5, 3); bw.write_u32(2, 2); bw.write_bit(false); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let bm = parse_basic_metadata(&mut br, channel_mode::STEREO).unwrap();
assert_eq!(bm.dialnorm_bits, 0);
assert!(bm.more_basic_metadata);
assert_eq!(bm.pre_dmixtyp_2ch, Some(5));
assert_eq!(bm.phase90_info_2ch, Some(2));
assert!(bm.dc_block_on.is_none());
}
#[test]
fn basic_metadata_5_1_with_dmx_coeff_and_lfe() {
let mut bw = BitWriter::new();
bw.write_u32(0, 7); bw.write_bit(true); bw.write_bit(false); bw.write_bit(true); bw.write_u32(2, 3); bw.write_u32(4, 3); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_bit(true); bw.write_u32(15, 5); bw.write_u32(1, 2); bw.write_bit(false); bw.write_bit(false); bw.write_u32(1, 2); bw.write_bit(true); bw.write_bit(false); bw.write_bit(false); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let bm = parse_basic_metadata(&mut br, channel_mode::FIVE_1).unwrap();
assert_eq!(bm.loro_centre_mixgain, Some(2));
assert_eq!(bm.loro_surround_mixgain, Some(4));
assert!(bm.loro_dmx_loud_corr.is_none());
assert!(bm.ltrt_centre_mixgain.is_none());
assert_eq!(bm.lfe_mixgain, Some(15));
assert_eq!(bm.preferred_dmx_method, Some(1));
assert_eq!(bm.phase90_info_mc, Some(1));
assert_eq!(bm.b_surround_attenuation_known, Some(true));
assert_eq!(bm.b_lfe_attenuation_known, Some(false));
}
#[test]
fn extended_metadata_no_flags() {
let mut bw = BitWriter::new();
bw.write_bit(false); bw.write_bit(false); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let em = parse_extended_metadata(&mut br, channel_mode::STEREO, false, false).unwrap();
assert!(em.event_probability.is_none());
assert!(em.b_l_active.is_none());
}
#[test]
fn extended_metadata_associated_mono_with_pan() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(0xAB, 8);
bw.write_bit(false); bw.write_bit(false); bw.write_u32(0xCD, 8); bw.write_bit(false); bw.write_bit(false); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let em = parse_extended_metadata(&mut br, channel_mode::MONO, true, false).unwrap();
assert_eq!(em.scale_main, Some(0xAB));
assert_eq!(em.pan_associated, Some(0xCD));
assert!(em.dialog_max_gain.is_none());
}
fn write_minimal_drc_absent(bw: &mut BitWriter) {
bw.write_bit(false);
}
fn write_minimal_de_absent(bw: &mut BitWriter) {
bw.write_bit(false);
}
#[test]
fn metadata_walker_minimal_iframe_mono_no_payload() {
let mut bw = BitWriter::new();
bw.write_u32(0x40, 7); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(2, 7); bw.write_bit(false); write_minimal_drc_absent(&mut bw);
write_minimal_de_absent(&mut bw);
bw.write_bit(false); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ctx = MetadataContext {
channel_mode: channel_mode::MONO,
b_iframe: true,
b_associated: false,
b_dialog: false,
frame_length: 1024,
};
let state = MetadataState::default();
let m = parse_metadata(&mut br, ctx, &state).unwrap();
assert_eq!(m.basic.dialnorm_bits, 0x40);
assert_eq!(m.tools_metadata_size, 2);
assert!(!m.drc.b_drc_present);
assert!(!m.dialog_enhancement.data_present);
assert!(!m.emdf_payloads_substream_present);
assert_eq!(m.tools_metadata_trailing_bits, 0);
}
#[test]
fn metadata_walker_more_bits_extension() {
let mut bw = BitWriter::new();
bw.write_u32(0, 7); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(2, 7); bw.write_bit(true); bw.write_u32(0, 3); bw.write_bit(false); write_minimal_drc_absent(&mut bw);
write_minimal_de_absent(&mut bw);
bw.write_bit(false); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ctx = MetadataContext {
channel_mode: channel_mode::MONO,
b_iframe: true,
b_associated: false,
b_dialog: false,
frame_length: 1024,
};
let m = parse_metadata(&mut br, ctx, &MetadataState::default()).unwrap();
assert_eq!(m.tools_metadata_size, 2);
}
#[test]
fn metadata_walker_trailing_bits_skipped() {
let mut bw = BitWriter::new();
bw.write_u32(0, 7); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(6, 7); bw.write_bit(false); write_minimal_drc_absent(&mut bw);
write_minimal_de_absent(&mut bw);
bw.write_u32(0xF, 4);
bw.write_bit(false); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ctx = MetadataContext {
channel_mode: channel_mode::MONO,
b_iframe: true,
b_associated: false,
b_dialog: false,
frame_length: 1024,
};
let m = parse_metadata(&mut br, ctx, &MetadataState::default()).unwrap();
assert_eq!(m.tools_metadata_trailing_bits, 4);
assert!(!m.emdf_payloads_substream_present);
}
#[test]
fn metadata_walker_dispatches_drc_with_curve_and_de() {
let mut bw = BitWriter::new();
bw.write_u32(0, 7); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(23, 7);
bw.write_bit(false); bw.write_bit(true); bw.write_u32(0, 3); bw.write_u32(0, 3); bw.write_bit(false); bw.write_bit(true); bw.write_u32(0, 3); bw.write_bit(false); bw.write_u32(0, 2); bw.write_bit(true); bw.write_u32(0, 2); bw.write_u32(0, 2); bw.write_u32(0, 3); bw.write_bit(false);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ctx = MetadataContext {
channel_mode: channel_mode::MONO,
b_iframe: true,
b_associated: false,
b_dialog: false,
frame_length: 512,
};
let m = parse_metadata(&mut br, ctx, &MetadataState::default()).unwrap();
assert_eq!(m.tools_metadata_size, 23);
assert!(m.drc.b_drc_present);
let cfg = m.drc.config.as_ref().expect("drc config present");
assert_eq!(cfg.modes.len(), 1);
assert!(m.dialog_enhancement.data_present);
let de_cfg = m.dialog_enhancement.config.expect("de config present");
assert_eq!(de_cfg.channel_config, 0);
assert_eq!(m.tools_metadata_trailing_bits, 0);
}
#[test]
fn metadata_walker_chains_prev_state_for_p_frame() {
let mut bw = BitWriter::new();
bw.write_u32(0, 7); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(7, 7); bw.write_bit(false); bw.write_bit(true); bw.write_bit(false); bw.write_u32(0, 2); bw.write_bit(true); bw.write_bit(false); bw.write_bit(false); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let prev_drc = DrcConfig {
drc_decoder_nr_modes: 0,
drc_eac3_profile: 0,
modes: vec![crate::drc::DrcDecoderMode {
drc_decoder_mode_id: 0,
drc_output_level_from: None,
drc_output_level_to: None,
drc_repeat_profile_flag: false,
drc_repeat_id: None,
drc_default_profile_flag: Some(true),
drc_compression_curve_flag: true,
compression_curve: None,
drc_gains_config: None,
}],
};
let prev_de = DeConfig {
method: crate::de::DeMethod::ChannelIndependent,
max_gain: 0,
channel_config: 0,
};
let state = MetadataState {
prev_drc_config: Some(prev_drc),
prev_de_config: Some(prev_de),
};
let ctx = MetadataContext {
channel_mode: channel_mode::MONO,
b_iframe: false,
b_associated: false,
b_dialog: false,
frame_length: 512,
};
let m = parse_metadata(&mut br, ctx, &state).unwrap();
assert!(m.drc.b_drc_present);
assert!(m.drc.config.is_none());
assert!(m.drc.data.is_some());
assert!(m.dialog_enhancement.data_present);
assert!(!m.dialog_enhancement.config_flag);
assert_eq!(m.tools_metadata_size, 7);
}
#[test]
fn metadata_walker_emdf_present_terminator_only_is_ok() {
let mut bw = BitWriter::new();
bw.write_u32(0, 7); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(2, 7); bw.write_bit(false); write_minimal_drc_absent(&mut bw);
write_minimal_de_absent(&mut bw);
bw.write_bit(true); bw.write_u32(0, 5);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ctx = MetadataContext {
channel_mode: channel_mode::MONO,
b_iframe: true,
b_associated: false,
b_dialog: false,
frame_length: 1024,
};
let m = parse_metadata(&mut br, ctx, &MetadataState::default()).unwrap();
assert!(m.emdf_payloads_substream_present);
let sub = m
.emdf_payloads_substream
.as_ref()
.expect("present flag set, payload missing");
assert!(sub.payloads.is_empty());
}
#[test]
fn metadata_walker_emdf_present_with_one_payload_round_trips() {
let mut bw = BitWriter::new();
bw.write_u32(0, 7); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(2, 7); bw.write_bit(false); write_minimal_drc_absent(&mut bw);
write_minimal_de_absent(&mut bw);
bw.write_bit(true); bw.write_u32(9, 5);
bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_bit(true); bw.write_u32(0, 8);
bw.write_bit(false); bw.write_u32(0, 5);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ctx = MetadataContext {
channel_mode: channel_mode::MONO,
b_iframe: true,
b_associated: false,
b_dialog: false,
frame_length: 1024,
};
let m = parse_metadata(&mut br, ctx, &MetadataState::default()).unwrap();
let sub = m.emdf_payloads_substream.as_ref().unwrap();
assert_eq!(sub.payloads.len(), 1);
assert_eq!(sub.payloads[0].emdf_payload_id, 9);
assert!(sub.payloads[0].payload_bytes.is_empty());
}
#[test]
fn further_loudness_info_minimum() {
let mut bw = BitWriter::new();
bw.write_u32(0, 2); bw.write_u32(0, 4); for _ in 0..11 {
bw.write_bit(false); }
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let v = parse_further_loudness_info(&mut br).unwrap();
assert_eq!(v.loudness_version, 0);
assert_eq!(v.loud_prac_type, 0);
assert!(v.loudrelgat.is_none());
assert!(v.loudmntry.is_none());
}
}