mp4_edit/atom/container/
meta.rs1use std::io::Read;
2
3use crate::FourCC;
4
5pub const META: FourCC = FourCC::new(b"meta");
6
7pub const META_VERSION_FLAGS_SIZE: usize = 4;
8
9#[derive(Debug, Clone, PartialEq)]
10pub struct MetaHeader {
11 pub version: u8,
12 pub flags: [u8; 3],
13}
14
15impl MetaHeader {
16 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseMetaError> {
18 if bytes.len() < 4 {
19 return Err(ParseMetaError::InsufficientData);
20 }
21
22 Ok(MetaHeader {
23 version: bytes[0],
24 flags: [bytes[1], bytes[2], bytes[3]],
25 })
26 }
27
28 pub fn from_reader<R: Read>(reader: &mut R) -> Result<Self, ParseMetaError> {
30 let mut header_bytes = [0u8; 4];
31 reader
32 .read_exact(&mut header_bytes)
33 .map_err(|_| ParseMetaError::ReadError)?;
34
35 Ok(MetaHeader {
36 version: header_bytes[0],
37 flags: [header_bytes[1], header_bytes[2], header_bytes[3]],
38 })
39 }
40
41 pub fn flags_as_u32(&self) -> u32 {
43 u32::from_be_bytes([0, self.flags[0], self.flags[1], self.flags[2]])
44 }
45
46 pub fn is_valid_for_apple(&self) -> bool {
48 self.version == 0 && self.flags == [0, 0, 0]
49 }
50
51 pub fn to_bytes(&self) -> [u8; 4] {
53 [self.version, self.flags[0], self.flags[1], self.flags[2]]
54 }
55}
56
57#[derive(Debug, Clone, PartialEq)]
58pub enum ParseMetaError {
59 InsufficientData,
60 ReadError,
61}
62
63impl std::fmt::Display for ParseMetaError {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 match self {
66 ParseMetaError::InsufficientData => {
67 write!(f, "Insufficient data to parse META header")
68 }
69 ParseMetaError::ReadError => write!(f, "Failed to read META header data"),
70 }
71 }
72}
73
74impl std::error::Error for ParseMetaError {}
75
76#[cfg(test)]
77mod tests {
78 use std::io::Cursor;
79
80 use super::*;
81
82 #[test]
83 fn test_parse_valid_apple_meta_header() {
84 let bytes = [0x00, 0x00, 0x00, 0x00]; let header = MetaHeader::from_bytes(&bytes).unwrap();
86
87 assert_eq!(header.version, 0);
88 assert_eq!(header.flags, [0, 0, 0]);
89 assert!(header.is_valid_for_apple());
90 assert_eq!(header.flags_as_u32(), 0);
91 }
92
93 #[test]
94 fn test_parse_non_apple_meta_header() {
95 let bytes = [0x01, 0x00, 0x00, 0x01]; let header = MetaHeader::from_bytes(&bytes).unwrap();
97
98 assert_eq!(header.version, 1);
99 assert_eq!(header.flags, [0, 0, 1]);
100 assert!(!header.is_valid_for_apple());
101 assert_eq!(header.flags_as_u32(), 1);
102 }
103
104 #[test]
105 fn test_parse_from_reader() {
106 let data = [0x00, 0x00, 0x00, 0x00];
107 let mut cursor = Cursor::new(&data);
108 let header = MetaHeader::from_reader(&mut cursor).unwrap();
109
110 assert_eq!(header.version, 0);
111 assert_eq!(header.flags, [0, 0, 0]);
112 }
113
114 #[test]
115 fn test_insufficient_data() {
116 let bytes = [0x00, 0x00]; let result = MetaHeader::from_bytes(&bytes);
118
119 assert!(matches!(result, Err(ParseMetaError::InsufficientData)));
120 }
121
122 #[test]
123 fn test_round_trip() {
124 let original = MetaHeader {
125 version: 0,
126 flags: [0x12, 0x34, 0x56],
127 };
128
129 let bytes = original.to_bytes();
130 let parsed = MetaHeader::from_bytes(&bytes).unwrap();
131
132 assert_eq!(original, parsed);
133 }
134
135 #[test]
136 fn test_flags_as_u32() {
137 let header = MetaHeader {
138 version: 0,
139 flags: [0x01, 0x02, 0x03],
140 };
141
142 assert_eq!(header.flags_as_u32(), 0x010203);
143 }
144}