Skip to main content

mp4/
writer.rs

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    /// Consume self, returning the inner writer.
27    ///
28    /// This can be useful to recover the inner writer after completion in case
29    /// it's owned by the [Mp4Writer] instance.
30    ///
31    /// # Examples
32    ///
33    /// ```rust
34    /// use mp4::{Mp4Writer, Mp4Config};
35    /// use std::io::Cursor;
36    ///
37    /// # fn main() -> mp4::Result<()> {
38    /// let config = Mp4Config {
39    ///     major_brand: str::parse("isom").unwrap(),
40    ///     minor_version: 512,
41    ///     compatible_brands: vec![
42    ///         str::parse("isom").unwrap(),
43    ///         str::parse("iso2").unwrap(),
44    ///         str::parse("avc1").unwrap(),
45    ///         str::parse("mp41").unwrap(),
46    ///     ],
47    ///     timescale: 1000,
48    /// };
49    ///
50    /// let data = Cursor::new(Vec::<u8>::new());
51    /// let mut writer = mp4::Mp4Writer::write_start(data, &config)?;
52    /// writer.write_end()?;
53    ///
54    /// let data: Vec<u8> = writer.into_writer().into_inner();
55    /// # Ok(()) }
56    /// ```
57    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        // TODO largesize
72        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}