mp4_atom/moov/trak/mdia/
hdlr.rs

1use crate::*;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub struct Hdlr {
6    pub handler: FourCC,
7    pub name: String,
8}
9
10impl Default for Hdlr {
11    fn default() -> Self {
12        Hdlr {
13            handler: FourCC::new(b"none"),
14            name: String::new(),
15        }
16    }
17}
18
19impl AtomExt for Hdlr {
20    type Ext = ();
21    const KIND_EXT: FourCC = FourCC::new(b"hdlr");
22
23    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: ()) -> Result<Self> {
24        u32::decode(buf)?; // pre-defined
25        let handler = FourCC::decode(buf)?;
26
27        <[u8; 12]>::decode(buf)?; // reserved
28
29        let name = String::decode(buf)?;
30
31        // Skip any trailing padding
32        if buf.has_remaining() {
33            tracing::warn!("Skipped {} extra trailing bytes in hdlr", buf.remaining());
34            buf.advance(buf.remaining());
35        }
36
37        Ok(Hdlr { handler, name })
38    }
39
40    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<()> {
41        0u32.encode(buf)?; // pre-defined
42        self.handler.encode(buf)?;
43
44        // 12 bytes reserved
45        [0u8; 12].encode(buf)?;
46
47        self.name.as_str().encode(buf)?;
48
49        Ok(())
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_hdlr() {
59        let expected = Hdlr {
60            handler: FourCC::new(b"vide"),
61            name: String::from("VideoHandler"),
62        };
63        let mut buf = Vec::new();
64        expected.encode(&mut buf).unwrap();
65
66        let mut buf = buf.as_ref();
67        let decoded = Hdlr::decode(&mut buf).unwrap();
68        assert_eq!(decoded, expected);
69    }
70
71    #[test]
72    fn test_hdlr_empty() {
73        let expected = Hdlr {
74            handler: FourCC::new(b"vide"),
75            name: String::new(),
76        };
77        let mut buf = Vec::new();
78        expected.encode(&mut buf).unwrap();
79
80        let mut buf = buf.as_ref();
81        let decoded = Hdlr::decode(&mut buf).unwrap();
82        assert_eq!(decoded, expected);
83    }
84
85    #[test]
86    fn test_hdlr_with_trailing_bytes() {
87        // Test that we can decode hdlr boxes with extra padding bytes after the name
88        // Some encoders add extra null bytes or padding at the end of the box
89
90        let mut buf = Vec::new();
91
92        // hdlr box header
93        buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); // size (will be fixed later)
94        buf.extend_from_slice(b"hdlr");
95
96        // version and flags
97        buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
98
99        // pre-defined
100        buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
101
102        // handler type
103        buf.extend_from_slice(b"vide");
104
105        // reserved (12 bytes)
106        buf.extend_from_slice(&[0x00; 12]);
107
108        // name (null-terminated string)
109        buf.extend_from_slice(b"VideoHandler\0");
110
111        // Add extra trailing bytes that should be skipped
112        buf.extend_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD]); // 4 extra bytes
113
114        // Fix the size
115        let size = buf.len() as u32;
116        buf[0..4].copy_from_slice(&size.to_be_bytes());
117
118        // Decode
119        let mut cursor = std::io::Cursor::new(&buf);
120        let decoded = Hdlr::decode(&mut cursor).expect("failed to decode hdlr with trailing bytes");
121
122        // Verify the decoded data (trailing bytes should be ignored)
123        assert_eq!(decoded.handler, FourCC::new(b"vide"));
124        assert_eq!(decoded.name, "VideoHandler");
125
126        // Verify we've consumed all the data
127        assert_eq!(cursor.position(), buf.len() as u64);
128    }
129
130    #[test]
131    fn test_hdlr_with_multiple_trailing_nulls() {
132        // Test with multiple null bytes at the end (common in some encoders)
133
134        let mut buf = Vec::new();
135
136        // hdlr box header
137        buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); // size
138        buf.extend_from_slice(b"hdlr");
139
140        // version and flags
141        buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
142
143        // pre-defined
144        buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
145
146        // handler type
147        buf.extend_from_slice(b"soun");
148
149        // reserved (12 bytes)
150        buf.extend_from_slice(&[0x00; 12]);
151
152        // name
153        buf.extend_from_slice(b"SoundHandler\0");
154
155        // Add multiple null bytes at the end
156        buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
157
158        // Fix the size
159        let size = buf.len() as u32;
160        buf[0..4].copy_from_slice(&size.to_be_bytes());
161
162        // Decode
163        let mut cursor = std::io::Cursor::new(&buf);
164        let decoded = Hdlr::decode(&mut cursor).expect("failed to decode hdlr with trailing nulls");
165
166        // Verify
167        assert_eq!(decoded.handler, FourCC::new(b"soun"));
168        assert_eq!(decoded.name, "SoundHandler");
169        assert_eq!(cursor.position(), buf.len() as u64);
170    }
171
172    #[test]
173    fn test_hdlr_roundtrip_with_trailing_bytes() {
174        // Test that our encoder doesn't add trailing bytes,
175        // but our decoder can handle them
176
177        let original = Hdlr {
178            handler: FourCC::new(b"meta"),
179            name: "MetaHandler".to_string(),
180        };
181
182        // Encode (should not have trailing bytes)
183        let mut encoded = Vec::new();
184        original.encode(&mut encoded).unwrap();
185
186        // Decode the clean version
187        let mut cursor = std::io::Cursor::new(&encoded);
188        let decoded = Hdlr::decode(&mut cursor).expect("failed to decode clean hdlr");
189        assert_eq!(decoded, original);
190
191        // Now manually add trailing bytes to the encoded data
192        let box_end = cursor.position() as usize;
193        let mut encoded_with_trash = encoded[..box_end].to_vec();
194
195        // Add some trash bytes
196        encoded_with_trash.extend_from_slice(&[0xFF, 0xEE, 0xDD]);
197
198        // Update the size in the header
199        let new_size = encoded_with_trash.len() as u32;
200        encoded_with_trash[0..4].copy_from_slice(&new_size.to_be_bytes());
201
202        // Decode again - should still work and ignore trash
203        let mut cursor2 = std::io::Cursor::new(&encoded_with_trash);
204        let decoded2 = Hdlr::decode(&mut cursor2).expect("failed to decode hdlr with added trash");
205        assert_eq!(decoded2, original);
206        assert_eq!(cursor2.position(), encoded_with_trash.len() as u64);
207    }
208}