mirror of
https://github.com/alfg/mp4-rust.git
synced 2025-04-15 08:14:15 +00:00
.
This commit is contained in:
parent
c96f92fde0
commit
f483d55966
3 changed files with 94 additions and 13 deletions
|
@ -5,8 +5,7 @@ use std::io::{self, BufReader, BufWriter};
|
|||
use std::path::Path;
|
||||
|
||||
use mp4::{
|
||||
AacConfig, AvcConfig, HevcConfig, MediaConfig, MediaType, Mp4Config, Result, TrackConfig,
|
||||
TtxtConfig, Vp9Config,
|
||||
AacConfig, AvcConfig, HevcConfig, MediaConfig, MediaType, Mp4Config, OpusConfig, Result, TrackConfig, TtxtConfig, Vp9Config
|
||||
};
|
||||
|
||||
fn main() {
|
||||
|
@ -64,6 +63,12 @@ fn copy<P: AsRef<Path>>(src_filename: &P, dst_filename: &P) -> Result<()> {
|
|||
freq_index: track.sample_freq_index()?,
|
||||
chan_conf: track.channel_config()?,
|
||||
}),
|
||||
MediaType::OPUS => MediaConfig::OpusConfig(OpusConfig {
|
||||
bitrate: track.bitrate(),
|
||||
freq_index: track.sample_freq_index()?,
|
||||
chan_conf: track.channel_config()?,
|
||||
pre_skip: 0,
|
||||
}),
|
||||
MediaType::TTXT => MediaConfig::TtxtConfig(TtxtConfig {}),
|
||||
};
|
||||
|
||||
|
|
46
src/track.rs
46
src/track.rs
|
@ -9,8 +9,8 @@ use crate::mp4box::trak::TrakBox;
|
|||
use crate::mp4box::trun::TrunBox;
|
||||
use crate::mp4box::{
|
||||
avc1::Avc1Box, co64::Co64Box, ctts::CttsBox, ctts::CttsEntry, hev1::Hev1Box, mp4a::Mp4aBox,
|
||||
smhd::SmhdBox, stco::StcoBox, stsc::StscEntry, stss::StssBox, stts::SttsEntry, tx3g::Tx3gBox,
|
||||
vmhd::VmhdBox, vp09::Vp09Box,
|
||||
opus::OpusBox, smhd::SmhdBox, stco::StcoBox, stsc::StscEntry, stss::StssBox, stts::SttsEntry,
|
||||
tx3g::Tx3gBox, vmhd::VmhdBox, vp09::Vp09Box,
|
||||
};
|
||||
use crate::*;
|
||||
|
||||
|
@ -30,6 +30,7 @@ impl From<MediaConfig> for TrackConfig {
|
|||
MediaConfig::AacConfig(aac_conf) => Self::from(aac_conf),
|
||||
MediaConfig::TtxtConfig(ttxt_conf) => Self::from(ttxt_conf),
|
||||
MediaConfig::Vp9Config(vp9_config) => Self::from(vp9_config),
|
||||
MediaConfig::OpusConfig(opus_config) => Self::from(opus_config),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +90,17 @@ impl From<Vp9Config> for TrackConfig {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<OpusConfig> for TrackConfig {
|
||||
fn from(opus_conf: OpusConfig) -> Self {
|
||||
Self {
|
||||
track_type: TrackType::Audio,
|
||||
timescale: 1000, // XXX
|
||||
language: String::from("und"), // XXX
|
||||
media_conf: MediaConfig::OpusConfig(opus_conf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mp4Track {
|
||||
pub trak: TrakBox,
|
||||
|
@ -129,6 +141,8 @@ impl Mp4Track {
|
|||
Ok(MediaType::AAC)
|
||||
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
|
||||
Ok(MediaType::TTXT)
|
||||
} else if self.trak.mdia.minf.stbl.stsd.opus.is_some() {
|
||||
Ok(MediaType::OPUS)
|
||||
} else {
|
||||
Err(Error::InvalidData("unsupported media type"))
|
||||
}
|
||||
|
@ -143,10 +157,10 @@ impl Mp4Track {
|
|||
Ok(FourCC::from(BoxType::Vp09Box))
|
||||
} else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() {
|
||||
Ok(FourCC::from(BoxType::Mp4aBox))
|
||||
} else if self.trak.mdia.minf.stbl.stsd.opus.is_some() {
|
||||
Ok(FourCC::from(BoxType::OpusBox))
|
||||
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
|
||||
Ok(FourCC::from(BoxType::Tx3gBox))
|
||||
} else if self.trak.mdia.minf.stbl.stsd.opus.is_some() {
|
||||
Ok(FourCC::from(BoxType::OpusBox))
|
||||
} else {
|
||||
Err(Error::InvalidData("unsupported sample entry box"))
|
||||
}
|
||||
|
@ -184,6 +198,12 @@ impl Mp4Track {
|
|||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
|
||||
}
|
||||
} else if let Some(ref opus) = self.trak.mdia.minf.stbl.stsd.opus {
|
||||
if let Some(ref dops) = opus.dops_box {
|
||||
SampleFreqIndex::try_from(dops.input_sample_rate)
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::DopsBox))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
|
||||
}
|
||||
|
@ -196,6 +216,12 @@ impl Mp4Track {
|
|||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
|
||||
}
|
||||
} else if let Some(ref opus) = self.trak.mdia.minf.stbl.stsd.opus {
|
||||
if let Some(ref dops ) = opus.dops_box {
|
||||
ChannelConfig::try_from(dops.output_channel_count)
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::DopsBox))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
|
||||
}
|
||||
|
@ -263,7 +289,7 @@ impl Mp4Track {
|
|||
|
||||
pub fn sequence_parameter_set(&self) -> Result<&[u8]> {
|
||||
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
|
||||
match avc1.avcc.sequence_parameter_sets.get(0) {
|
||||
match avc1.avcc.sequence_parameter_sets.first() {
|
||||
Some(nal) => Ok(nal.bytes.as_ref()),
|
||||
None => Err(Error::EntryInStblNotFound(
|
||||
self.track_id(),
|
||||
|
@ -278,7 +304,7 @@ impl Mp4Track {
|
|||
|
||||
pub fn picture_parameter_set(&self) -> Result<&[u8]> {
|
||||
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
|
||||
match avc1.avcc.picture_parameter_sets.get(0) {
|
||||
match avc1.avcc.picture_parameter_sets.first() {
|
||||
Some(nal) => Ok(nal.bytes.as_ref()),
|
||||
None => Err(Error::EntryInStblNotFound(
|
||||
self.track_id(),
|
||||
|
@ -647,7 +673,7 @@ impl Mp4TrackWriter {
|
|||
let mut trak = TrakBox::default();
|
||||
trak.tkhd.track_id = track_id;
|
||||
trak.mdia.mdhd.timescale = config.timescale;
|
||||
trak.mdia.mdhd.language = config.language.to_owned();
|
||||
config.language.clone_into(&mut trak.mdia.mdhd.language);
|
||||
trak.mdia.hdlr.handler_type = config.track_type.into();
|
||||
trak.mdia.minf.stbl.co64 = Some(Co64Box::default());
|
||||
match config.media_conf {
|
||||
|
@ -688,6 +714,10 @@ impl Mp4TrackWriter {
|
|||
let tx3g = Tx3gBox::default();
|
||||
trak.mdia.minf.stbl.stsd.tx3g = Some(tx3g);
|
||||
}
|
||||
MediaConfig::OpusConfig(ref _opus_config) => {
|
||||
let opus = OpusBox::default();
|
||||
trak.mdia.minf.stbl.stsd.opus = Some(opus);
|
||||
}
|
||||
}
|
||||
Ok(Mp4TrackWriter {
|
||||
trak,
|
||||
|
@ -917,4 +947,4 @@ impl Mp4TrackWriter {
|
|||
|
||||
Ok(self.trak.clone())
|
||||
}
|
||||
}
|
||||
}
|
52
src/types.rs
52
src/types.rs
|
@ -222,6 +222,7 @@ const MEDIA_TYPE_H265: &str = "h265";
|
|||
const MEDIA_TYPE_VP9: &str = "vp9";
|
||||
const MEDIA_TYPE_AAC: &str = "aac";
|
||||
const MEDIA_TYPE_TTXT: &str = "ttxt";
|
||||
const MEDIA_TYPE_OPUS: &str = "opus";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MediaType {
|
||||
|
@ -229,8 +230,8 @@ pub enum MediaType {
|
|||
H265,
|
||||
VP9,
|
||||
AAC,
|
||||
TTXT,
|
||||
}
|
||||
OPUS,
|
||||
TTXT}
|
||||
|
||||
impl fmt::Display for MediaType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -248,6 +249,7 @@ impl TryFrom<&str> for MediaType {
|
|||
MEDIA_TYPE_VP9 => Ok(MediaType::VP9),
|
||||
MEDIA_TYPE_AAC => Ok(MediaType::AAC),
|
||||
MEDIA_TYPE_TTXT => Ok(MediaType::TTXT),
|
||||
MEDIA_TYPE_OPUS => Ok(MediaType::OPUS),
|
||||
_ => Err(Error::InvalidData("unsupported media type")),
|
||||
}
|
||||
}
|
||||
|
@ -261,6 +263,7 @@ impl From<MediaType> for &str {
|
|||
MediaType::VP9 => MEDIA_TYPE_VP9,
|
||||
MediaType::AAC => MEDIA_TYPE_AAC,
|
||||
MediaType::TTXT => MEDIA_TYPE_TTXT,
|
||||
MediaType::OPUS => MEDIA_TYPE_OPUS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -273,6 +276,7 @@ impl From<&MediaType> for &str {
|
|||
MediaType::VP9 => MEDIA_TYPE_VP9,
|
||||
MediaType::AAC => MEDIA_TYPE_AAC,
|
||||
MediaType::TTXT => MEDIA_TYPE_TTXT,
|
||||
MediaType::OPUS => MEDIA_TYPE_OPUS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -502,6 +506,28 @@ impl TryFrom<u8> for SampleFreqIndex {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for SampleFreqIndex {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<SampleFreqIndex> {
|
||||
match value {
|
||||
9600 => Ok(SampleFreqIndex::Freq96000),
|
||||
88200 => Ok(SampleFreqIndex::Freq88200),
|
||||
64000 => Ok(SampleFreqIndex::Freq64000),
|
||||
48000 => Ok(SampleFreqIndex::Freq48000),
|
||||
44100 => Ok(SampleFreqIndex::Freq44100),
|
||||
32000 => Ok(SampleFreqIndex::Freq32000),
|
||||
24000 => Ok(SampleFreqIndex::Freq24000),
|
||||
22050 => Ok(SampleFreqIndex::Freq22050),
|
||||
16000 => Ok(SampleFreqIndex::Freq16000),
|
||||
12000 => Ok(SampleFreqIndex::Freq12000),
|
||||
11025 => Ok(SampleFreqIndex::Freq11025),
|
||||
8000 => Ok(SampleFreqIndex::Freq8000),
|
||||
7350 => Ok(SampleFreqIndex::Freq7350),
|
||||
_ => Err(Error::InvalidData("invalid sampling frequency index")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SampleFreqIndex {
|
||||
pub fn freq(&self) -> u32 {
|
||||
match *self {
|
||||
|
@ -606,6 +632,25 @@ impl Default for AacConfig {
|
|||
#[derive(Debug, PartialEq, Eq, Clone, Default)]
|
||||
pub struct TtxtConfig {}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct OpusConfig {
|
||||
pub bitrate: u32,
|
||||
pub freq_index: SampleFreqIndex,
|
||||
pub chan_conf: ChannelConfig,
|
||||
pub pre_skip: u16,
|
||||
}
|
||||
|
||||
impl Default for OpusConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bitrate: 0,
|
||||
freq_index: SampleFreqIndex::Freq48000,
|
||||
chan_conf: ChannelConfig::Stereo,
|
||||
pre_skip: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum MediaConfig {
|
||||
AvcConfig(AvcConfig),
|
||||
|
@ -613,6 +658,7 @@ pub enum MediaConfig {
|
|||
Vp9Config(Vp9Config),
|
||||
AacConfig(AacConfig),
|
||||
TtxtConfig(TtxtConfig),
|
||||
OpusConfig(OpusConfig),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -738,4 +784,4 @@ impl<'a, T: Metadata<'a>> Metadata<'a> for Option<T> {
|
|||
fn summary(&self) -> Option<Cow<str>> {
|
||||
self.as_ref().and_then(|t| t.summary())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue