1use byteorder::{BigEndian, WriteBytesExt};
2use std::io::{Seek, SeekFrom, Write};
3
4use crate::mp4box::*;
5use crate::track::Mp4TrackWriter;
6use crate::*;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Mp4Config {
10 pub major_brand: FourCC,
11 pub minor_version: u32,
12 pub compatible_brands: Vec<FourCC>,
13 pub timescale: u32,
14}
15
16#[derive(Debug)]
17pub struct Mp4Writer<W> {
18 writer: W,
19 tracks: Vec<Mp4TrackWriter>,
20 mdat_pos: u64,
21 timescale: u32,
22 duration: u64,
23}
24
25impl<W> Mp4Writer<W> {
26 pub fn into_writer(self) -> W {
58 self.writer
59 }
60}
61
62impl<W: Write + Seek> Mp4Writer<W> {
63 pub fn write_start(mut writer: W, config: &Mp4Config) -> Result<Self> {
64 let ftyp = FtypBox {
65 major_brand: config.major_brand,
66 minor_version: config.minor_version,
67 compatible_brands: config.compatible_brands.clone(),
68 };
69 ftyp.write_box(&mut writer)?;
70
71 let mdat_pos = writer.stream_position()?;
73 BoxHeader::new(BoxType::MdatBox, HEADER_SIZE).write(&mut writer)?;
74 BoxHeader::new(BoxType::WideBox, HEADER_SIZE).write(&mut writer)?;
75
76 let tracks = Vec::new();
77 let timescale = config.timescale;
78 let duration = 0;
79 Ok(Self {
80 writer,
81 tracks,
82 mdat_pos,
83 timescale,
84 duration,
85 })
86 }
87
88 pub fn add_track(&mut self, config: &TrackConfig) -> Result<()> {
89 let track_id = self.tracks.len() as u32 + 1;
90 let track = Mp4TrackWriter::new(track_id, config)?;
91 self.tracks.push(track);
92 Ok(())
93 }
94
95 fn update_durations(&mut self, track_dur: u64) {
96 if track_dur > self.duration {
97 self.duration = track_dur;
98 }
99 }
100
101 pub fn write_sample(&mut self, track_id: u32, sample: &Mp4Sample) -> Result<()> {
102 if track_id == 0 {
103 return Err(Error::TrakNotFound(track_id));
104 }
105
106 let track_dur = if let Some(ref mut track) = self.tracks.get_mut(track_id as usize - 1) {
107 track.write_sample(&mut self.writer, sample, self.timescale)?
108 } else {
109 return Err(Error::TrakNotFound(track_id));
110 };
111
112 self.update_durations(track_dur);
113
114 Ok(())
115 }
116
117 fn update_mdat_size(&mut self) -> Result<()> {
118 let mdat_end = self.writer.stream_position()?;
119 let mdat_size = mdat_end - self.mdat_pos;
120 if mdat_size > std::u32::MAX as u64 {
121 self.writer.seek(SeekFrom::Start(self.mdat_pos))?;
122 self.writer.write_u32::<BigEndian>(1)?;
123 self.writer.seek(SeekFrom::Start(self.mdat_pos + 8))?;
124 self.writer.write_u64::<BigEndian>(mdat_size)?;
125 } else {
126 self.writer.seek(SeekFrom::Start(self.mdat_pos))?;
127 self.writer.write_u32::<BigEndian>(mdat_size as u32)?;
128 }
129 self.writer.seek(SeekFrom::Start(mdat_end))?;
130 Ok(())
131 }
132
133 pub fn write_end(&mut self) -> Result<()> {
134 let mut moov = MoovBox::default();
135
136 for track in self.tracks.iter_mut() {
137 moov.traks.push(track.write_end(&mut self.writer)?);
138 }
139 self.update_mdat_size()?;
140
141 moov.mvhd.timescale = self.timescale;
142 moov.mvhd.duration = self.duration;
143 if moov.mvhd.duration > (u32::MAX as u64) {
144 moov.mvhd.version = 1
145 }
146 moov.write_box(&mut self.writer)?;
147 Ok(())
148 }
149}