1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-12-22 12:06:33 +00:00

example/copy supports vp9-in-mp4 and example/dump supports fmp4 (#41)

* feat: mvex box中的mehd box改为可选,支持fmp4的解析

* feat: support to copy mp4 with vp9 codec, but not support to copy fmp4 with vp9 codec

* Update types.rs

undo unnecessary changes.

* Update types.rs

undo reduce unnecessary changes.

* Update types.rs

* Update mp4copy.rs

Add vp9 code after h265

* Update stsd.rs

Add vp09 after the Hevc

* Update types.rs

Add after the HevcConfig.

* fix: Track.rs add vp9 support

* feat: mp4 writer set vp09 box into stsd box
This commit is contained in:
ninthakeey 2021-01-27 11:31:32 +08:00 committed by GitHub
parent 9e8f27be2a
commit 00b50636b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 387 additions and 9 deletions

View file

@ -8,6 +8,7 @@ use mp4::{
AacConfig,
AvcConfig,
HevcConfig,
Vp9Config,
TtxtConfig,
MediaConfig,
MediaType,
@ -61,6 +62,10 @@ fn copy<P: AsRef<Path>>(src_filename: &P, dst_filename: &P) -> Result<()> {
width: track.width(),
height: track.height(),
}),
MediaType::VP9 => MediaConfig::Vp9Config(Vp9Config {
width: track.width(),
height: track.height(),
}),
MediaType::AAC => MediaConfig::AacConfig(AacConfig {
bitrate: track.bitrate(),
profile: track.audio_profile()?,

View file

@ -54,7 +54,9 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
if let Some(ref mvex) = &mp4.moov.mvex {
boxes.push(build_box(mvex));
boxes.push(build_box(&mvex.mehd));
if let Some(mehd) = &mvex.mehd {
boxes.push(build_box(mehd));
}
boxes.push(build_box(&mvex.trex));
}

View file

@ -90,6 +90,8 @@ pub(crate) mod traf;
pub(crate) mod trun;
pub(crate) mod tx3g;
pub(crate) mod vmhd;
pub(crate) mod vp09;
pub(crate) mod vpcc;
pub use ftyp::FtypBox;
pub use moov::MoovBox;
@ -170,7 +172,9 @@ boxtype! {
HvcCBox => 0x68766343,
Mp4aBox => 0x6d703461,
EsdsBox => 0x65736473,
Tx3gBox => 0x74783367
Tx3gBox => 0x74783367,
VpccBox => 0x76706343,
Vp09Box => 0x76703039
}
pub trait Mp4Box: Sized {

View file

@ -6,7 +6,7 @@ use crate::mp4box::{mehd::MehdBox, trex::TrexBox};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
pub struct MvexBox {
pub mehd: MehdBox,
pub mehd: Option<MehdBox>,
pub trex: TrexBox,
}
@ -16,7 +16,7 @@ impl MvexBox {
}
pub fn get_size(&self) -> u64 {
HEADER_SIZE + self.mehd.box_size() + self.trex.box_size()
HEADER_SIZE + self.mehd.as_ref().map(|x| x.box_size()).unwrap_or(0) + self.trex.box_size()
}
}
@ -69,9 +69,6 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvexBox {
current = reader.seek(SeekFrom::Current(0))?;
}
if mehd.is_none() {
return Err(Error::BoxNotFound(BoxType::MehdBox));
}
if trex.is_none() {
return Err(Error::BoxNotFound(BoxType::TrexBox));
}
@ -79,7 +76,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvexBox {
skip_bytes_to(reader, start + size)?;
Ok(MvexBox {
mehd: mehd.unwrap(),
mehd,
trex: trex.unwrap(),
})
}
@ -90,7 +87,9 @@ impl<W: Write> WriteBox<&mut W> for MvexBox {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
self.mehd.write_box(writer)?;
if let Some(mehd) = &self.mehd{
mehd.write_box(writer)?;
}
self.trex.write_box(writer)?;
Ok(size)

View file

@ -4,6 +4,7 @@ use serde::{Serialize};
use crate::mp4box::*;
use crate::mp4box::{avc1::Avc1Box, hev1::Hev1Box, mp4a::Mp4aBox, tx3g::Tx3gBox};
use crate::mp4box::vp09::Vp09Box;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
pub struct StsdBox {
@ -15,6 +16,9 @@ pub struct StsdBox {
#[serde(skip_serializing_if = "Option::is_none")]
pub hev1: Option<Hev1Box>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vp09: Option<Vp09Box>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mp4a: Option<Mp4aBox>,
@ -34,6 +38,8 @@ impl StsdBox {
size += avc1.box_size();
} else if let Some(ref hev1) = self.hev1 {
size += hev1.box_size();
} else if let Some(ref vp09) = self.vp09 {
size += vp09.box_size();
} else if let Some(ref mp4a) = self.mp4a {
size += mp4a.box_size();
} else if let Some(ref tx3g) = self.tx3g {
@ -72,6 +78,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
let mut avc1 = None;
let mut hev1 = None;
let mut vp09 = None;
let mut mp4a = None;
let mut tx3g = None;
@ -86,6 +93,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
BoxType::Hev1Box => {
hev1 = Some(Hev1Box::read_box(reader, s)?);
}
BoxType::Vp09Box => {
vp09 = Some(Vp09Box::read_box(reader, s)?);
}
BoxType::Mp4aBox => {
mp4a = Some(Mp4aBox::read_box(reader, s)?);
}
@ -102,6 +112,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
flags,
avc1,
hev1,
vp09,
mp4a,
tx3g,
})
@ -121,6 +132,8 @@ impl<W: Write> WriteBox<&mut W> for StsdBox {
avc1.write_box(writer)?;
} else if let Some(ref hev1) = self.hev1 {
hev1.write_box(writer)?;
} else if let Some(ref vp09) = self.vp09 {
vp09.write_box(writer)?;
} else if let Some(ref mp4a) = self.mp4a {
mp4a.write_box(writer)?;
} else if let Some(ref tx3g) = self.tx3g {

191
src/mp4box/vp09.rs Normal file
View file

@ -0,0 +1,191 @@
use crate::Mp4Box;
use crate::mp4box::*;
use serde::{Serialize};
use crate::mp4box::vpcc::VpccBox;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
pub struct Vp09Box {
pub version: u8,
pub flags: u32,
pub start_code: u16,
pub data_reference_index: u16,
pub reserved0: [u8; 16],
pub width: u16,
pub height: u16,
pub horizresolution: (u16, u16),
pub vertresolution: (u16, u16),
pub reserved1: [u8; 4],
pub frame_count: u16,
pub compressorname: [u8; 32],
pub depth: u16,
pub end_code: u16,
pub vpcc: VpccBox,
}
impl Vp09Box {
pub const DEFAULT_START_CODE: u16 = 0;
pub const DEFAULT_END_CODE: u16 = 0xFFFF;
pub const DEFAULT_DATA_REFERENCE_INDEX: u16 = 1;
pub const DEFAULT_HORIZRESOLUTION: (u16, u16) = (0x48, 0x00);
pub const DEFAULT_VERTRESOLUTION: (u16, u16) = (0x48, 0x00);
pub const DEFAULT_FRAME_COUNT: u16 = 1;
pub const DEFAULT_COMPRESSORNAME: [u8; 32] = [0; 32];
pub const DEFAULT_DEPTH: u16 = 24;
pub fn new(config: &Vp9Config) -> Self {
Vp09Box {
version: 0,
flags: 0,
start_code: Vp09Box::DEFAULT_START_CODE,
data_reference_index: Vp09Box::DEFAULT_DATA_REFERENCE_INDEX,
reserved0: Default::default(),
width: config.width,
height: config.height,
horizresolution: Vp09Box::DEFAULT_HORIZRESOLUTION,
vertresolution: Vp09Box::DEFAULT_VERTRESOLUTION,
reserved1: Default::default(),
frame_count: Vp09Box::DEFAULT_FRAME_COUNT,
compressorname: Vp09Box::DEFAULT_COMPRESSORNAME,
depth: Vp09Box::DEFAULT_DEPTH,
end_code: Vp09Box::DEFAULT_END_CODE,
vpcc: VpccBox {
version: VpccBox::DEFAULT_VERSION,
flags: 0,
profile: 0,
level: 0x1F,
bit_depth: VpccBox::DEFAULT_BIT_DEPTH,
chroma_subsampling: 0,
video_full_range_flag: false,
color_primaries: 0,
transfer_characteristics: 0,
matrix_coefficients: 0,
codec_initialization_data_size: 0,
},
}
}
}
impl Mp4Box for Vp09Box {
fn box_type(&self) -> BoxType {
BoxType::Vp09Box
}
fn box_size(&self) -> u64 {
0x6A
}
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
Ok(format!("{:?}", self))
}
}
impl<R: Read + Seek> ReadBox<&mut R> for Vp09Box {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
let (version, flags) = read_box_header_ext(reader)?;
let start_code: u16 = reader.read_u16::<BigEndian>()?;
let data_reference_index: u16 = reader.read_u16::<BigEndian>()?;
let reserved0: [u8; 16] = {
let mut buf = [0u8; 16];
reader.read_exact(&mut buf)?;
buf
};
let width: u16 = reader.read_u16::<BigEndian>()?;
let height: u16 = reader.read_u16::<BigEndian>()?;
let horizresolution: (u16, u16) = (reader.read_u16::<BigEndian>()?, reader.read_u16::<BigEndian>()?);
let vertresolution: (u16, u16) = (reader.read_u16::<BigEndian>()?, reader.read_u16::<BigEndian>()?);
let reserved1: [u8; 4] = {
let mut buf = [0u8; 4];
reader.read_exact(&mut buf)?;
buf
};
let frame_count: u16 = reader.read_u16::<BigEndian>()?;
let compressorname: [u8; 32] = {
let mut buf = [0u8; 32];
reader.read_exact(&mut buf)?;
buf
};
let depth: u16 = reader.read_u16::<BigEndian>()?;
let end_code: u16 = reader.read_u16::<BigEndian>()?;
let vpcc = {
let header = BoxHeader::read(reader)?;
VpccBox::read_box(reader, header.size)?
};
skip_bytes_to(reader, start + size)?;
Ok(Self {
version,
flags,
start_code,
data_reference_index,
reserved0,
width,
height,
horizresolution,
vertresolution,
reserved1,
frame_count,
compressorname,
depth,
end_code,
vpcc,
})
}
}
impl<W: Write> WriteBox<&mut W> for Vp09Box {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
writer.write_u16::<BigEndian>(self.start_code)?;
writer.write_u16::<BigEndian>(self.data_reference_index)?;
writer.write_all(&self.reserved0)?;
writer.write_u16::<BigEndian>(self.width)?;
writer.write_u16::<BigEndian>(self.height)?;
writer.write_u16::<BigEndian>(self.horizresolution.0)?;
writer.write_u16::<BigEndian>(self.horizresolution.1)?;
writer.write_u16::<BigEndian>(self.vertresolution.0)?;
writer.write_u16::<BigEndian>(self.vertresolution.1)?;
writer.write_all(&self.reserved1)?;
writer.write_u16::<BigEndian>(self.frame_count)?;
writer.write_all(&self.compressorname)?;
writer.write_u16::<BigEndian>(self.depth)?;
writer.write_u16::<BigEndian>(self.end_code)?;
VpccBox::write_box(&self.vpcc, writer)?;
Ok(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_vpcc() {
let src_box = Vp09Box::new(&Vp9Config{ width: 1920, height: 1080 });
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::Vp09Box);
assert_eq!(src_box.box_size(), header.size);
let dst_box = Vp09Box::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}

128
src/mp4box/vpcc.rs Normal file
View file

@ -0,0 +1,128 @@
use crate::Mp4Box;
use crate::mp4box::*;
use serde::{Serialize};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
pub struct VpccBox {
pub version: u8,
pub flags: u32,
pub profile: u8,
pub level: u8,
pub bit_depth: u8,
pub chroma_subsampling: u8,
pub video_full_range_flag: bool,
pub color_primaries: u8,
pub transfer_characteristics: u8,
pub matrix_coefficients: u8,
pub codec_initialization_data_size: u16,
}
impl VpccBox {
pub const DEFAULT_VERSION: u8 = 1;
pub const DEFAULT_BIT_DEPTH: u8 = 8;
}
impl Mp4Box for VpccBox {
fn box_type(&self) -> BoxType {
BoxType::VpccBox
}
fn box_size(&self) -> u64 {
HEADER_SIZE + HEADER_EXT_SIZE + 8
}
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
Ok(format!("{:?}", self))
}
}
impl<R: Read + Seek> ReadBox<&mut R> for VpccBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
let (version, flags) = read_box_header_ext(reader)?;
let profile: u8 = reader.read_u8()?;
let level: u8 = reader.read_u8()?;
let (bit_depth, chroma_subsampling, video_full_range_flag) = {
let b = reader.read_u8()?;
(b >> 4, b << 4 >> 5, b & 0x01 == 1)
};
let transfer_characteristics: u8 = reader.read_u8()?;
let matrix_coefficients: u8 = reader.read_u8()?;
let codec_initialization_data_size: u16 = reader.read_u16::<BigEndian>()?;
skip_bytes_to(reader, start + size)?;
Ok(Self {
version,
flags,
profile,
level,
bit_depth,
chroma_subsampling,
video_full_range_flag,
color_primaries: 0,
transfer_characteristics,
matrix_coefficients,
codec_initialization_data_size,
})
}
}
impl<W: Write> WriteBox<&mut W> for VpccBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
writer.write_u8(self.profile)?;
writer.write_u8(self.level)?;
writer.write_u8((self.bit_depth << 4) | (self.chroma_subsampling << 1) | (self.video_full_range_flag as u8))?;
writer.write_u8(self.color_primaries)?;
writer.write_u8(self.transfer_characteristics)?;
writer.write_u8(self.matrix_coefficients)?;
writer.write_u16::<BigEndian>(self.codec_initialization_data_size)?;
Ok(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_vpcc() {
let src_box = VpccBox {
version: VpccBox::DEFAULT_VERSION,
flags: 0,
profile: 0,
level: 0x1F,
bit_depth: VpccBox::DEFAULT_BIT_DEPTH,
chroma_subsampling: 0,
video_full_range_flag: false,
color_primaries: 0,
transfer_characteristics: 0,
matrix_coefficients: 0,
codec_initialization_data_size: 0,
};
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::VpccBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = VpccBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}

View file

@ -10,6 +10,7 @@ use crate::mp4box::*;
use crate::mp4box::{
avc1::Avc1Box,
hev1::Hev1Box,
vp09::Vp09Box,
ctts::CttsBox,
ctts::CttsEntry,
mp4a::Mp4aBox,
@ -38,6 +39,7 @@ impl From<MediaConfig> for TrackConfig {
MediaConfig::HevcConfig(hevc_conf) => Self::from(hevc_conf),
MediaConfig::AacConfig(aac_conf) => Self::from(aac_conf),
MediaConfig::TtxtConfig(ttxt_conf) => Self::from(ttxt_conf),
MediaConfig::Vp9Config(vp9_config) => Self::from(vp9_config),
}
}
}
@ -86,6 +88,17 @@ impl From<TtxtConfig> for TrackConfig {
}
}
impl From<Vp9Config> for TrackConfig {
fn from(vp9_conf: Vp9Config) -> Self {
Self {
track_type: TrackType::Video,
timescale: 1000, // XXX
language: String::from("und"), // XXX
media_conf: MediaConfig::Vp9Config(vp9_conf),
}
}
}
#[derive(Debug)]
pub struct Mp4Track {
pub trak: TrakBox,
@ -114,6 +127,8 @@ impl Mp4Track {
Ok(MediaType::H264)
} else if self.trak.mdia.minf.stbl.stsd.hev1.is_some() {
Ok(MediaType::H265)
} else if self.trak.mdia.minf.stbl.stsd.vp09.is_some() {
Ok(MediaType::VP9)
} else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() {
Ok(MediaType::AAC)
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
@ -128,6 +143,8 @@ impl Mp4Track {
Ok(FourCC::from(BoxType::Avc1Box))
} else if self.trak.mdia.minf.stbl.stsd.hev1.is_some() {
Ok(FourCC::from(BoxType::Hev1Box))
} else if self.trak.mdia.minf.stbl.stsd.vp09.is_some() {
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.tx3g.is_some() {
@ -555,6 +572,12 @@ impl Mp4TrackWriter {
let hev1 = Hev1Box::new(hevc_config);
trak.mdia.minf.stbl.stsd.hev1 = Some(hev1);
}
MediaConfig::Vp9Config(ref config) => {
trak.tkhd.set_width(config.width);
trak.tkhd.set_height(config.height);
trak.mdia.minf.stbl.stsd.vp09 = Some(Vp09Box::new(config));
}
MediaConfig::AacConfig(ref aac_config) => {
let smhd = SmhdBox::default();
trak.mdia.minf.smhd = Some(smhd);
@ -566,6 +589,7 @@ impl Mp4TrackWriter {
let tx3g = Tx3gBox::default();
trak.mdia.minf.stbl.stsd.tx3g = Some(tx3g);
}
}
Ok(Mp4TrackWriter {
trak,

View file

@ -238,6 +238,7 @@ impl Into<FourCC> for TrackType {
const MEDIA_TYPE_H264: &str = "h264";
const MEDIA_TYPE_H265: &str = "h265";
const MEDIA_TYPE_VP9: &str = "vp9";
const MEDIA_TYPE_AAC: &str = "aac";
const MEDIA_TYPE_TTXT: &str = "ttxt";
@ -245,6 +246,7 @@ const MEDIA_TYPE_TTXT: &str = "ttxt";
pub enum MediaType {
H264,
H265,
VP9,
AAC,
TTXT,
}
@ -262,6 +264,7 @@ impl TryFrom<&str> for MediaType {
match media {
MEDIA_TYPE_H264 => Ok(MediaType::H264),
MEDIA_TYPE_H265 => Ok(MediaType::H265),
MEDIA_TYPE_VP9 => Ok(MediaType::VP9),
MEDIA_TYPE_AAC => Ok(MediaType::AAC),
MEDIA_TYPE_TTXT => Ok(MediaType::TTXT),
_ => Err(Error::InvalidData("unsupported media type")),
@ -274,6 +277,7 @@ impl Into<&str> for MediaType {
match self {
MediaType::H264 => MEDIA_TYPE_H264,
MediaType::H265 => MEDIA_TYPE_H265,
MediaType::VP9 => MEDIA_TYPE_VP9,
MediaType::AAC => MEDIA_TYPE_AAC,
MediaType::TTXT => MEDIA_TYPE_TTXT,
}
@ -285,6 +289,7 @@ impl Into<&str> for &MediaType {
match self {
MediaType::H264 => MEDIA_TYPE_H264,
MediaType::H265 => MEDIA_TYPE_H265,
MediaType::VP9 => MEDIA_TYPE_VP9,
MediaType::AAC => MEDIA_TYPE_AAC,
MediaType::TTXT => MEDIA_TYPE_TTXT,
}
@ -475,6 +480,12 @@ pub struct HevcConfig {
pub height: u16,
}
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Vp9Config {
pub width: u16,
pub height: u16,
}
#[derive(Debug, PartialEq, Clone)]
pub struct AacConfig {
pub bitrate: u32,
@ -501,6 +512,7 @@ pub struct TtxtConfig {}
pub enum MediaConfig {
AvcConfig(AvcConfig),
HevcConfig(HevcConfig),
Vp9Config(Vp9Config),
AacConfig(AacConfig),
TtxtConfig(TtxtConfig),
}