symphonia_metadata/
flac.rs

1// Symphonia
2// Copyright (c) 2019-2024 The Project Symphonia Developers.
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
8//! Readers for FLAC comment and picture metadata blocks.
9
10use std::num::NonZeroU32;
11
12use symphonia_core::errors::{decode_error, Result};
13use symphonia_core::io::ReadBytes;
14use symphonia_core::meta::{ColorMode, MetadataBuilder, Size, StandardTagKey, Tag, Value, Visual};
15
16use crate::{id3v2, vorbis};
17
18/// Converts a string of bytes to an ASCII string if all characters are within the printable ASCII
19/// range. If a null byte is encounted, the string terminates at that point.
20fn printable_ascii_to_string(bytes: &[u8]) -> Option<String> {
21    let mut result = String::with_capacity(bytes.len());
22
23    for c in bytes {
24        match c {
25            0x00 => break,
26            0x20..=0x7e => result.push(char::from(*c)),
27            _ => return None,
28        }
29    }
30
31    Some(result)
32}
33
34/// Read a comment metadata block.
35pub fn read_comment_block<B: ReadBytes>(
36    reader: &mut B,
37    metadata: &mut MetadataBuilder,
38) -> Result<()> {
39    vorbis::read_comment_no_framing(reader, metadata)
40}
41
42/// Read a picture metadata block.
43pub fn read_picture_block<B: ReadBytes>(
44    reader: &mut B,
45    metadata: &mut MetadataBuilder,
46) -> Result<()> {
47    let type_enc = reader.read_be_u32()?;
48
49    // Read the Media Type length in bytes.
50    let media_type_len = reader.read_be_u32()? as usize;
51
52    // Read the Media Type bytes
53    let mut media_type_buf = vec![0u8; media_type_len];
54    reader.read_buf_exact(&mut media_type_buf)?;
55
56    // Convert Media Type bytes to an ASCII string. Non-printable ASCII characters are invalid.
57    let media_type = match printable_ascii_to_string(&media_type_buf) {
58        Some(s) => s,
59        None => return decode_error("meta (flac): picture mime-type contains invalid characters"),
60    };
61
62    // Read the description length in bytes.
63    let desc_len = reader.read_be_u32()? as usize;
64
65    // Read the description bytes.
66    let mut desc_buf = vec![0u8; desc_len];
67    reader.read_buf_exact(&mut desc_buf)?;
68
69    let desc = String::from_utf8_lossy(&desc_buf);
70
71    // Convert description bytes into a standard Vorbis DESCRIPTION tag.
72    let tags = vec![Tag::new(Some(StandardTagKey::Description), "DESCRIPTION", Value::from(desc))];
73
74    // Read the width, and height of the visual.
75    let width = reader.read_be_u32()?;
76    let height = reader.read_be_u32()?;
77
78    // If either the width or height is 0, then the size is invalid.
79    let dimensions = if width > 0 && height > 0 { Some(Size { width, height }) } else { None };
80
81    // Read bits-per-pixel of the visual.
82    let bits_per_pixel = NonZeroU32::new(reader.read_be_u32()?);
83
84    // Indexed colours is only valid for image formats that use an indexed colour palette. If it is
85    // 0, the image does not used indexed colours.
86    let indexed_colours_enc = reader.read_be_u32()?;
87
88    let color_mode = match indexed_colours_enc {
89        0 => Some(ColorMode::Discrete),
90        _ => Some(ColorMode::Indexed(NonZeroU32::new(indexed_colours_enc).unwrap())),
91    };
92
93    // Read the image data
94    let data_len = reader.read_be_u32()? as usize;
95    let data = reader.read_boxed_slice_exact(data_len)?;
96
97    metadata.add_visual(Visual {
98        media_type,
99        dimensions,
100        bits_per_pixel,
101        color_mode,
102        usage: id3v2::util::apic_picture_type_to_visual_key(type_enc),
103        tags,
104        data,
105    });
106
107    Ok(())
108}