1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Opus in ISOBMFF — `Opus` AudioSampleEntry + `dOps` config box.
//!
//! "Encapsulation of Opus in ISO Base Media File Format"
//! (<https://opus-codec.org/docs/opus_in_isobmff.html>, §4.3.2 OpusSpecificBox).
//!
//! Unlike the Ogg `OpusHead` (RFC 7845), `dOps` fields are **big-endian** and
//! carry **no** magic signature.
use crate::error::{Error, Result};
use alloc::vec::Vec;
use broadcast_common::len::{need_in, need_out};
use broadcast_common::{Parse, Serialize};
/// FourCC of the Opus config box.
pub const DOPS_FOURCC: [u8; 4] = *b"dOps";
/// FourCC of the Opus sample entry.
pub const OPUS_FOURCC: [u8; 4] = *b"Opus";
/// Fixed length of the `dOps` body up to (and excluding) the channel-mapping table.
const DOPS_FIXED_LEN: usize = 11;
/// OpusSpecificBox (`dOps` box body) — Opus-in-ISOBMFF §4.3.2.
///
/// Layout: `Version(8)` | `OutputChannelCount(8)` | `PreSkip(16)` |
/// `InputSampleRate(32)` | `OutputGain(16, signed 8.8)` |
/// `ChannelMappingFamily(8)`; when the family is non-zero, a channel-mapping
/// table follows (`StreamCount(8)`, `CoupledCount(8)`, `ChannelMapping[Nch]`).
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct OpusSpecificBox {
/// `Version` (0).
pub version: u8,
/// `OutputChannelCount`.
pub output_channel_count: u8,
/// `PreSkip` samples at 48 kHz.
pub pre_skip: u16,
/// `InputSampleRate` in Hz (original rate; playback is always 48 kHz).
pub input_sample_rate: u32,
/// `OutputGain` (signed Q7.8 fixed-point, dB).
pub output_gain: i16,
/// `ChannelMappingFamily`.
pub channel_mapping_family: u8,
/// Channel-mapping table (`StreamCount`, `CoupledCount`, `ChannelMapping[]`),
/// present verbatim iff `channel_mapping_family != 0`.
pub channel_mapping: Option<ChannelMappingTable>,
}
/// Opus channel-mapping table (present when `ChannelMappingFamily != 0`).
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ChannelMappingTable {
/// `StreamCount`.
pub stream_count: u8,
/// `CoupledCount`.
pub coupled_count: u8,
/// `ChannelMapping[OutputChannelCount]`.
pub channel_mapping: Vec<u8>,
}
impl OpusSpecificBox {
/// RFC 6381 codec string — always the literal `"Opus"`.
pub fn rfc6381(&self) -> &'static str {
"Opus"
}
}
impl<'a> Parse<'a> for OpusSpecificBox {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
need_in(bytes, DOPS_FIXED_LEN, "dOps body")?;
let version = bytes[0];
let output_channel_count = bytes[1];
let pre_skip = u16::from_be_bytes([bytes[2], bytes[3]]);
let input_sample_rate = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
let output_gain = i16::from_be_bytes([bytes[8], bytes[9]]);
let channel_mapping_family = bytes[10];
let channel_mapping = if channel_mapping_family != 0 {
need_in(bytes, DOPS_FIXED_LEN + 2, "dOps channel mapping")?;
let stream_count = bytes[11];
let coupled_count = bytes[12];
let map_start = DOPS_FIXED_LEN + 2;
// `ChannelMapping[OutputChannelCount]` (Opus-in-ISOBMFF §4.3.2):
// the table's width is derived from `OutputChannelCount`, so a short
// tail is truncated input — accepting it would parse a shorter map
// than the box declares and re-serialize a different box.
let map_end = map_start + output_channel_count as usize;
need_in(bytes, map_end, "dOps ChannelMapping")?;
Some(ChannelMappingTable {
stream_count,
coupled_count,
channel_mapping: bytes[map_start..map_end].to_vec(),
})
} else {
None
};
Ok(Self {
version,
output_channel_count,
pre_skip,
input_sample_rate,
output_gain,
channel_mapping_family,
channel_mapping,
})
}
}
impl Serialize for OpusSpecificBox {
type Error = Error;
fn serialized_len(&self) -> usize {
let mut n = DOPS_FIXED_LEN;
if let Some(ref m) = self.channel_mapping {
n += 2 + m.channel_mapping.len();
}
n
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let need = self.serialized_len();
need_out(buf, need)?;
buf[0] = self.version;
buf[1] = self.output_channel_count;
buf[2..4].copy_from_slice(&self.pre_skip.to_be_bytes());
buf[4..8].copy_from_slice(&self.input_sample_rate.to_be_bytes());
buf[8..10].copy_from_slice(&self.output_gain.to_be_bytes());
buf[10] = self.channel_mapping_family;
if let Some(ref m) = self.channel_mapping {
buf[11] = m.stream_count;
buf[12] = m.coupled_count;
buf[13..13 + m.channel_mapping.len()].copy_from_slice(&m.channel_mapping);
}
Ok(need)
}
}