use crate::error::{Error, Result};
use alloc::vec::Vec;
use broadcast_common::len::{fit_bits, fit_u8};
const fn ones(n: u32) -> u8 {
((1u16 << n) - 1) as u8
}
fn f(v: u8, bits: u32, name: &'static str) -> Result<u8> {
Ok(fit_bits(u64::from(v), bits, name)? as u8)
}
fn n(len: usize, bits: u32, name: &'static str) -> Result<u8> {
Ok(fit_bits(len as u64, bits, name)? as u8)
}
pub(crate) struct Reader<'a> {
body: &'a [u8],
pos: usize,
what: &'static str,
}
impl<'a> Reader<'a> {
pub(crate) fn new(body: &'a [u8], what: &'static str) -> Self {
Self { body, pos: 0, what }
}
fn u8(&mut self) -> Result<u8> {
let b = *self.body.get(self.pos).ok_or(Error::BufferTooShort(
broadcast_common::error::BufferTooShort {
need: self.pos + 1,
have: self.body.len(),
what: self.what,
},
))?;
self.pos += 1;
Ok(b)
}
fn take(&mut self, len: usize) -> Result<&'a [u8]> {
let end = self.pos + len;
let s = self.body.get(self.pos..end).ok_or(Error::BufferTooShort(
broadcast_common::error::BufferTooShort {
need: end,
have: self.body.len(),
what: self.what,
},
))?;
self.pos = end;
Ok(s)
}
fn rest(&mut self) -> &'a [u8] {
let s = &self.body[self.pos..];
self.pos = self.body.len();
s
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MpegH3dAudio {
pub profile_level_indication: u8,
pub interactivity_enabled: bool,
pub compatible_sets: Option<Vec<u8>>,
pub reserved_flags: u8,
pub reserved_layout: u8,
pub reference_channel_layout: u8,
pub trailing: Vec<u8>,
}
impl MpegH3dAudio {
#[must_use]
pub fn new(
profile_level_indication: u8,
interactivity_enabled: bool,
compatible_sets: Option<Vec<u8>>,
reference_channel_layout: u8,
) -> Self {
Self {
profile_level_indication,
interactivity_enabled,
compatible_sets,
reserved_flags: ones(6),
reserved_layout: ones(2),
reference_channel_layout,
trailing: Vec::new(),
}
}
pub(crate) fn parse(r: &mut Reader<'_>) -> Result<Self> {
let profile_level_indication = r.u8()?;
let b1 = r.u8()?;
let b2 = r.u8()?;
let present = b1 & 0x40 == 0;
let compatible_sets = if present {
let cnt = r.u8()? as usize;
Some(r.take(cnt)?.to_vec())
} else {
None
};
Ok(Self {
profile_level_indication,
interactivity_enabled: b1 & 0x80 != 0,
compatible_sets,
reserved_flags: b1 & 0x3F,
reserved_layout: b2 >> 6,
reference_channel_layout: b2 & 0x3F,
trailing: r.rest().to_vec(),
})
}
pub(crate) fn write(&self, out: &mut Vec<u8>) -> Result<()> {
out.push(self.profile_level_indication);
out.push(
(u8::from(self.interactivity_enabled) << 7)
| (u8::from(self.compatible_sets.is_none()) << 6)
| f(self.reserved_flags, 6, "reserved")?,
);
out.push(
(f(self.reserved_layout, 2, "reserved")? << 6)
| f(self.reference_channel_layout, 6, "referenceChannelLayout")?,
);
if let Some(sets) = &self.compatible_sets {
out.push(fit_u8(sets.len(), "numCompatibleSets")?);
out.extend_from_slice(sets);
}
out.extend_from_slice(&self.trailing);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MpegH3dAudioConfig {
pub mpegh3da_config: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct PositionInteractivity {
pub reserved_min_az: u8,
pub min_az_offset: u8,
pub reserved_max_az: u8,
pub max_az_offset: u8,
pub reserved_min_el: u8,
pub min_el_offset: u8,
pub reserved_max_el: u8,
pub max_el_offset: u8,
pub min_dist_offset: u8,
pub max_dist_offset: u8,
}
impl PositionInteractivity {
#[must_use]
pub const fn new(
min_az_offset: u8,
max_az_offset: u8,
min_el_offset: u8,
max_el_offset: u8,
min_dist_offset: u8,
max_dist_offset: u8,
) -> Self {
Self {
reserved_min_az: 1,
min_az_offset,
reserved_max_az: 1,
max_az_offset,
reserved_min_el: 7,
min_el_offset,
reserved_max_el: 7,
max_el_offset,
min_dist_offset,
max_dist_offset,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct GainInteractivity {
pub reserved_min: u8,
pub min_gain: u8,
pub reserved_max: u8,
pub max_gain: u8,
}
impl GainInteractivity {
#[must_use]
pub const fn new(min_gain: u8, max_gain: u8) -> Self {
Self {
reserved_min: 3,
min_gain,
reserved_max: 7,
max_gain,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct SceneGroup {
pub reserved_id: u8,
pub group_id: u8,
pub reserved_flags: u8,
pub allow_on_off: bool,
pub default_on_off: bool,
pub reserved_kind: u8,
pub content_kind: u8,
pub position: Option<PositionInteractivity>,
pub gain: Option<GainInteractivity>,
pub content_language: Option<[u8; 3]>,
}
impl SceneGroup {
#[must_use]
pub const fn new(
group_id: u8,
allow_on_off: bool,
default_on_off: bool,
content_kind: u8,
) -> Self {
Self {
reserved_id: 1,
group_id,
reserved_flags: 7,
allow_on_off,
default_on_off,
reserved_kind: 15,
content_kind,
position: None,
gain: None,
content_language: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct SwitchGroupMember {
pub reserved: u8,
pub member_id: u8,
}
impl SwitchGroupMember {
#[must_use]
pub const fn new(member_id: u8) -> Self {
Self {
reserved: 1,
member_id,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct SwitchGroup {
pub reserved_id: u8,
pub switch_group_id: u8,
pub allow_on_off: bool,
pub default_on_off: bool,
pub reserved_members: u8,
pub members: Vec<SwitchGroupMember>,
pub reserved_default: u8,
pub default_group_id: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct PresetConditionDetail {
pub reserved: u8,
pub disable_gain_interactivity: bool,
pub disable_position_interactivity: bool,
pub gain: Option<u8>,
pub position: Option<PresetPosition>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct PresetPosition {
pub az_offset: u8,
pub reserved_el: u8,
pub el_offset: u8,
pub reserved_dist: u8,
pub dist_factor: u8,
}
impl PresetPosition {
#[must_use]
pub const fn new(az_offset: u8, el_offset: u8, dist_factor: u8) -> Self {
Self {
az_offset,
reserved_el: 3,
el_offset,
reserved_dist: 15,
dist_factor,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct PresetCondition {
pub group_id: u8,
pub detail: Option<PresetConditionDetail>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct GroupPreset {
pub reserved_id: u8,
pub preset_id: u8,
pub reserved_kind: u8,
pub preset_kind: u8,
pub reserved_conditions: u8,
pub conditions: Vec<PresetCondition>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MpegH3dAudioScene {
pub reserved_flags: u8,
pub scene_info_id: u8,
pub groups: Option<Vec<SceneGroup>>,
pub reserved_switch_groups: u8,
pub switch_groups: Option<Vec<SwitchGroup>>,
pub reserved_presets: u8,
pub presets: Option<Vec<GroupPreset>>,
pub reserved_groups: u8,
pub trailing: Vec<u8>,
}
impl MpegH3dAudioScene {
#[must_use]
pub fn new(
scene_info_id: u8,
groups: Option<Vec<SceneGroup>>,
switch_groups: Option<Vec<SwitchGroup>>,
presets: Option<Vec<GroupPreset>>,
) -> Self {
Self {
reserved_flags: ones(5),
scene_info_id,
groups,
reserved_switch_groups: 7,
switch_groups,
reserved_presets: 7,
presets,
reserved_groups: 1,
trailing: Vec::new(),
}
}
pub(crate) fn parse(r: &mut Reader<'_>) -> Result<Self> {
let flags = r.u8()?;
let scene_info_id = r.u8()?;
let (group_present, switch_present, preset_present) =
(flags & 0x80 != 0, flags & 0x40 != 0, flags & 0x20 != 0);
let mut reserved_groups = 1;
let groups = if group_present {
let b = r.u8()?;
reserved_groups = b >> 7;
let count = usize::from(b & 0x7F);
let mut v = Vec::with_capacity(count);
for _ in 0..count {
v.push(parse_group(r)?);
}
Some(v)
} else {
None
};
let mut reserved_switch_groups = 7;
let switch_groups = if switch_present {
let b = r.u8()?;
reserved_switch_groups = b >> 5;
let count = usize::from(b & 0x1F);
let mut v = Vec::with_capacity(count);
for _ in 0..count {
v.push(parse_switch_group(r)?);
}
Some(v)
} else {
None
};
let mut reserved_presets = 7;
let presets = if preset_present {
let b = r.u8()?;
reserved_presets = b >> 5;
let count = usize::from(b & 0x1F);
let mut v = Vec::with_capacity(count);
for _ in 0..count {
v.push(parse_preset(r)?);
}
Some(v)
} else {
None
};
Ok(Self {
reserved_flags: flags & 0x1F,
scene_info_id,
groups,
reserved_switch_groups,
switch_groups,
reserved_presets,
presets,
reserved_groups,
trailing: r.rest().to_vec(),
})
}
pub(crate) fn write(&self, out: &mut Vec<u8>) -> Result<()> {
out.push(
(u8::from(self.groups.is_some()) << 7)
| (u8::from(self.switch_groups.is_some()) << 6)
| (u8::from(self.presets.is_some()) << 5)
| f(self.reserved_flags, 5, "reserved")?,
);
out.push(self.scene_info_id);
if let Some(groups) = &self.groups {
out.push(
(f(self.reserved_groups, 1, "reserved")? << 7) | n(groups.len(), 7, "numGroups")?,
);
for g in groups {
write_group(g, out)?;
}
}
if let Some(sgs) = &self.switch_groups {
out.push(
(f(self.reserved_switch_groups, 3, "reserved")? << 5)
| n(sgs.len(), 5, "numSwitchGroups")?,
);
for sg in sgs {
write_switch_group(sg, out)?;
}
}
if let Some(ps) = &self.presets {
out.push(
(f(self.reserved_presets, 3, "reserved")? << 5)
| n(ps.len(), 5, "mae_numGroupPresets")?,
);
for p in ps {
write_preset(p, out)?;
}
}
out.extend_from_slice(&self.trailing);
Ok(())
}
}
fn parse_group(r: &mut Reader<'_>) -> Result<SceneGroup> {
let b0 = r.u8()?;
let b1 = r.u8()?;
let b2 = r.u8()?;
let (allow_pos, allow_gain, has_lang) = (b1 & 0x04 != 0, b1 & 0x02 != 0, b1 & 0x01 != 0);
let position = if allow_pos {
let a = r.u8()?;
let b = r.u8()?;
let c = r.u8()?;
let d = r.u8()?;
let e = r.u8()?;
Some(PositionInteractivity {
reserved_min_az: a >> 7,
min_az_offset: a & 0x7F,
reserved_max_az: b >> 7,
max_az_offset: b & 0x7F,
reserved_min_el: c >> 5,
min_el_offset: c & 0x1F,
reserved_max_el: d >> 5,
max_el_offset: d & 0x1F,
min_dist_offset: e >> 4,
max_dist_offset: e & 0x0F,
})
} else {
None
};
let gain = if allow_gain {
let a = r.u8()?;
let b = r.u8()?;
Some(GainInteractivity {
reserved_min: a >> 6,
min_gain: a & 0x3F,
reserved_max: b >> 5,
max_gain: b & 0x1F,
})
} else {
None
};
let content_language = if has_lang {
let l = r.take(3)?;
Some([l[0], l[1], l[2]])
} else {
None
};
Ok(SceneGroup {
reserved_id: b0 >> 7,
group_id: b0 & 0x7F,
reserved_flags: b1 >> 5,
allow_on_off: b1 & 0x10 != 0,
default_on_off: b1 & 0x08 != 0,
reserved_kind: b2 >> 4,
content_kind: b2 & 0x0F,
position,
gain,
content_language,
})
}
fn write_group(g: &SceneGroup, out: &mut Vec<u8>) -> Result<()> {
out.push((f(g.reserved_id, 1, "reserved")? << 7) | f(g.group_id, 7, "mae_groupID")?);
out.push(
(f(g.reserved_flags, 3, "reserved")? << 5)
| (u8::from(g.allow_on_off) << 4)
| (u8::from(g.default_on_off) << 3)
| (u8::from(g.position.is_some()) << 2)
| (u8::from(g.gain.is_some()) << 1)
| u8::from(g.content_language.is_some()),
);
out.push((f(g.reserved_kind, 4, "reserved")? << 4) | f(g.content_kind, 4, "mae_contentKind")?);
if let Some(p) = &g.position {
out.push(
(f(p.reserved_min_az, 1, "reserved")? << 7)
| f(p.min_az_offset, 7, "mae_interactivityMinAzOffset")?,
);
out.push(
(f(p.reserved_max_az, 1, "reserved")? << 7)
| f(p.max_az_offset, 7, "mae_interactivityMaxAzOffset")?,
);
out.push(
(f(p.reserved_min_el, 3, "reserved")? << 5)
| f(p.min_el_offset, 5, "mae_interactivityMinElOffset")?,
);
out.push(
(f(p.reserved_max_el, 3, "reserved")? << 5)
| f(p.max_el_offset, 5, "mae_interactivityMaxElOffset")?,
);
out.push(
(f(p.min_dist_offset, 4, "mae_interactivityMinDistOffset")? << 4)
| f(p.max_dist_offset, 4, "mae_interactivityMaxDistOffset")?,
);
}
if let Some(gn) = &g.gain {
out.push(
(f(gn.reserved_min, 2, "reserved")? << 6)
| f(gn.min_gain, 6, "mae_interactivityMinGain")?,
);
out.push(
(f(gn.reserved_max, 3, "reserved")? << 5)
| f(gn.max_gain, 5, "mae_interactivityMaxGain")?,
);
}
if let Some(l) = &g.content_language {
out.extend_from_slice(l);
}
Ok(())
}
fn parse_switch_group(r: &mut Reader<'_>) -> Result<SwitchGroup> {
let b0 = r.u8()?;
let b1 = r.u8()?;
let members_n = usize::from(b1 & 0x1F) + 1;
let mut members = Vec::with_capacity(members_n);
for _ in 0..members_n {
let m = r.u8()?;
members.push(SwitchGroupMember {
reserved: m >> 7,
member_id: m & 0x7F,
});
}
let d = r.u8()?;
Ok(SwitchGroup {
reserved_id: b0 >> 7,
switch_group_id: (b0 >> 2) & 0x1F,
allow_on_off: b0 & 0x02 != 0,
default_on_off: b0 & 0x01 != 0,
reserved_members: b1 >> 5,
members,
reserved_default: d >> 7,
default_group_id: d & 0x7F,
})
}
fn write_switch_group(sg: &SwitchGroup, out: &mut Vec<u8>) -> Result<()> {
if sg.members.is_empty() {
return Err(Error::InvalidDescriptor {
tag: super::mpeg2_extension::TAG,
reason: "switch group needs at least one member (mae_bsSwitchGroupNumMembers + 1)",
});
}
out.push(
(f(sg.reserved_id, 1, "reserved")? << 7)
| (f(sg.switch_group_id, 5, "mae_switchGroupID")? << 2)
| (u8::from(sg.allow_on_off) << 1)
| u8::from(sg.default_on_off),
);
out.push(
(f(sg.reserved_members, 3, "reserved")? << 5)
| n(sg.members.len() - 1, 5, "mae_bsSwitchGroupNumMembers")?,
);
for m in &sg.members {
out.push(
(f(m.reserved, 1, "reserved")? << 7) | f(m.member_id, 7, "mae_switchGroupMemberID")?,
);
}
out.push(
(f(sg.reserved_default, 1, "reserved")? << 7)
| f(sg.default_group_id, 7, "mae_switchGroupDefaultGroupID")?,
);
Ok(())
}
fn parse_preset(r: &mut Reader<'_>) -> Result<GroupPreset> {
let b0 = r.u8()?;
let b1 = r.u8()?;
let b2 = r.u8()?;
let cond_n = usize::from(b2 & 0x0F) + 1;
let mut conditions = Vec::with_capacity(cond_n);
for _ in 0..cond_n {
let c = r.u8()?;
let detail = if c & 1 != 0 {
let d = r.u8()?;
let (gain_flag, pos_flag) = (d & 0x04 != 0, d & 0x01 != 0);
let gain = if gain_flag { Some(r.u8()?) } else { None };
let position = if pos_flag {
let az = r.u8()?;
let el = r.u8()?;
let dist = r.u8()?;
Some(PresetPosition {
az_offset: az,
reserved_el: el >> 6,
el_offset: el & 0x3F,
reserved_dist: dist >> 4,
dist_factor: dist & 0x0F,
})
} else {
None
};
Some(PresetConditionDetail {
reserved: d >> 4,
disable_gain_interactivity: d & 0x08 != 0,
disable_position_interactivity: d & 0x02 != 0,
gain,
position,
})
} else {
None
};
conditions.push(PresetCondition {
group_id: c >> 1,
detail,
});
}
Ok(GroupPreset {
reserved_id: b0 >> 5,
preset_id: b0 & 0x1F,
reserved_kind: b1 >> 5,
preset_kind: b1 & 0x1F,
reserved_conditions: b2 >> 4,
conditions,
})
}
fn write_preset(p: &GroupPreset, out: &mut Vec<u8>) -> Result<()> {
if p.conditions.is_empty() {
return Err(Error::InvalidDescriptor {
tag: super::mpeg2_extension::TAG,
reason: "group preset needs at least one condition (mae_numGroupPresetConditions + 1)",
});
}
out.push((f(p.reserved_id, 3, "reserved")? << 5) | f(p.preset_id, 5, "mae_groupPresetID")?);
out.push(
(f(p.reserved_kind, 3, "reserved")? << 5) | f(p.preset_kind, 5, "mae_groupPresetKind")?,
);
out.push(
(f(p.reserved_conditions, 4, "reserved")? << 4)
| n(p.conditions.len() - 1, 4, "mae_numGroupPresetConditions")?,
);
for c in &p.conditions {
out.push((f(c.group_id, 7, "mae_groupPresetGroupID")? << 1) | u8::from(c.detail.is_some()));
if let Some(d) = &c.detail {
out.push(
(f(d.reserved, 4, "reserved")? << 4)
| (u8::from(d.disable_gain_interactivity) << 3)
| (u8::from(d.gain.is_some()) << 2)
| (u8::from(d.disable_position_interactivity) << 1)
| u8::from(d.position.is_some()),
);
if let Some(g) = d.gain {
out.push(g);
}
if let Some(pos) = &d.position {
out.push(pos.az_offset);
out.push(
(f(pos.reserved_el, 2, "reserved")? << 6)
| f(pos.el_offset, 6, "mae_groupPresetElOffset")?,
);
out.push(
(f(pos.reserved_dist, 4, "reserved")? << 4)
| f(pos.dist_factor, 4, "mae_groupPresetDistFactor")?,
);
}
}
}
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct TextLabelEntry {
pub reserved: u8,
pub id: u8,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct TextLabelLanguage {
pub language: [u8; 3],
pub reserved_groups: u8,
pub groups: Vec<TextLabelEntry>,
pub reserved_switch_groups: u8,
pub switch_groups: Vec<TextLabelEntry>,
pub reserved_presets: u8,
pub presets: Vec<TextLabelEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MpegH3dAudioTextLabel {
pub scene_info_id: u8,
pub reserved: u8,
pub languages: Vec<TextLabelLanguage>,
pub trailing: Vec<u8>,
}
fn parse_entries(r: &mut Reader<'_>, count: usize, id_bits: u32) -> Result<Vec<TextLabelEntry>> {
let mut v = Vec::with_capacity(count);
for _ in 0..count {
let b = r.u8()?;
let len = usize::from(r.u8()?);
v.push(TextLabelEntry {
reserved: b >> id_bits,
id: b & ((1u8 << id_bits) - 1),
data: r.take(len)?.to_vec(),
});
}
Ok(v)
}
fn write_entries(
entries: &[TextLabelEntry],
id_bits: u32,
id_name: &'static str,
len_name: &'static str,
out: &mut Vec<u8>,
) -> Result<()> {
for e in entries {
out.push((f(e.reserved, 8 - id_bits, "reserved")? << id_bits) | f(e.id, id_bits, id_name)?);
out.push(fit_u8(e.data.len(), len_name)?);
out.extend_from_slice(&e.data);
}
Ok(())
}
impl MpegH3dAudioTextLabel {
pub(crate) fn parse(r: &mut Reader<'_>) -> Result<Self> {
let scene_info_id = r.u8()?;
let b = r.u8()?;
let langs = usize::from(b & 0x0F);
let mut languages = Vec::with_capacity(langs);
for _ in 0..langs {
let l = r.take(3)?;
let language = [l[0], l[1], l[2]];
let g = r.u8()?;
let groups = parse_entries(r, usize::from(g & 0x7F), 7)?;
let s = r.u8()?;
let switch_groups = parse_entries(r, usize::from(s & 0x1F), 5)?;
let p = r.u8()?;
let presets = parse_entries(r, usize::from(p & 0x1F), 5)?;
languages.push(TextLabelLanguage {
language,
reserved_groups: g >> 7,
groups,
reserved_switch_groups: s >> 5,
switch_groups,
reserved_presets: p >> 5,
presets,
});
}
Ok(Self {
scene_info_id,
reserved: b >> 4,
languages,
trailing: r.rest().to_vec(),
})
}
pub(crate) fn write(&self, out: &mut Vec<u8>) -> Result<()> {
out.push(self.scene_info_id);
out.push(
(f(self.reserved, 4, "reserved")? << 4)
| n(self.languages.len(), 4, "numDescLanguages")?,
);
for l in &self.languages {
out.extend_from_slice(&l.language);
out.push(
(f(l.reserved_groups, 1, "reserved")? << 7)
| n(l.groups.len(), 7, "numGroupDescriptions")?,
);
write_entries(
&l.groups,
7,
"mae_descriptionGroupID",
"groupDescriptionDataLength",
out,
)?;
out.push(
(f(l.reserved_switch_groups, 3, "reserved")? << 5)
| n(l.switch_groups.len(), 5, "numSwitchGroupDescriptions")?,
);
write_entries(
&l.switch_groups,
5,
"mae_descriptionSwitchGroupID",
"switchGroupDescriptionDataLength",
out,
)?;
out.push(
(f(l.reserved_presets, 3, "reserved")? << 5)
| n(l.presets.len(), 5, "numGroupPresetsDescriptions")?,
);
write_entries(
&l.presets,
5,
"mae_descriptionGroupPresetID",
"groupPresetDescriptionDataLength",
out,
)?;
}
out.extend_from_slice(&self.trailing);
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MultiStreamGroup {
pub group_id: u8,
pub is_in_main_stream: bool,
pub auxiliary: Option<AuxiliaryLocation>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct AuxiliaryLocation {
pub is_in_ts: bool,
pub auxiliary_stream_id: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MultiStreamMain {
pub reserved_aux: u8,
pub num_auxiliary_streams: u8,
pub reserved_groups: u8,
pub groups: Vec<MultiStreamGroup>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MpegH3dAudioMultiStream {
pub this_stream_id: u8,
pub main: Option<MultiStreamMain>,
pub trailing: Vec<u8>,
}
impl MpegH3dAudioMultiStream {
pub(crate) fn parse(r: &mut Reader<'_>) -> Result<Self> {
let b0 = r.u8()?;
let main = if b0 & 0x80 != 0 {
let a = r.u8()?;
let g = r.u8()?;
let count = usize::from(g & 0x7F);
let mut groups = Vec::with_capacity(count);
for _ in 0..count {
let e = r.u8()?;
let in_main = e & 1 != 0;
let auxiliary = if in_main {
None
} else {
let x = r.u8()?;
Some(AuxiliaryLocation {
is_in_ts: x & 0x80 != 0,
auxiliary_stream_id: x & 0x7F,
})
};
groups.push(MultiStreamGroup {
group_id: e >> 1,
is_in_main_stream: in_main,
auxiliary,
});
}
Some(MultiStreamMain {
reserved_aux: a >> 7,
num_auxiliary_streams: a & 0x7F,
reserved_groups: g >> 7,
groups,
})
} else {
None
};
Ok(Self {
this_stream_id: b0 & 0x7F,
main,
trailing: r.rest().to_vec(),
})
}
pub(crate) fn write(&self, out: &mut Vec<u8>) -> Result<()> {
out.push((u8::from(self.main.is_some()) << 7) | f(self.this_stream_id, 7, "thisStreamID")?);
if let Some(m) = &self.main {
out.push(
(f(m.reserved_aux, 1, "reserved")? << 7)
| f(m.num_auxiliary_streams, 7, "numAuxiliaryStreams")?,
);
out.push(
(f(m.reserved_groups, 1, "reserved")? << 7)
| n(m.groups.len(), 7, "mae_numGroups")?,
);
for g in &m.groups {
if g.is_in_main_stream != g.auxiliary.is_none() {
return Err(Error::InvalidDescriptor {
tag: super::mpeg2_extension::TAG,
reason: "multi-stream group: auxiliary location must be present iff isInMainStream == 0",
});
}
out.push((f(g.group_id, 7, "mae_groupID")? << 1) | u8::from(g.is_in_main_stream));
if let Some(a) = &g.auxiliary {
out.push(
(u8::from(a.is_in_ts) << 7)
| f(a.auxiliary_stream_id, 7, "auxiliaryStreamID")?,
);
}
}
}
out.extend_from_slice(&self.trailing);
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum DrcScope {
Plain(u8),
Group {
kind: u8,
reserved: u8,
group_id: u8,
},
Preset {
reserved: u8,
preset_id: u8,
},
}
impl DrcScope {
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::Plain(_) => "no ID",
Self::Group { .. } => "mae_groupID",
Self::Preset { .. } => "mae_groupPresetID",
}
}
}
broadcast_common::impl_spec_display!(DrcScope);
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct DrcInstructions {
pub reserved_type: u8,
pub scope: DrcScope,
pub reserved_set: u8,
pub drc_set_id: u8,
pub reserved_downmix: u8,
pub downmix_id: u8,
pub reserved_additional: u8,
pub additional_downmix_ids: Vec<(u8, u8)>,
pub drc_set_effect: u16,
pub limiter_peak_target: Option<u8>,
pub target_loudness: Option<DrcTargetLoudness>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct DrcTargetLoudness {
pub reserved_upper: u8,
pub upper: u8,
pub lower: Option<(u8, u8)>,
pub reserved_depends: u8,
pub depends_on_drc_set: u8,
pub no_independent_use_or_reserved: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct LoudnessInfoEntry {
pub reserved_type: u8,
pub scope: DrcScope,
pub loudness_info: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct DownmixDefinition {
pub reserved: u8,
pub downmix_id: u8,
pub downmix_type: u8,
pub cicp_speaker_layout_idx: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct DrcLoudnessInfo {
pub reserved_drc: u8,
pub reserved_loudness: u8,
pub reserved_downmix: u8,
pub drc_instructions: Vec<DrcInstructions>,
pub loudness_infos: Vec<LoudnessInfoEntry>,
pub downmix_definitions: Vec<DownmixDefinition>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MpegH3dAudioDrcLoudness {
pub reserved: u8,
pub info: Option<DrcLoudnessInfo>,
pub trailing: Vec<u8>,
}
fn parse_scope(r: &mut Reader<'_>, kind: u8, group_types: &[u8]) -> Result<DrcScope> {
if group_types.contains(&kind) {
let b = r.u8()?;
Ok(DrcScope::Group {
kind,
reserved: b >> 7,
group_id: b & 0x7F,
})
} else if kind == 3 {
let b = r.u8()?;
Ok(DrcScope::Preset {
reserved: b >> 5,
preset_id: b & 0x1F,
})
} else {
Ok(DrcScope::Plain(kind))
}
}
fn write_scope(s: &DrcScope, group_types: &[u8], out_tail: &mut Vec<u8>) -> Result<u8> {
match *s {
DrcScope::Plain(k) => {
f(k, 2, "type")?;
if group_types.contains(&k) || k == 3 {
return Err(Error::InvalidDescriptor {
tag: super::mpeg2_extension::TAG,
reason: "DrcScope::Plain used with a type that requires an ID",
});
}
Ok(k)
}
DrcScope::Group {
kind,
reserved,
group_id,
} => {
if !group_types.contains(&kind) {
return Err(Error::InvalidDescriptor {
tag: super::mpeg2_extension::TAG,
reason: "DrcScope::Group type does not carry an mae_groupID here",
});
}
out_tail.push((f(reserved, 1, "reserved")? << 7) | f(group_id, 7, "mae_groupID")?);
Ok(kind)
}
DrcScope::Preset {
reserved,
preset_id,
} => {
out_tail
.push((f(reserved, 3, "reserved")? << 5) | f(preset_id, 5, "mae_groupPresetID")?);
Ok(3)
}
}
}
const DRC_GROUP_TYPES: &[u8] = &[2];
const LOUDNESS_GROUP_TYPES: &[u8] = &[1, 2];
impl MpegH3dAudioDrcLoudness {
pub(crate) fn parse(r: &mut Reader<'_>) -> Result<Self> {
let b0 = r.u8()?;
let info = if b0 & 1 != 0 {
let a = r.u8()?;
let l = r.u8()?;
let d = r.u8()?;
let mut drc_instructions = Vec::new();
for _ in 0..(a & 0x3F) {
drc_instructions.push(parse_drc_instructions(r)?);
}
let mut loudness_infos = Vec::new();
for _ in 0..(l & 0x3F) {
let t = r.u8()?;
let scope = parse_scope(r, t & 0x03, LOUDNESS_GROUP_TYPES)?;
let size = usize::from(r.u8()?);
loudness_infos.push(LoudnessInfoEntry {
reserved_type: t >> 2,
scope,
loudness_info: r.take(size)?.to_vec(),
});
}
let mut downmix_definitions = Vec::new();
for _ in 0..(d & 0x1F) {
let x = r.u8()?;
let y = r.u8()?;
downmix_definitions.push(DownmixDefinition {
reserved: x >> 7,
downmix_id: x & 0x7F,
downmix_type: y >> 6,
cicp_speaker_layout_idx: y & 0x3F,
});
}
Some(DrcLoudnessInfo {
reserved_drc: a >> 6,
reserved_loudness: l >> 6,
reserved_downmix: d >> 5,
drc_instructions,
loudness_infos,
downmix_definitions,
})
} else {
None
};
Ok(Self {
reserved: b0 >> 1,
info,
trailing: r.rest().to_vec(),
})
}
pub(crate) fn write(&self, out: &mut Vec<u8>) -> Result<()> {
out.push((f(self.reserved, 7, "reserved")? << 1) | u8::from(self.info.is_some()));
if let Some(i) = &self.info {
out.push(
(f(i.reserved_drc, 2, "reserved")? << 6)
| n(i.drc_instructions.len(), 6, "drcInstructionsUniDrcCount")?,
);
out.push(
(f(i.reserved_loudness, 2, "reserved")? << 6)
| n(i.loudness_infos.len(), 6, "loudnessInfoCount")?,
);
out.push(
(f(i.reserved_downmix, 3, "reserved")? << 5)
| n(i.downmix_definitions.len(), 5, "downmixIdCount")?,
);
for d in &i.drc_instructions {
write_drc_instructions(d, out)?;
}
for e in &i.loudness_infos {
let mut tail = Vec::new();
let kind = write_scope(&e.scope, LOUDNESS_GROUP_TYPES, &mut tail)?;
out.push((f(e.reserved_type, 6, "reserved")? << 2) | kind);
out.extend_from_slice(&tail);
out.push(fit_u8(e.loudness_info.len(), "loudnessInfo_size")?);
out.extend_from_slice(&e.loudness_info);
}
for m in &i.downmix_definitions {
out.push((f(m.reserved, 1, "reserved")? << 7) | f(m.downmix_id, 7, "downmixId")?);
out.push(
(f(m.downmix_type, 2, "downmixType")? << 6)
| f(m.cicp_speaker_layout_idx, 6, "CICPspeakerLayoutIdx")?,
);
}
}
out.extend_from_slice(&self.trailing);
Ok(())
}
}
fn parse_drc_instructions(r: &mut Reader<'_>) -> Result<DrcInstructions> {
let t = r.u8()?;
let scope = parse_scope(r, t & 0x03, DRC_GROUP_TYPES)?;
let s = r.u8()?;
let dm = r.u8()?;
let c = r.u8()?;
let (limiter_present, loud_present) = (c & 0x02 != 0, c & 0x01 != 0);
let mut additional_downmix_ids = Vec::new();
for _ in 0..((c >> 2) & 0x07) {
let x = r.u8()?;
additional_downmix_ids.push((x >> 7, x & 0x7F));
}
let hi = r.u8()?;
let lo = r.u8()?;
let limiter_peak_target = if limiter_present { Some(r.u8()?) } else { None };
let target_loudness = if loud_present {
let u = r.u8()?;
let lower = if u & 1 != 0 {
let x = r.u8()?;
Some((x >> 6, x & 0x3F))
} else {
None
};
let dep = r.u8()?;
Some(DrcTargetLoudness {
reserved_upper: u >> 7,
upper: (u >> 1) & 0x3F,
lower,
reserved_depends: dep >> 7,
depends_on_drc_set: (dep >> 1) & 0x3F,
no_independent_use_or_reserved: dep & 1,
})
} else {
None
};
Ok(DrcInstructions {
reserved_type: t >> 2,
scope,
reserved_set: s >> 6,
drc_set_id: s & 0x3F,
reserved_downmix: dm >> 7,
downmix_id: dm & 0x7F,
reserved_additional: c >> 5,
additional_downmix_ids,
drc_set_effect: u16::from_be_bytes([hi, lo]),
limiter_peak_target,
target_loudness,
})
}
fn write_drc_instructions(d: &DrcInstructions, out: &mut Vec<u8>) -> Result<()> {
let mut tail = Vec::new();
let kind = write_scope(&d.scope, DRC_GROUP_TYPES, &mut tail)?;
out.push((f(d.reserved_type, 6, "reserved")? << 2) | kind);
out.extend_from_slice(&tail);
out.push((f(d.reserved_set, 2, "reserved")? << 6) | f(d.drc_set_id, 6, "drcSetId")?);
out.push((f(d.reserved_downmix, 1, "reserved")? << 7) | f(d.downmix_id, 7, "downmixId")?);
out.push(
(f(d.reserved_additional, 3, "reserved")? << 5)
| (n(
d.additional_downmix_ids.len(),
3,
"additionalDownmixIdCount",
)? << 2)
| (u8::from(d.limiter_peak_target.is_some()) << 1)
| u8::from(d.target_loudness.is_some()),
);
for &(res, id) in &d.additional_downmix_ids {
out.push((f(res, 1, "reserved")? << 7) | f(id, 7, "additionalDownmixId")?);
}
out.extend_from_slice(&d.drc_set_effect.to_be_bytes());
if let Some(p) = d.limiter_peak_target {
out.push(p);
}
if let Some(t) = &d.target_loudness {
out.push(
(f(t.reserved_upper, 1, "reserved")? << 7)
| (f(t.upper, 6, "bsDrcSetTargetLoudnessValueUpper")? << 1)
| u8::from(t.lower.is_some()),
);
if let Some((res, lower)) = t.lower {
out.push(
(f(res, 2, "reserved")? << 6) | f(lower, 6, "bsDrcSetTargetLoudnessValueLower")?,
);
}
out.push(
(f(t.reserved_depends, 1, "reserved")? << 7)
| (f(t.depends_on_drc_set, 6, "dependsOnDrcSet")? << 1)
| f(t.no_independent_use_or_reserved, 1, "noIndependentUse")?,
);
}
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MpegH3dAudioCommand {
pub mhas_packet: Vec<u8>,
}
impl MpegH3dAudioConfig {
#[must_use]
pub fn new(mpegh3da_config: Vec<u8>) -> Self {
Self { mpegh3da_config }
}
}
impl SwitchGroup {
#[must_use]
pub fn new(
switch_group_id: u8,
allow_on_off: bool,
default_on_off: bool,
members: Vec<SwitchGroupMember>,
default_group_id: u8,
) -> Self {
Self {
reserved_id: ones(1),
switch_group_id,
allow_on_off,
default_on_off,
reserved_members: ones(3),
members,
reserved_default: ones(1),
default_group_id,
}
}
}
impl PresetConditionDetail {
#[must_use]
pub const fn new(
disable_gain_interactivity: bool,
disable_position_interactivity: bool,
gain: Option<u8>,
position: Option<PresetPosition>,
) -> Self {
Self {
reserved: ones(4),
disable_gain_interactivity,
disable_position_interactivity,
gain,
position,
}
}
}
impl PresetCondition {
#[must_use]
pub const fn new(group_id: u8, detail: Option<PresetConditionDetail>) -> Self {
Self { group_id, detail }
}
}
impl GroupPreset {
#[must_use]
pub fn new(preset_id: u8, preset_kind: u8, conditions: Vec<PresetCondition>) -> Self {
Self {
reserved_id: ones(3),
preset_id,
reserved_kind: ones(3),
preset_kind,
reserved_conditions: ones(4),
conditions,
}
}
}
impl TextLabelEntry {
#[must_use]
pub fn group(id: u8, data: Vec<u8>) -> Self {
Self {
reserved: ones(1),
id,
data,
}
}
#[must_use]
pub fn switch_group_or_preset(id: u8, data: Vec<u8>) -> Self {
Self {
reserved: ones(3),
id,
data,
}
}
}
impl TextLabelLanguage {
#[must_use]
pub fn new(
language: [u8; 3],
groups: Vec<TextLabelEntry>,
switch_groups: Vec<TextLabelEntry>,
presets: Vec<TextLabelEntry>,
) -> Self {
Self {
language,
reserved_groups: ones(1),
groups,
reserved_switch_groups: ones(3),
switch_groups,
reserved_presets: ones(3),
presets,
}
}
}
impl MpegH3dAudioTextLabel {
#[must_use]
pub fn new(scene_info_id: u8, languages: Vec<TextLabelLanguage>) -> Self {
Self {
scene_info_id,
reserved: ones(4),
languages,
trailing: Vec::new(),
}
}
}
impl AuxiliaryLocation {
#[must_use]
pub const fn new(is_in_ts: bool, auxiliary_stream_id: u8) -> Self {
Self {
is_in_ts,
auxiliary_stream_id,
}
}
}
impl MultiStreamGroup {
#[must_use]
pub const fn in_main_stream(group_id: u8) -> Self {
Self {
group_id,
is_in_main_stream: true,
auxiliary: None,
}
}
#[must_use]
pub const fn in_auxiliary_stream(group_id: u8, auxiliary: AuxiliaryLocation) -> Self {
Self {
group_id,
is_in_main_stream: false,
auxiliary: Some(auxiliary),
}
}
}
impl MultiStreamMain {
#[must_use]
pub fn new(num_auxiliary_streams: u8, groups: Vec<MultiStreamGroup>) -> Self {
Self {
reserved_aux: ones(1),
num_auxiliary_streams,
reserved_groups: ones(1),
groups,
}
}
}
impl MpegH3dAudioMultiStream {
#[must_use]
pub fn new(this_stream_id: u8, main: Option<MultiStreamMain>) -> Self {
Self {
this_stream_id,
main,
trailing: Vec::new(),
}
}
}
impl DrcScope {
#[must_use]
pub const fn group(kind: u8, group_id: u8) -> Self {
Self::Group {
kind,
reserved: ones(1),
group_id,
}
}
#[must_use]
pub const fn preset(preset_id: u8) -> Self {
Self::Preset {
reserved: ones(3),
preset_id,
}
}
}
impl DrcInstructions {
#[must_use]
pub fn new(scope: DrcScope, drc_set_id: u8, downmix_id: u8, drc_set_effect: u16) -> Self {
Self {
reserved_type: ones(6),
scope,
reserved_set: ones(2),
drc_set_id,
reserved_downmix: ones(1),
downmix_id,
reserved_additional: ones(3),
additional_downmix_ids: Vec::new(),
drc_set_effect,
limiter_peak_target: None,
target_loudness: None,
}
}
}
impl DrcTargetLoudness {
#[must_use]
pub const fn new(
upper: u8,
depends_on_drc_set: u8,
no_independent_use_or_reserved: u8,
) -> Self {
Self {
reserved_upper: ones(1),
upper,
lower: None,
reserved_depends: ones(1),
depends_on_drc_set,
no_independent_use_or_reserved,
}
}
}
impl LoudnessInfoEntry {
#[must_use]
pub fn new(scope: DrcScope, loudness_info: Vec<u8>) -> Self {
Self {
reserved_type: ones(6),
scope,
loudness_info,
}
}
}
impl DownmixDefinition {
#[must_use]
pub const fn new(downmix_id: u8, downmix_type: u8, cicp_speaker_layout_idx: u8) -> Self {
Self {
reserved: ones(1),
downmix_id,
downmix_type,
cicp_speaker_layout_idx,
}
}
}
impl DrcLoudnessInfo {
#[must_use]
pub fn new(
drc_instructions: Vec<DrcInstructions>,
loudness_infos: Vec<LoudnessInfoEntry>,
downmix_definitions: Vec<DownmixDefinition>,
) -> Self {
Self {
reserved_drc: ones(2),
reserved_loudness: ones(2),
reserved_downmix: ones(3),
drc_instructions,
loudness_infos,
downmix_definitions,
}
}
}
impl MpegH3dAudioDrcLoudness {
#[must_use]
pub fn new(info: Option<DrcLoudnessInfo>) -> Self {
Self {
reserved: ones(7),
info,
trailing: Vec::new(),
}
}
}
impl MpegH3dAudioCommand {
#[must_use]
pub fn new(mhas_packet: Vec<u8>) -> Self {
Self { mhas_packet }
}
}