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

1use crate::*;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub struct Flac {
6    pub audio: Audio,
7    pub dfla: Dfla,
8}
9
10impl Atom for Flac {
11    const KIND: FourCC = FourCC::new(b"fLaC");
12
13    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
14        let audio = Audio::decode(buf)?;
15
16        let mut dfla = None;
17        while let Some(atom) = Any::decode_maybe(buf)? {
18            match atom {
19                Any::Dfla(atom) => dfla = atom.into(),
20                _ => tracing::warn!("unknown atom: {:?}", atom),
21            }
22        }
23
24        Ok(Self {
25            audio,
26            dfla: dfla.ok_or(Error::MissingBox(Dfla::KIND))?,
27        })
28    }
29
30    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
31        self.audio.encode(buf)?;
32        self.dfla.encode(buf)?;
33        Ok(())
34    }
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39pub enum FlacMetadataBlock {
40    StreamInfo {
41        minimum_block_size: u16,
42        maximum_block_size: u16,
43        minimum_frame_size: u24,
44        maximum_frame_size: u24,
45        sample_rate: u32,
46        num_channels_minus_one: u8,
47        bits_per_sample_minus_one: u8,
48        number_of_interchannel_samples: u64,
49        md5_checksum: Vec<u8>,
50    },
51    Padding,
52    Application,
53    SeekTable,
54    VorbisComment {
55        vendor_string: String,
56        comments: Vec<String>,
57    },
58    CueSheet,
59    Picture,
60    Reserved,
61    Forbidden,
62}
63
64impl FlacMetadataBlock {
65    fn encode_initial_byte<B: BufMut>(buf: &mut B, block_type: u8, is_last: bool) -> Result<()> {
66        match is_last {
67            true => (0x80 | block_type).encode(buf),
68            false => block_type.encode(buf),
69        }
70    }
71    fn encode<B: BufMut>(&self, buf: &mut B, is_last: bool) -> Result<()> {
72        match self {
73            FlacMetadataBlock::StreamInfo {
74                minimum_block_size,
75                maximum_block_size,
76                minimum_frame_size,
77                maximum_frame_size,
78                sample_rate,
79                num_channels_minus_one,
80                bits_per_sample_minus_one,
81                number_of_interchannel_samples,
82                md5_checksum,
83            } => {
84                Self::encode_initial_byte(buf, 0u8, is_last)?;
85                // Add a u24 length placeholder
86                u24::from([0u8, 0u8, 0u8]).encode(buf)?;
87                let length_position = buf.len();
88                minimum_block_size.encode(buf)?;
89                maximum_block_size.encode(buf)?;
90                minimum_frame_size.encode(buf)?;
91                maximum_frame_size.encode(buf)?;
92                (((*sample_rate as u64) << 44)
93                    | ((*num_channels_minus_one as u64) << 41)
94                    | ((*bits_per_sample_minus_one as u64) << 36)
95                    | number_of_interchannel_samples)
96                    .encode(buf)?;
97                if md5_checksum.len() != 16 {
98                    return Err(Error::MissingContent(
99                        "StreamInfo.md5_checksum must be 16 bytes",
100                    ));
101                }
102                md5_checksum.encode(buf)?;
103                let length: u24 = ((buf.len() - length_position) as u32)
104                    .try_into()
105                    .map_err(|_| Error::TooLarge(Dfla::KIND))?;
106                buf.set_slice(length_position - 3, &length.to_be_bytes());
107            }
108            FlacMetadataBlock::Padding => { /* cannot write this yet */ }
109            FlacMetadataBlock::Application => { /* cannot write this yet */ }
110            FlacMetadataBlock::SeekTable => { /* cannot write this yet */ }
111            FlacMetadataBlock::VorbisComment {
112                vendor_string,
113                comments,
114            } => {
115                Self::encode_initial_byte(buf, 4u8, is_last)?;
116
117                // Add a u24 length placeholder
118                u24::from([0u8, 0u8, 0u8]).encode(buf)?;
119                let length_position = buf.len();
120                let vendor_string_bytes = vendor_string.as_bytes();
121                let vendor_string_len: u32 = vendor_string_bytes.len() as u32;
122                vendor_string_len.to_le_bytes().encode(buf)?;
123                vendor_string_bytes.encode(buf)?;
124                let number_of_comments: u32 = comments.len() as u32;
125                number_of_comments.to_le_bytes().encode(buf)?;
126                for comment in comments {
127                    let comment_bytes = comment.as_bytes();
128                    let comment_len: u32 = comment_bytes.len() as u32;
129                    comment_len.to_le_bytes().encode(buf)?;
130                    comment_bytes.encode(buf)?;
131                }
132                let length: u24 = ((buf.len() - length_position) as u32)
133                    .try_into()
134                    .map_err(|_| Error::TooLarge(Dfla::KIND))?;
135                buf.set_slice(length_position - 3, &length.to_be_bytes());
136            }
137            FlacMetadataBlock::CueSheet => { /* cannot write this yet */ }
138            FlacMetadataBlock::Picture => { /* cannot write this yet */ }
139            FlacMetadataBlock::Reserved => { /* No way to write this */ }
140            FlacMetadataBlock::Forbidden => { /* No way to write this */ }
141        }
142        Ok(())
143    }
144}
145
146// FLAC specific data
147#[derive(Debug, Clone, PartialEq, Eq)]
148#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
149pub struct Dfla {
150    pub blocks: Vec<FlacMetadataBlock>,
151}
152
153fn parse_stream_info(arr: &[u8]) -> Result<FlacMetadataBlock> {
154    // See RFC 9639 Section 8.2
155    let buf = &mut std::io::Cursor::new(arr);
156    let minimum_block_size = u16::decode(buf)?;
157    let maximum_block_size = u16::decode(buf)?;
158    let minimum_frame_size = u24::decode(buf)?;
159    let maximum_frame_size = u24::decode(buf)?;
160    let temp64 = u64::decode(buf)?;
161    let sample_rate: u32 = (temp64 >> 44) as u32; // 20 bits
162    let num_channels_minus_one: u8 = ((temp64 >> 41) & 0x07) as u8; // 3 bits
163    let bits_per_sample_minus_one: u8 = ((temp64 >> 36) & 0x1F) as u8; // 5 bits
164    let number_of_interchannel_samples: u64 = temp64 & 0x0000_000F_FFFF_FFFF; // 36 bits
165    let md5_checksum: Vec<u8> = Vec::decode_exact(buf, 16)?;
166    Ok(FlacMetadataBlock::StreamInfo {
167        minimum_block_size,
168        maximum_block_size,
169        minimum_frame_size,
170        maximum_frame_size,
171        sample_rate,
172        num_channels_minus_one,
173        bits_per_sample_minus_one,
174        number_of_interchannel_samples,
175        md5_checksum,
176    })
177}
178
179fn parse_vorbis_comment(arr: &[u8]) -> Result<FlacMetadataBlock> {
180    // See RFC 9639 Section 8.6, and D.2.5 for an example
181    // Vorbis comments in FLAC use little endian, for Vorbis compatibility
182    let buf = &mut std::io::Cursor::new(arr);
183    let vendor_string_length = u32::from_le_bytes(<[u8; 4]>::decode(buf)?) as usize;
184    let vendor_string_bytes: Vec<u8> = Vec::decode_exact(buf, vendor_string_length)?;
185    let vendor_string = String::from_utf8_lossy(&vendor_string_bytes)
186        .trim_end_matches('\0')
187        .to_string();
188    let number_of_fields = u32::from_le_bytes(<[u8; 4]>::decode(buf)?) as usize;
189    let mut comments = Vec::with_capacity(number_of_fields);
190    for _ in 0..number_of_fields {
191        let field_length = u32::from_le_bytes(<[u8; 4]>::decode(buf)?) as usize;
192        let field_bytes: Vec<u8> = Vec::decode_exact(buf, field_length)?;
193        let comment = String::from_utf8_lossy(&field_bytes)
194            .trim_end_matches('\0')
195            .to_string();
196        comments.push(comment);
197    }
198    Ok(FlacMetadataBlock::VorbisComment {
199        vendor_string,
200        comments,
201    })
202}
203
204impl AtomExt for Dfla {
205    type Ext = ();
206
207    const KIND_EXT: FourCC = FourCC::new(b"dfLa");
208
209    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: ()) -> Result<Self> {
210        let mut is_last_block = false;
211        let mut metadata_blocks = Vec::new();
212        while buf.has_remaining() && !is_last_block {
213            let initial_bytes = u32::decode(buf)?;
214            is_last_block = (initial_bytes & 0x80_00_00_00) == 0x80_00_00_00;
215            let block_type = ((initial_bytes >> 24) & 0x7f) as u8;
216            let length = initial_bytes & 0x00_FF_FF_FF;
217            let block_data: Vec<u8> = Vec::decode_exact(buf, length as usize)?;
218            let metadata_block = match block_type {
219                0 => parse_stream_info(&block_data)?,
220                1 => FlacMetadataBlock::Padding,
221                2 => FlacMetadataBlock::Application,
222                3 => FlacMetadataBlock::SeekTable,
223                4 => parse_vorbis_comment(&block_data)?,
224                5 => FlacMetadataBlock::CueSheet,
225                6 => FlacMetadataBlock::Picture,
226                7..=126 => FlacMetadataBlock::Reserved,
227                127 => FlacMetadataBlock::Forbidden,
228                _ => unreachable!("FLAC Metadata Block type is only 7 bits"),
229            };
230            metadata_blocks.push(metadata_block);
231        }
232        Ok(Dfla {
233            blocks: metadata_blocks,
234        })
235    }
236
237    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<()> {
238        if self.blocks.is_empty() {
239            return Err(Error::MissingContent("Streaminfo"));
240        }
241        for (i, metadata_block) in self.blocks.iter().enumerate() {
242            let is_last = i + 1 == self.blocks.len();
243            metadata_block.encode(buf, is_last)?;
244        }
245        Ok(())
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use super::*;
252
253    // Streaminfo metadata block only
254    const ENCODED_DFLA: &[u8] = &[
255        0x00, 0x00, 0x00, 0x32, 0x64, 0x66, 0x4c, 0x61, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
256        0x22, 0x12, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x23, 0x8e, 0x0a, 0xc4, 0x43, 0x70,
257        0x00, 0x01, 0xd8, 0x00, 0x75, 0x30, 0x88, 0x11, 0x2d, 0xd5, 0x7a, 0x13, 0xe7, 0xf7, 0x22,
258        0xd0, 0xee, 0x56, 0xae, 0xa3,
259    ];
260
261    #[test]
262    fn test_dfla_decode() {
263        let buf: &mut std::io::Cursor<&[u8]> = &mut std::io::Cursor::new(ENCODED_DFLA);
264
265        let dfla = Dfla::decode(buf).expect("failed to decode dfLa");
266
267        assert_eq!(
268            dfla,
269            Dfla {
270                blocks: vec![FlacMetadataBlock::StreamInfo {
271                    minimum_block_size: 4608,
272                    maximum_block_size: 4608,
273                    minimum_frame_size: 16u32.try_into().expect("should fit in u24"),
274                    maximum_frame_size: 9102u32.try_into().expect("should fit in u24"),
275                    sample_rate: 44100,
276                    num_channels_minus_one: 1,
277                    bits_per_sample_minus_one: 23,
278                    number_of_interchannel_samples: 120832,
279                    md5_checksum: vec![
280                        117, 48, 136, 17, 45, 213, 122, 19, 231, 247, 34, 208, 238, 86, 174, 163
281                    ]
282                },]
283            }
284        );
285    }
286
287    #[test]
288    fn test_dfla_encode() {
289        let dfla = Dfla {
290            blocks: vec![FlacMetadataBlock::StreamInfo {
291                minimum_block_size: 4608,
292                maximum_block_size: 4608,
293                minimum_frame_size: 16u32.try_into().expect("should fit in u24"),
294                maximum_frame_size: 9102u32.try_into().expect("should fit in u24"),
295                sample_rate: 44100,
296                num_channels_minus_one: 1,
297                bits_per_sample_minus_one: 23,
298                number_of_interchannel_samples: 120832,
299                md5_checksum: vec![
300                    117, 48, 136, 17, 45, 213, 122, 19, 231, 247, 34, 208, 238, 86, 174, 163,
301                ],
302            }],
303        };
304
305        let mut buf = Vec::new();
306        dfla.encode(&mut buf).unwrap();
307
308        assert_eq!(buf.as_slice(), ENCODED_DFLA);
309    }
310
311    // Streaminfo metadata block plus Vorbis Comment metadata block
312    const ENCODED_DFLA_2: &[u8] = &[
313        0x00, 0x00, 0x00, 0x7c, 0x64, 0x66, 0x4c, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
314        0x22, 0x12, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xc4, 0x40, 0x70,
315        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
316        0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x46, 0x20, 0x00, 0x00, 0x00, 0x72, 0x65,
317        0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x6c, 0x69, 0x62, 0x46, 0x4c, 0x41, 0x43,
318        0x20, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x20, 0x32, 0x30, 0x32, 0x33, 0x30, 0x36, 0x32, 0x33,
319        0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x44, 0x45, 0x53, 0x43, 0x52, 0x49, 0x50,
320        0x54, 0x49, 0x4f, 0x4e, 0x3d, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x74, 0x65, 0x73, 0x74, 0x20,
321        0x77, 0x61, 0x76, 0x65,
322    ];
323
324    #[test]
325    fn test_dfla2_decode() {
326        let buf: &mut std::io::Cursor<&[u8]> = &mut std::io::Cursor::new(ENCODED_DFLA_2);
327
328        let dfla = Dfla::decode(buf).expect("failed to decode dfLa");
329
330        assert_eq!(
331            dfla,
332            Dfla {
333                blocks: vec![
334                    FlacMetadataBlock::StreamInfo {
335                        minimum_block_size: 4608,
336                        maximum_block_size: 4608,
337                        minimum_frame_size: 0u32.try_into().expect("should fit in u24"),
338                        maximum_frame_size: 0u32.try_into().expect("should fit in u24"),
339                        sample_rate: 44100,
340                        num_channels_minus_one: 0,
341                        bits_per_sample_minus_one: 7,
342                        number_of_interchannel_samples: 0,
343                        md5_checksum: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
344                    },
345                    FlacMetadataBlock::VorbisComment {
346                        vendor_string: "reference libFLAC 1.4.3 20230623".into(),
347                        comments: vec!["DESCRIPTION=audiotest wave".into()],
348                    },
349                ]
350            }
351        );
352    }
353
354    #[test]
355    fn test_dfla2_encode() {
356        let dfla = Dfla {
357            blocks: vec![
358                FlacMetadataBlock::StreamInfo {
359                    minimum_block_size: 4608,
360                    maximum_block_size: 4608,
361                    minimum_frame_size: 0u32.try_into().expect("should fit in u24"),
362                    maximum_frame_size: 0u32.try_into().expect("should fit in u24"),
363                    sample_rate: 44100,
364                    num_channels_minus_one: 0,
365                    bits_per_sample_minus_one: 7,
366                    number_of_interchannel_samples: 0,
367                    md5_checksum: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
368                },
369                FlacMetadataBlock::VorbisComment {
370                    vendor_string: "reference libFLAC 1.4.3 20230623".into(),
371                    comments: vec!["DESCRIPTION=audiotest wave".into()],
372                },
373            ],
374        };
375
376        let mut buf = Vec::new();
377        dfla.encode(&mut buf).unwrap();
378
379        assert_eq!(buf.as_slice(), ENCODED_DFLA_2);
380    }
381}