mirror of
https://github.com/alfg/mp4-rust.git
synced 2025-01-05 09:58:40 +00:00
feat(opus): add opus box
This commit is contained in:
parent
2fb21bbbc8
commit
b72a26b3f5
2 changed files with 175 additions and 1 deletions
|
@ -85,6 +85,7 @@ pub(crate) mod moov;
|
||||||
pub(crate) mod mp4a;
|
pub(crate) mod mp4a;
|
||||||
pub(crate) mod mvex;
|
pub(crate) mod mvex;
|
||||||
pub(crate) mod mvhd;
|
pub(crate) mod mvhd;
|
||||||
|
pub(crate) mod opus;
|
||||||
pub(crate) mod smhd;
|
pub(crate) mod smhd;
|
||||||
pub(crate) mod stbl;
|
pub(crate) mod stbl;
|
||||||
pub(crate) mod stco;
|
pub(crate) mod stco;
|
||||||
|
@ -206,7 +207,10 @@ boxtype! {
|
||||||
DayBox => 0xa9646179,
|
DayBox => 0xa9646179,
|
||||||
CovrBox => 0x636f7672,
|
CovrBox => 0x636f7672,
|
||||||
DescBox => 0x64657363,
|
DescBox => 0x64657363,
|
||||||
WideBox => 0x77696465
|
WideBox => 0x77696465,
|
||||||
|
DopsBox => 0x644F7073,
|
||||||
|
OpusBox => 0x4F707573
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Mp4Box: Sized {
|
pub trait Mp4Box: Sized {
|
||||||
|
|
170
src/mp4box/opus.rs
Normal file
170
src/mp4box/opus.rs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue