Skip to main content

mp4_atom/moov/trak/mdia/minf/stbl/stsd/
mett.rs

1use crate::*;
2
3/// TextConfigBox ('txtC'), configuration for a TextMetaDataSampleEntry ('mett').///
4///
5/// See ISO/IEC 14496-12:2026 Section 12.3.3.2.
6#[derive(Debug, Clone, PartialEq, Eq)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct TxtC {
9    pub text_config: String,
10}
11
12impl AtomExt for TxtC {
13    type Ext = ();
14
15    const KIND_EXT: FourCC = FourCC::new(b"txtC");
16
17    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: ()) -> Result<Self> {
18        Ok(Self {
19            text_config: String::decode(buf)?,
20        })
21    }
22
23    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<()> {
24        self.text_config.as_str().encode(buf)
25    }
26}
27
28/// TextMetaDataSampleEntry ('mett'), used for text-based timed metadata tracks.
29///
30/// See ISO/IEC 14496-12:2026 Section 12.3.3.2.
31#[derive(Debug, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub struct Mett {
34    pub metadata: MetaData,
35    pub content_encoding: String,
36    pub mime_format: String,
37    pub btrt: Option<Btrt>,
38    pub txtc: Option<TxtC>,
39}
40
41impl Atom for Mett {
42    const KIND: FourCC = FourCC::new(b"mett");
43
44    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
45        let metadata = MetaData::decode(buf)?;
46
47        let content_encoding = String::decode(buf)?;
48        let mime_format = String::decode(buf)?;
49
50        let mut btrt = None;
51        let mut txtc = None;
52        while let Some(atom) = Any::decode_maybe(buf)? {
53            match atom {
54                Any::Btrt(atom) => btrt = atom.into(),
55                Any::TxtC(atom) => txtc = atom.into(),
56                unknown => Self::decode_unknown(&unknown)?,
57            }
58        }
59        skip_trailing_padding(buf);
60
61        Ok(Self {
62            metadata,
63            content_encoding,
64            mime_format,
65            btrt,
66            txtc,
67        })
68    }
69
70    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
71        self.metadata.encode(buf)?;
72
73        self.content_encoding.as_str().encode(buf)?;
74        self.mime_format.as_str().encode(buf)?;
75        self.btrt.encode(buf)?;
76        self.txtc.encode(buf)?;
77        Ok(())
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_mett_roundtrip() {
87        let mett = Mett {
88            metadata: MetaData {
89                data_reference_index: 1,
90            },
91            content_encoding: "".into(),
92            mime_format: "text/plain".into(),
93            btrt: Some(Btrt {
94                buffer_size_db: 1,
95                max_bitrate: 2000,
96                avg_bitrate: 400,
97            }),
98            txtc: Some(TxtC {
99                text_config: "some config".into(),
100            }),
101        };
102
103        let mut buf = Vec::new();
104        mett.encode(&mut buf).unwrap();
105
106        let decoded = Mett::decode(&mut buf.as_slice()).expect("failed to decode mett");
107        assert_eq!(decoded, mett);
108    }
109
110    #[test]
111    fn test_mett_roundtrip_no_config() {
112        let mett = Mett {
113            metadata: MetaData {
114                data_reference_index: 1,
115            },
116            content_encoding: "application/zip".into(),
117            mime_format: "text/html".into(),
118            btrt: None,
119            txtc: None,
120        };
121
122        let mut buf = Vec::new();
123        mett.encode(&mut buf).unwrap();
124
125        let decoded = Mett::decode(&mut buf.as_slice()).expect("failed to decode mett");
126        assert_eq!(decoded, mett);
127    }
128}