1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2025-04-15 08:14:15 +00:00

opus support

This commit is contained in:
damitha 2025-03-18 21:59:22 +11:00
parent 58c8dc1af4
commit b2caeaf43c
3 changed files with 339 additions and 2 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 sidx;
pub(crate) mod smhd;
pub(crate) mod stbl;
@ -130,6 +131,7 @@ pub use moov::MoovBox;
pub use mp4a::Mp4aBox;
pub use mvex::MvexBox;
pub use mvhd::MvhdBox;
pub use opus::OpusBox;
pub use sidx::SidxBox;
pub use smhd::SmhdBox;
pub use stbl::StblBox;
@ -185,6 +187,7 @@ macro_rules! boxtype {
}
boxtype! {
DopsBox => 0x644f7073,
FtypBox => 0x66747970,
MvhdBox => 0x6d766864,
MfhdBox => 0x6d666864,
@ -193,6 +196,7 @@ boxtype! {
MoovBox => 0x6d6f6f76,
MvexBox => 0x6d766578,
MehdBox => 0x6d656864,
OpusBox => 0x4f707573,
TrexBox => 0x74726578,
EmsgBox => 0x656d7367,
MoofBox => 0x6d6f6f66,

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

@ -0,0 +1,322 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use serde::Serialize;
use std::io::{Read, Seek, Write};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct OpusBox {
pub data_reference_index: u16,
pub channel_count: u16,
pub sample_size: u16,
#[serde(with = "value_u32")]
pub sample_rate: FixedPointU16,
pub dops_box: Option<DopsBox>,
}
impl Default for OpusBox {
fn default() -> Self {
Self {
data_reference_index: 0,
channel_count: 2,
sample_size: 16,
sample_rate: FixedPointU16::new(48000),
dops_box: Some(DopsBox::default()),
}
}
}
impl OpusBox {
pub fn get_type(&self) -> BoxType {
BoxType::OpusBox
}
pub fn get_size(&self) -> u64 {
let mut size = HEADER_SIZE + 28;
if let Some(ref dops_box) = self.dops_box {
size += dops_box.box_size();
}
size
}
}
impl Mp4Box for OpusBox {
fn box_type(&self) -> BoxType {
self.get_type()
}
fn box_size(&self) -> u64 {
self.get_size()
}
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!(
"channel_count={} sample_size={} sample_rate={}",
self.channel_count,
self.sample_size,
self.sample_rate.value()
);
Ok(s)
}
}
impl<R: Read + Seek> ReadBox<&mut R> for OpusBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
reader.read_u32::<BigEndian>()?; // reserved
reader.read_u16::<BigEndian>()?; // reserved
let data_reference_index = reader.read_u16::<BigEndian>()?;
reader.read_u16::<BigEndian>()?; // reserved
reader.read_u16::<BigEndian>()?; // reserved
reader.read_u32::<BigEndian>()?; // reserved
let channel_count = reader.read_u16::<BigEndian>()?;
let sample_size = reader.read_u16::<BigEndian>()?;
reader.read_u32::<BigEndian>()?; // pre-defined, reserved
let sample_rate = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"opus box contains a box with a larger size than it",
));
}
let mut dops_box = None;
if name == BoxType::DopsBox {
dops_box = Some(DopsBox::read_box(reader, s)?);
}
skip_bytes_to(reader, start + size)?;
Ok(OpusBox {
data_reference_index,
channel_count,
sample_size,
sample_rate,
dops_box,
})
}
}
impl<W: Write> WriteBox<&mut W> for OpusBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(self.data_reference_index)?;
writer.write_u64::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(self.channel_count)?;
writer.write_u16::<BigEndian>(self.sample_size)?;
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u32::<BigEndian>(self.sample_rate.raw_value())?;
if let Some(ref dops_box) = self.dops_box {
dops_box.write_box(writer)?;
}
Ok(size)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct DopsBox {
pub version: u8,
pub output_channel_count: u8,
pub pre_skip: u16,
pub input_sample_rate: u32,
pub output_gain: i16,
pub channel_mapping_family: u8,
pub channel_mapping_table: Option<ChannelMappingTable>,
}
impl Default for DopsBox {
fn default() -> Self {
Self {
version: 0,
output_channel_count: 2,
pre_skip: 16,
input_sample_rate: 0,
output_gain: -1,
channel_mapping_family: 0,
channel_mapping_table: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ChannelMappingTable {
pub stream_count: u8,
pub coupled_count: u8,
pub channel_mapping: Vec<u8>,
}
impl Default for ChannelMappingTable {
fn default() -> Self {
Self {
stream_count: 0,
coupled_count: 2,
channel_mapping: Vec::new(),
}
}
}
impl Mp4Box for DopsBox {
fn box_type(&self) -> BoxType {
BoxType::DopsBox
}
fn box_size(&self) -> u64 {
let mut channel_table_size = 0;
if self.channel_mapping_family != 0 {
channel_table_size = self.output_channel_count as u64 + 2;
}
HEADER_SIZE + 11 + channel_table_size
}
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
Ok(String::new())
}
}
impl<R: Read + Seek> ReadBox<&mut R> for DopsBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
let version = reader.read_u8()?;
let output_channel_count = reader.read_u8()?;
let pre_skip = reader.read_u16::<BigEndian>()?;
let input_sample_rate = reader.read_u32::<BigEndian>()?;
let output_gain = reader.read_i16::<BigEndian>()?;
let channel_mapping_family = reader.read_u8()?;
let mut channel_mapping_table = None;
if channel_mapping_family != 0 {
let stream_count = reader.read_u8()?;
let coupled_count = reader.read_u8()?;
let mut channel_mapping = Vec::new();
for _ in 0..output_channel_count {
channel_mapping.push(reader.read_u8()?);
}
channel_mapping_table = Some(ChannelMappingTable {
stream_count,
coupled_count,
channel_mapping,
});
}
skip_bytes_to(reader, start + size)?;
Ok(DopsBox {
version,
output_channel_count,
pre_skip,
input_sample_rate,
output_gain,
channel_mapping_family,
channel_mapping_table,
})
}
}
impl<W: Write> WriteBox<&mut W> for DopsBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
writer.write_u8(self.version)?;
writer.write_u8(self.output_channel_count)?;
writer.write_u16::<BigEndian>(self.pre_skip)?;
writer.write_u32::<BigEndian>(self.input_sample_rate)?;
writer.write_i16::<BigEndian>(self.output_gain)?;
writer.write_u8(self.channel_mapping_family)?;
if self.channel_mapping_family != 0 {
let channel_mapping_table = self.channel_mapping_table.clone().unwrap();
writer.write_u8(channel_mapping_table.stream_count)?;
writer.write_u8(channel_mapping_table.coupled_count)?;
for b in channel_mapping_table.channel_mapping.iter() {
writer.write_u8(*b)?;
}
}
Ok(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_opus() {
let src_box = OpusBox {
data_reference_index: 1,
channel_count: 6,
sample_size: 16,
sample_rate: FixedPointU16::new(48000),
dops_box: Some(DopsBox {
version: 0,
output_channel_count: 6,
pre_skip: 312,
input_sample_rate: 48000,
output_gain: 0,
channel_mapping_family: 1,
channel_mapping_table: Some(ChannelMappingTable {
stream_count: 4,
coupled_count: 2,
channel_mapping: [0, 4, 1, 2, 3, 5].to_vec(),
}),
}),
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::OpusBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = OpusBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
#[test]
fn test_opus_witout_channel_mapping_table() {
let src_box = OpusBox {
data_reference_index: 1,
channel_count: 6,
sample_size: 16,
sample_rate: FixedPointU16::new(48000),
dops_box: Some(DopsBox {
version: 0,
output_channel_count: 6,
pre_skip: 312,
input_sample_rate: 48000,
output_gain: 0,
channel_mapping_family: 0,
channel_mapping_table: None,
}),
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::OpusBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = OpusBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}

View file

@ -4,7 +4,7 @@ use std::io::{Read, Seek, Write};
use crate::mp4box::vp09::Vp09Box;
use crate::mp4box::*;
use crate::mp4box::{avc1::Avc1Box, hev1::Hev1Box, mp4a::Mp4aBox, tx3g::Tx3gBox};
use crate::mp4box::{avc1::Avc1Box, hev1::Hev1Box, mp4a::Mp4aBox, opus::OpusBox, tx3g::Tx3gBox};
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
pub struct StsdBox {
@ -23,6 +23,9 @@ pub struct StsdBox {
#[serde(skip_serializing_if = "Option::is_none")]
pub mp4a: Option<Mp4aBox>,
#[serde(skip_serializing_if = "Option::is_none")]
pub opus: Option<OpusBox>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tx3g: Option<Tx3gBox>,
}
@ -42,7 +45,10 @@ impl StsdBox {
size += vp09.box_size();
} else if let Some(ref mp4a) = self.mp4a {
size += mp4a.box_size();
} else if let Some(ref tx3g) = self.tx3g {
} else if let Some(ref opus) = self.opus {
size += opus.box_size();
}
else if let Some(ref tx3g) = self.tx3g {
size += tx3g.box_size();
}
size
@ -80,6 +86,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
let mut hev1 = None;
let mut vp09 = None;
let mut mp4a = None;
let mut opus = None;
let mut tx3g = None;
// Get box header.
@ -104,6 +111,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
BoxType::Mp4aBox => {
mp4a = Some(Mp4aBox::read_box(reader, s)?);
}
BoxType::OpusBox => {
opus = Some(OpusBox::read_box(reader, s)?);
}
BoxType::Tx3gBox => {
tx3g = Some(Tx3gBox::read_box(reader, s)?);
}
@ -118,6 +128,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
avc1,
hev1,
vp09,
opus,
mp4a,
tx3g,
})