1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-06-11 01:19:21 +00:00

feat(opus): add opus box

This commit is contained in:
Stuart Woodbury 2023-07-13 10:09:27 -04:00
parent 2fb21bbbc8
commit b72a26b3f5
2 changed files with 175 additions and 1 deletions

View file

@ -85,6 +85,7 @@ pub(crate) mod moov;
pub(crate) mod mp4a;
pub(crate) mod mvex;
pub(crate) mod mvhd;
pub(crate) mod opus;
pub(crate) mod smhd;
pub(crate) mod stbl;
pub(crate) mod stco;
@ -206,7 +207,10 @@ boxtype! {
DayBox => 0xa9646179,
CovrBox => 0x636f7672,
DescBox => 0x64657363,
WideBox => 0x77696465
WideBox => 0x77696465,
DopsBox => 0x644F7073,
OpusBox => 0x4F707573
}
pub trait Mp4Box: Sized {

170
src/mp4box/opus.rs Normal file
View file

@ -0,0 +1,170 @@
use crate::mp4box::*;
use crate::Mp4Box;
use serde::Serialize;
// taken from the following sources
// - https://opus-codec.org/docs/opus_in_isobmff.html
// - chromium source code: box_definitions.h - OpusSpecificBox
// - async-mp4 crate: https://github.com/Wicpar/async-mp4/blob/master/src/mp4box/dops.rs
// this OpusBox is a combination of the AudioSampleEntry box and OpusSpecificBox
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct OpusBox {
pub data_reference_index: u16,
pub channelcount: u16,
pub samplesize: u16,
#[serde(with = "value_u32")]
pub samplerate: FixedPointU16,
pub dops: DopsBox,
}
impl Mp4Box for OpusBox {
fn box_type(&self) -> BoxType {
BoxType::OpusBox
}
fn box_size(&self) -> u64 {
// the +19 is for DopsBox
36 + 19
}
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
Ok(format!("{self:?}"))
}
}
impl<W: Write> WriteBox<&mut W> for OpusBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let mut written = 0;
written += BoxHeader::new(self.box_type(), self.box_size()).write(writer)?;
writer.write_u32::<BigEndian>(0)?; // reserved
written += 4;
writer.write_u16::<BigEndian>(0)?; // reserved
written += 2;
writer.write_u16::<BigEndian>(self.data_reference_index)?;
written += 2;
writer.write_u16::<BigEndian>(0)?; // reserved
written += 2;
writer.write_u16::<BigEndian>(0)?; // reserved
written += 2;
writer.write_u32::<BigEndian>(0)?; // reserved
written += 4;
writer.write_u16::<BigEndian>(self.channelcount)?;
written += 2;
writer.write_u16::<BigEndian>(self.samplesize)?;
written += 2;
writer.write_u32::<BigEndian>(0)?; // reserved
written += 4;
writer.write_u32::<BigEndian>(self.samplerate.raw_value())?;
written += 4;
written += self.dops.write_box(writer)?;
assert_eq!(written, self.box_size());
Ok(written)
}
}
// https://github.com/Wicpar/async-mp4/blob/master/src/mp4box/dops.rs
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct DopsBox {
pub version: u8,
pub pre_skip: u16,
pub input_sample_rate: u32,
pub output_gain: i16,
pub channel_mapping_family: ChannelMappingFamily,
}
impl Mp4Box for DopsBox {
fn box_type(&self) -> BoxType {
BoxType::DopsBox
}
fn box_size(&self) -> u64 {
// if channel_mapping_family is updates to support more than 2 channels,
// box_size could change, depending on the channel mapping.
19
}
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
Ok(format!("{self:?}"))
}
}
// https://github.com/Wicpar/async-mp4/blob/master/src/mp4box/dops.rs
impl<W: Write> WriteBox<&mut W> for DopsBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let mut written = 0;
written += BoxHeader::new(self.box_type(), self.box_size()).write(writer)?;
writer.write_u8(self.version)?;
written += 1;
let num_channels = match self.channel_mapping_family {
ChannelMappingFamily::Family0 { stereo } => match stereo {
true => 2,
false => 1,
},
};
writer.write_u8(num_channels)?;
written += 1;
writer.write_u16::<BigEndian>(self.pre_skip)?;
written += 2;
writer.write_u32::<BigEndian>(self.input_sample_rate)?;
written += 4;
writer.write_i16::<BigEndian>(self.output_gain)?;
written += 2;
// channel mapping family 0
writer.write_u8(0)?;
written += 1;
// todo: StreamCount? CoupledCount? ChannelMapping?
assert_eq!(written, self.box_size());
Ok(written)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum ChannelMappingFamily {
Family0 { stereo: bool },
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_opus_writer() {
let dops = DopsBox {
version: 0,
pre_skip: 1,
input_sample_rate: 2,
output_gain: 3,
channel_mapping_family: ChannelMappingFamily::Family0 { stereo: false },
};
let opus = OpusBox {
data_reference_index: 1,
channelcount: 1,
samplesize: 2,
samplerate: FixedPointU16::new(48000),
dops,
};
let mut buffer = Vec::<u8>::new();
opus.write_box(&mut buffer).expect("write_box failed");
}
}