mirror of
https://github.com/alfg/mp4-rust.git
synced 2024-11-18 21:21:01 +00:00
3104a2d95b
* Add ReadBox trait * Add boxtype macro * Remove offset in BoxHeader * Fix parsing error when box has largesize * Remove duplicated codes reading version and flags * Add avc1 box * Add mp4a box * Add mp4a box * Add DecoderSpecificDescriptor in esds box * Add necessary sub-boxes to stbl box * Improve ReadBox::read_box() * Add smhd box * Refactor BoxHeader * Refactor BMFF * Refactor * Add some functions to get offset and size of sample * Add Mp4Reader::read_sample() that read media samples * Move Mp4Reader to reader.rs * Add mandatory check when reading boxes Add some methods to Mp4Reader, TrackReader Format codes * Update mp4info * Refactor common types * Add FixedPointX types * Add media configuration, profile, ... * Add initial Mp4Writer * Run cargo fmt * Add Mp4Writer and examples/mp4copy * Add test codes for Avc1Box and Mp4aBox * Remove prefix "get_" from method names * Rename atoms to mp4box * Fix some bugs Co-authored-by: Byungwan Jun <unipro.kr@gmail.com>
102 lines
2.9 KiB
Rust
102 lines
2.9 KiB
Rust
use byteorder::{BigEndian, WriteBytesExt};
|
|
use std::io::{Seek, SeekFrom, Write};
|
|
|
|
use crate::mp4box::*;
|
|
use crate::track::Mp4TrackWriter;
|
|
use crate::*;
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct Mp4Config {
|
|
pub major_brand: FourCC,
|
|
pub minor_version: u32,
|
|
pub compatible_brands: Vec<FourCC>,
|
|
pub timescale: u32,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Mp4Writer<W> {
|
|
writer: W,
|
|
tracks: Vec<Mp4TrackWriter>,
|
|
mdat_pos: u64,
|
|
timescale: u32,
|
|
duration: u64,
|
|
}
|
|
|
|
impl<W: Write + Seek> Mp4Writer<W> {
|
|
pub fn write_start(mut writer: W, config: &Mp4Config) -> Result<Self> {
|
|
let ftyp = FtypBox {
|
|
major_brand: config.major_brand.clone(),
|
|
minor_version: config.minor_version.clone(),
|
|
compatible_brands: config.compatible_brands.clone(),
|
|
};
|
|
ftyp.write_box(&mut writer)?;
|
|
|
|
// TODO largesize
|
|
let mdat_pos = writer.seek(SeekFrom::Current(0))?;
|
|
BoxHeader::new(BoxType::MdatBox, HEADER_SIZE).write(&mut writer)?;
|
|
|
|
let tracks = Vec::new();
|
|
let timescale = config.timescale;
|
|
let duration = 0;
|
|
Ok(Self {
|
|
writer,
|
|
tracks,
|
|
mdat_pos,
|
|
timescale,
|
|
duration,
|
|
})
|
|
}
|
|
|
|
pub fn add_track(&mut self, config: &TrackConfig) -> Result<()> {
|
|
let track_id = self.tracks.len() as u32 + 1;
|
|
let track = Mp4TrackWriter::new(track_id, config)?;
|
|
self.tracks.push(track);
|
|
Ok(())
|
|
}
|
|
|
|
fn update_durations(&mut self, track_dur: u64) {
|
|
if track_dur > self.duration {
|
|
self.duration = track_dur;
|
|
}
|
|
}
|
|
|
|
pub fn write_sample(&mut self, track_id: u32, sample: &Mp4Sample) -> Result<()> {
|
|
if track_id == 0 {
|
|
return Err(Error::TrakNotFound(track_id));
|
|
}
|
|
|
|
let track_dur = if let Some(ref mut track) = self.tracks.get_mut(track_id as usize - 1) {
|
|
track.write_sample(&mut self.writer, sample, self.timescale)?
|
|
} else {
|
|
return Err(Error::TrakNotFound(track_id));
|
|
};
|
|
|
|
self.update_durations(track_dur);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn update_mdat_size(&mut self) -> Result<()> {
|
|
let mdat_end = self.writer.seek(SeekFrom::Current(0))?;
|
|
let mdat_size = mdat_end - self.mdat_pos;
|
|
assert!(mdat_size < std::u32::MAX as u64);
|
|
self.writer.seek(SeekFrom::Start(self.mdat_pos))?;
|
|
self.writer.write_u32::<BigEndian>(mdat_size as u32)?;
|
|
self.writer.seek(SeekFrom::Start(mdat_end))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn write_end(&mut self) -> Result<()> {
|
|
let mut moov = MoovBox::default();
|
|
|
|
for track in self.tracks.iter_mut() {
|
|
moov.traks.push(track.write_end(&mut self.writer)?);
|
|
}
|
|
self.update_mdat_size()?;
|
|
|
|
moov.mvhd.timescale = self.timescale;
|
|
moov.mvhd.duration = self.duration;
|
|
moov.write_box(&mut self.writer)?;
|
|
Ok(())
|
|
}
|
|
}
|