mp4_atom/meta/ilst/
tool.rs

1use crate::*;
2
3const DATA_4CC: FourCC = FourCC::new(b"data");
4const TYPE_INDICATOR_UTF8: u32 = 1u32;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct Tool {
9    pub country_indicator: u16,
10    pub language_indicator: u16,
11    pub text: String,
12}
13
14impl Tool {
15    pub fn new(country_indicator: u16, language_indicator: u16, text: String) -> Result<Self> {
16        Ok(Self {
17            country_indicator,
18            language_indicator,
19            text,
20        })
21    }
22}
23
24impl Atom for Tool {
25    const KIND: FourCC = FourCC::new(b"\xa9too");
26
27    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
28        let type_indicator_or_len = u32::decode(buf)?;
29        match type_indicator_or_len {
30            1 => {
31                // Too short for a valid length, so probably
32                // UTF-8 text, FFmpeg short-style
33                let country_indicator = u16::decode(buf)?;
34                let language_indicator = u16::decode(buf)?;
35                Ok(Tool {
36                    country_indicator,
37                    language_indicator,
38                    text: String::decode(buf)?,
39                })
40            }
41            _ => {
42                // Maybe Atom follows on straight away.
43                // Try parsing as Quicktime data atom: GPAC style or FFmpeg long style
44                let fourcc = FourCC::decode(buf)?;
45                if fourcc != DATA_4CC {
46                    return Err(Error::UnexpectedBox(fourcc));
47                }
48                let type_indicator = u32::decode(buf)?;
49                if type_indicator != TYPE_INDICATOR_UTF8 {
50                    return Err(Error::Unsupported(
51                        "Only UTF-8 text is supported in ilst tool box",
52                    ));
53                }
54                let country_indicator = u16::decode(buf)?;
55                let language_indicator = u16::decode(buf)?;
56                let remaining_bytes = buf.remaining();
57                let body = &mut buf.slice(remaining_bytes);
58                let text = String::from_utf8(body.to_vec()).map_err(|_| Error::InvalidSize)?;
59                buf.advance(remaining_bytes);
60                Ok(Tool {
61                    country_indicator,
62                    language_indicator,
63                    text,
64                })
65            }
66        }
67    }
68
69    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
70        let text_bytes = self.text.as_bytes();
71        // the length of the nested atom is the length field (4 bytes),
72        // the 4CC (4 bytes), the type indicator (4 bytes), the country
73        // indicator (2 bytes), the language indicator (2 bytes) and
74        // then the actual text.
75        let nested_len = (4 + 4 + 4 + 2 + 2 + text_bytes.len()) as u32;
76        nested_len.encode(buf)?;
77        DATA_4CC.encode(buf)?;
78        TYPE_INDICATOR_UTF8.encode(buf)?;
79        self.country_indicator.encode(buf)?;
80        self.language_indicator.encode(buf)?;
81        text_bytes.encode(buf)?;
82        Ok(())
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_ilst_tool_short_style() {
92        let buf = vec![
93            0, 0, 0, 28, 0xa9, b't', b'o', b'o', 0, 0, 0, 1, 0, 0, 0, 0, b'L', b'a', b'v', b'f',
94            b'6', b'1', b'.', b'7', b'.', b'1', b'0', b'0',
95        ];
96
97        let parse_result = Tool::decode(&mut buf.as_slice());
98        assert!(parse_result.is_ok());
99        let ctoo = parse_result.unwrap();
100        assert_eq!(ctoo.country_indicator, 0);
101        assert_eq!(ctoo.language_indicator, 0);
102        assert_eq!(ctoo.text, "Lavf61.7.100");
103    }
104
105    const ENCODED_CTOO: &[u8] = &[
106        0x00, 0x00, 0x00, 0x24, 0xA9, 0x74, 0x6F, 0x6F, 0x00, 0x00, 0x00, 0x1C, b'd', b'a', b't',
107        b'a', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, b'L', b'a', b'v', b'f', b'6', b'1',
108        b'.', b'7', b'.', b'1', b'0', b'0',
109    ];
110
111    #[test]
112    fn test_ilst_tool_long_style() {
113        let inbuf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_CTOO);
114        let parse_result = Tool::decode(inbuf);
115        assert!(parse_result.is_ok());
116        let ctoo = parse_result.unwrap();
117        assert_eq!(ctoo.country_indicator, 0);
118        assert_eq!(ctoo.language_indicator, 0);
119        assert_eq!(ctoo.text, "Lavf61.7.100");
120
121        let mut outbuf = Vec::new();
122        ctoo.encode(&mut outbuf).unwrap();
123
124        assert_eq!(outbuf.as_slice(), ENCODED_CTOO);
125    }
126
127    #[test]
128    fn test_bad_nested_atom() {
129        let buf = vec![
130            0x00, 0x00, 0x00, 28, 0xA9, 0x74, 0x6F, 0x6F, 0x00, 0x00, 0x00, 0x1C, b'd', b'a', b't',
131            b'x', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, b't', b'e', b's', b't',
132        ];
133
134        let parse_result = Tool::decode(&mut buf.as_slice());
135        assert!(parse_result.is_err());
136        match parse_result.err().unwrap() {
137            Error::UnexpectedBox(four_cc) => {
138                assert_eq!(four_cc, FourCC::new(b"datx"));
139            }
140            _ => {
141                panic!("unexpected error");
142            }
143        }
144    }
145
146    #[test]
147    fn test_bad_type_indicator() {
148        let buf = vec![
149            0x00, 0x00, 0x00, 28, 0xA9, 0x74, 0x6F, 0x6F, 0x00, 0x00, 0x00, 0x1C, b'd', b'a', b't',
150            b'a', 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, b't', b'e', b's', b't',
151        ];
152
153        let parse_result = Tool::decode(&mut buf.as_slice());
154        assert!(parse_result.is_err());
155        match parse_result.err().unwrap() {
156            Error::Unsupported(s) => {
157                assert_eq!(s, "Only UTF-8 text is supported in ilst tool box")
158            }
159            _ => {
160                panic!("unexpected error");
161            }
162        }
163    }
164}