Skip to main content

mp4/mp4box/
mod.rs

1//! All ISO-MP4 boxes (atoms) and operations.
2//!
3//! * [ISO/IEC 14496-12](https://en.wikipedia.org/wiki/MPEG-4_Part_14) - ISO Base Media File Format (QuickTime, MPEG-4, etc)
4//! * [ISO/IEC 14496-14](https://en.wikipedia.org/wiki/MPEG-4_Part_14) - MP4 file format
5//! * ISO/IEC 14496-17 - Streaming text format
6//! * [ISO 23009-1](https://www.iso.org/standard/79329.html) -Dynamic adaptive streaming over HTTP (DASH)
7//!
8//! http://developer.apple.com/documentation/QuickTime/QTFF/index.html
9//! http://www.adobe.com/devnet/video/articles/mp4_movie_atom.html
10//! http://mp4ra.org/#/atoms
11//!
12//! Supported Atoms:
13//! ftyp
14//! moov
15//!     mvhd
16//!     udta
17//!         meta
18//!             ilst
19//!                 data
20//!     trak
21//!         tkhd
22//!         mdia
23//!             mdhd
24//!             hdlr
25//!             minf
26//!                 stbl
27//!                     stsd
28//!                         avc1
29//!                         hev1
30//!                         mp4a
31//!                         tx3g
32//!                     stts
33//!                     stsc
34//!                     stsz
35//!                     stss
36//!                     stco
37//!                     co64
38//!                     ctts
39//!                 dinf
40//!                     dref
41//!                 smhd
42//!                 vmhd
43//!         edts
44//!             elst
45//!     mvex
46//!         mehd
47//!         trex
48//! emsg
49//! moof
50//!     mfhd
51//!     traf
52//!         tfhd
53//!         tfdt
54//!         trun
55//! mdat
56//! free
57//!
58
59use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
60use std::convert::TryInto;
61use std::io::{Read, Seek, SeekFrom, Write};
62
63use crate::*;
64
65pub(crate) mod avc1;
66pub(crate) mod co64;
67pub(crate) mod ctts;
68pub(crate) mod data;
69pub(crate) mod dinf;
70pub(crate) mod edts;
71pub(crate) mod elst;
72pub(crate) mod emsg;
73pub(crate) mod ftyp;
74pub(crate) mod hdlr;
75pub(crate) mod hev1;
76pub(crate) mod ilst;
77pub(crate) mod mdhd;
78pub(crate) mod mdia;
79pub(crate) mod mehd;
80pub(crate) mod meta;
81pub(crate) mod mfhd;
82pub(crate) mod minf;
83pub(crate) mod moof;
84pub(crate) mod moov;
85pub(crate) mod mp4a;
86pub(crate) mod mvex;
87pub(crate) mod mvhd;
88pub(crate) mod smhd;
89pub(crate) mod stbl;
90pub(crate) mod stco;
91pub(crate) mod stsc;
92pub(crate) mod stsd;
93pub(crate) mod stss;
94pub(crate) mod stsz;
95pub(crate) mod stts;
96pub(crate) mod tfdt;
97pub(crate) mod tfhd;
98pub(crate) mod tkhd;
99pub(crate) mod traf;
100pub(crate) mod trak;
101pub(crate) mod trex;
102pub(crate) mod trun;
103pub(crate) mod tx3g;
104pub(crate) mod udta;
105pub(crate) mod vmhd;
106pub(crate) mod vp09;
107pub(crate) mod vpcc;
108
109pub use avc1::Avc1Box;
110pub use co64::Co64Box;
111pub use ctts::CttsBox;
112pub use data::DataBox;
113pub use dinf::DinfBox;
114pub use edts::EdtsBox;
115pub use elst::ElstBox;
116pub use emsg::EmsgBox;
117pub use ftyp::FtypBox;
118pub use hdlr::HdlrBox;
119pub use hev1::Hev1Box;
120pub use ilst::IlstBox;
121pub use mdhd::MdhdBox;
122pub use mdia::MdiaBox;
123pub use mehd::MehdBox;
124pub use meta::MetaBox;
125pub use mfhd::MfhdBox;
126pub use minf::MinfBox;
127pub use moof::MoofBox;
128pub use moov::MoovBox;
129pub use mp4a::Mp4aBox;
130pub use mvex::MvexBox;
131pub use mvhd::MvhdBox;
132pub use smhd::SmhdBox;
133pub use stbl::StblBox;
134pub use stco::StcoBox;
135pub use stsc::StscBox;
136pub use stsd::StsdBox;
137pub use stss::StssBox;
138pub use stsz::StszBox;
139pub use stts::SttsBox;
140pub use tfdt::TfdtBox;
141pub use tfhd::TfhdBox;
142pub use tkhd::TkhdBox;
143pub use traf::TrafBox;
144pub use trak::TrakBox;
145pub use trex::TrexBox;
146pub use trun::TrunBox;
147pub use tx3g::Tx3gBox;
148pub use udta::UdtaBox;
149pub use vmhd::VmhdBox;
150pub use vp09::Vp09Box;
151pub use vpcc::VpccBox;
152
153pub const HEADER_SIZE: u64 = 8;
154// const HEADER_LARGE_SIZE: u64 = 16;
155pub const HEADER_EXT_SIZE: u64 = 4;
156
157macro_rules! boxtype {
158    ($( $name:ident => $value:expr ),*) => {
159        #[derive(Clone, Copy, PartialEq, Eq)]
160        pub enum BoxType {
161            $( $name, )*
162            UnknownBox(u32),
163        }
164
165        impl From<u32> for BoxType {
166            fn from(t: u32) -> BoxType {
167                match t {
168                    $( $value => BoxType::$name, )*
169                    _ => BoxType::UnknownBox(t),
170                }
171            }
172        }
173
174        impl From<BoxType> for u32 {
175            fn from(b: BoxType) -> u32 {
176                match b {
177                    $( BoxType::$name => $value, )*
178                    BoxType::UnknownBox(t) => t,
179                }
180            }
181        }
182    }
183}
184
185boxtype! {
186    FtypBox => 0x66747970,
187    MvhdBox => 0x6d766864,
188    MfhdBox => 0x6d666864,
189    FreeBox => 0x66726565,
190    MdatBox => 0x6d646174,
191    MoovBox => 0x6d6f6f76,
192    MvexBox => 0x6d766578,
193    MehdBox => 0x6d656864,
194    TrexBox => 0x74726578,
195    EmsgBox => 0x656d7367,
196    MoofBox => 0x6d6f6f66,
197    TkhdBox => 0x746b6864,
198    TfhdBox => 0x74666864,
199    TfdtBox => 0x74666474,
200    EdtsBox => 0x65647473,
201    MdiaBox => 0x6d646961,
202    ElstBox => 0x656c7374,
203    MdhdBox => 0x6d646864,
204    HdlrBox => 0x68646c72,
205    MinfBox => 0x6d696e66,
206    VmhdBox => 0x766d6864,
207    StblBox => 0x7374626c,
208    StsdBox => 0x73747364,
209    SttsBox => 0x73747473,
210    CttsBox => 0x63747473,
211    StssBox => 0x73747373,
212    StscBox => 0x73747363,
213    StszBox => 0x7374737A,
214    StcoBox => 0x7374636F,
215    Co64Box => 0x636F3634,
216    TrakBox => 0x7472616b,
217    TrafBox => 0x74726166,
218    TrunBox => 0x7472756E,
219    UdtaBox => 0x75647461,
220    MetaBox => 0x6d657461,
221    DinfBox => 0x64696e66,
222    DrefBox => 0x64726566,
223    UrlBox  => 0x75726C20,
224    SmhdBox => 0x736d6864,
225    Avc1Box => 0x61766331,
226    AvcCBox => 0x61766343,
227    Hev1Box => 0x68657631,
228    HvcCBox => 0x68766343,
229    Mp4aBox => 0x6d703461,
230    EsdsBox => 0x65736473,
231    Tx3gBox => 0x74783367,
232    VpccBox => 0x76706343,
233    Vp09Box => 0x76703039,
234    DataBox => 0x64617461,
235    IlstBox => 0x696c7374,
236    NameBox => 0xa96e616d,
237    DayBox => 0xa9646179,
238    CovrBox => 0x636f7672,
239    DescBox => 0x64657363,
240    WideBox => 0x77696465,
241    WaveBox => 0x77617665
242}
243
244pub trait Mp4Box: Sized {
245    fn box_type(&self) -> BoxType;
246    fn box_size(&self) -> u64;
247    fn to_json(&self) -> Result<String>;
248    fn summary(&self) -> Result<String>;
249}
250
251pub trait ReadBox<T>: Sized {
252    fn read_box(_: T, size: u64) -> Result<Self>;
253}
254
255pub trait WriteBox<T>: Sized {
256    fn write_box(&self, _: T) -> Result<u64>;
257}
258
259#[derive(Debug, Clone, Copy)]
260pub struct BoxHeader {
261    pub name: BoxType,
262    pub size: u64,
263}
264
265impl BoxHeader {
266    pub fn new(name: BoxType, size: u64) -> Self {
267        Self { name, size }
268    }
269
270    // TODO: if size is 0, then this box is the last one in the file
271    pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
272        // Create and read to buf.
273        let mut buf = [0u8; 8]; // 8 bytes for box header.
274        reader.read_exact(&mut buf)?;
275
276        // Get size.
277        let s = buf[0..4].try_into().unwrap();
278        let size = u32::from_be_bytes(s);
279
280        // Get box type string.
281        let t = buf[4..8].try_into().unwrap();
282        let typ = u32::from_be_bytes(t);
283
284        // Get largesize if size is 1
285        if size == 1 {
286            reader.read_exact(&mut buf)?;
287            let largesize = u64::from_be_bytes(buf);
288
289            Ok(BoxHeader {
290                name: BoxType::from(typ),
291
292                // Subtract the length of the serialized largesize, as callers assume `size - HEADER_SIZE` is the length
293                // of the box data. Disallow `largesize < 16`, or else a largesize of 8 will result in a BoxHeader::size
294                // of 0, incorrectly indicating that the box data extends to the end of the stream.
295                size: match largesize {
296                    0 => 0,
297                    1..=15 => return Err(Error::InvalidData("64-bit box size too small")),
298                    16..=u64::MAX => largesize - 8,
299                },
300            })
301        } else {
302            Ok(BoxHeader {
303                name: BoxType::from(typ),
304                size: size as u64,
305            })
306        }
307    }
308
309    pub fn write<W: Write>(&self, writer: &mut W) -> Result<u64> {
310        if self.size > u32::MAX as u64 {
311            writer.write_u32::<BigEndian>(1)?;
312            writer.write_u32::<BigEndian>(self.name.into())?;
313            writer.write_u64::<BigEndian>(self.size)?;
314            Ok(16)
315        } else {
316            writer.write_u32::<BigEndian>(self.size as u32)?;
317            writer.write_u32::<BigEndian>(self.name.into())?;
318            Ok(8)
319        }
320    }
321}
322
323pub fn read_box_header_ext<R: Read>(reader: &mut R) -> Result<(u8, u32)> {
324    let version = reader.read_u8()?;
325    let flags = reader.read_u24::<BigEndian>()?;
326    Ok((version, flags))
327}
328
329pub fn write_box_header_ext<W: Write>(w: &mut W, v: u8, f: u32) -> Result<u64> {
330    w.write_u8(v)?;
331    w.write_u24::<BigEndian>(f)?;
332    Ok(4)
333}
334
335pub fn box_start<R: Seek>(seeker: &mut R) -> Result<u64> {
336    Ok(seeker.stream_position()? - HEADER_SIZE)
337}
338
339pub fn skip_bytes<S: Seek>(seeker: &mut S, size: u64) -> Result<()> {
340    seeker.seek(SeekFrom::Current(size as i64))?;
341    Ok(())
342}
343
344pub fn skip_bytes_to<S: Seek>(seeker: &mut S, pos: u64) -> Result<()> {
345    seeker.seek(SeekFrom::Start(pos))?;
346    Ok(())
347}
348
349pub fn skip_box<S: Seek>(seeker: &mut S, size: u64) -> Result<()> {
350    let start = box_start(seeker)?;
351    skip_bytes_to(seeker, start + size)?;
352    Ok(())
353}
354
355pub fn write_zeros<W: Write>(writer: &mut W, size: u64) -> Result<()> {
356    for _ in 0..size {
357        writer.write_u8(0)?;
358    }
359    Ok(())
360}
361
362mod value_u32 {
363    use crate::types::FixedPointU16;
364    use serde::{self, Serializer};
365
366    pub fn serialize<S>(fixed: &FixedPointU16, serializer: S) -> Result<S::Ok, S::Error>
367    where
368        S: Serializer,
369    {
370        serializer.serialize_u16(fixed.value())
371    }
372}
373
374mod value_i16 {
375    use crate::types::FixedPointI8;
376    use serde::{self, Serializer};
377
378    pub fn serialize<S>(fixed: &FixedPointI8, serializer: S) -> Result<S::Ok, S::Error>
379    where
380        S: Serializer,
381    {
382        serializer.serialize_i8(fixed.value())
383    }
384}
385
386mod value_u8 {
387    use crate::types::FixedPointU8;
388    use serde::{self, Serializer};
389
390    pub fn serialize<S>(fixed: &FixedPointU8, serializer: S) -> Result<S::Ok, S::Error>
391    where
392        S: Serializer,
393    {
394        serializer.serialize_u8(fixed.value())
395    }
396}
397
398#[cfg(test)]
399mod tests {
400    use super::*;
401
402    #[test]
403    fn test_fourcc() {
404        let ftyp_fcc = 0x66747970;
405        let ftyp_value = FourCC::from(ftyp_fcc);
406        assert_eq!(&ftyp_value.value[..], b"ftyp");
407        let ftyp_fcc2: u32 = ftyp_value.into();
408        assert_eq!(ftyp_fcc, ftyp_fcc2);
409    }
410
411    #[test]
412    fn test_largesize_too_small() {
413        let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 7][..]);
414        assert!(matches!(error, Err(Error::InvalidData(_))));
415    }
416
417    #[test]
418    fn test_zero_largesize() {
419        let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 8][..]);
420        assert!(matches!(error, Err(Error::InvalidData(_))));
421    }
422
423    #[test]
424    fn test_nonzero_largesize_too_small() {
425        let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 15][..]);
426        assert!(matches!(error, Err(Error::InvalidData(_))));
427    }
428
429    #[test]
430    fn test_valid_largesize() {
431        let header = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 16][..]);
432        assert!(matches!(header, Ok(BoxHeader { size: 8, .. })));
433    }
434}