Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mp4"
version = "0.14.0"
version = "0.15.0"
authors = ["Alf <alf.g.jr@gmail.com>"]
edition = "2018"
description = "MP4 reader and writer library in Rust."
Expand Down
7 changes: 7 additions & 0 deletions src/mp4box/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ 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;
pub(crate) mod stco;
Expand Down Expand Up @@ -129,6 +131,8 @@ 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;
pub use stco::StcoBox;
Expand Down Expand Up @@ -183,6 +187,7 @@ macro_rules! boxtype {
}

boxtype! {
DopsBox => 0x644f7073,
FtypBox => 0x66747970,
MvhdBox => 0x6d766864,
MfhdBox => 0x6d666864,
Expand All @@ -191,6 +196,7 @@ boxtype! {
MoovBox => 0x6d6f6f76,
MvexBox => 0x6d766578,
MehdBox => 0x6d656864,
OpusBox => 0x4f707573,
TrexBox => 0x74726578,
EmsgBox => 0x656d7367,
MoofBox => 0x6d6f6f66,
Expand All @@ -204,6 +210,7 @@ boxtype! {
HdlrBox => 0x68646c72,
MinfBox => 0x6d696e66,
VmhdBox => 0x766d6864,
SidxBox => 0x73696478,
StblBox => 0x7374626c,
StsdBox => 0x73747364,
SttsBox => 0x73747473,
Expand Down
322 changes: 322 additions & 0 deletions src/mp4box/opus.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading
Loading