scuffle_mp4/boxes/types/
tfdt.rs

1use std::io;
2
3use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4use bytes::Bytes;
5
6use crate::boxes::header::{BoxHeader, FullBoxHeader};
7use crate::boxes::traits::BoxType;
8
9#[derive(Debug, Clone, PartialEq)]
10/// Track Fragment Base Media Decode Time Box
11/// ISO/IEC 14496-12:2022(E) - 8.8.12
12pub struct Tfdt {
13    pub header: FullBoxHeader,
14    pub base_media_decode_time: u64,
15}
16
17impl Tfdt {
18    pub fn new(base_media_decode_time: u64) -> Self {
19        let version = if base_media_decode_time > u32::MAX as u64 { 1 } else { 0 };
20
21        Self {
22            header: FullBoxHeader::new(Self::NAME, version, 0),
23            base_media_decode_time,
24        }
25    }
26}
27
28impl BoxType for Tfdt {
29    const NAME: [u8; 4] = *b"tfdt";
30
31    fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
32        let mut reader = io::Cursor::new(data);
33
34        let header = FullBoxHeader::demux(header, &mut reader)?;
35
36        let base_media_decode_time = if header.version == 1 {
37            reader.read_u64::<BigEndian>()?
38        } else {
39            reader.read_u32::<BigEndian>()? as u64
40        };
41
42        Ok(Self {
43            header,
44            base_media_decode_time,
45        })
46    }
47
48    fn primitive_size(&self) -> u64 {
49        self.header.size() + if self.header.version == 1 { 8 } else { 4 }
50    }
51
52    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
53        self.header.mux(writer)?;
54
55        if self.header.version == 1 {
56            writer.write_u64::<BigEndian>(self.base_media_decode_time)?;
57        } else {
58            writer.write_u32::<BigEndian>(self.base_media_decode_time as u32)?;
59        }
60
61        Ok(())
62    }
63
64    fn validate(&self) -> io::Result<()> {
65        if self.header.version > 1 {
66            return Err(io::Error::new(io::ErrorKind::InvalidData, "tfdt version must be 0 or 1"));
67        }
68
69        if self.header.flags != 0 {
70            return Err(io::Error::new(io::ErrorKind::InvalidData, "tfdt flags must be 0"));
71        }
72
73        if self.header.version == 0 && self.base_media_decode_time > u32::MAX as u64 {
74            return Err(io::Error::new(
75                io::ErrorKind::InvalidData,
76                "tfdt base_data_offset must be less than 2^32",
77            ));
78        }
79
80        Ok(())
81    }
82}