Skip to main content

symphonia_core/
meta.rs

1// Symphonia
2// Copyright (c) 2019-2026 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//! The `meta` module defines basic metadata elements, and management structures.
9//!
10//! # Tags
11//!
12//! Within the context of media, a tag is single piece of metadata about the media as a whole, or a
13//! track within the media. The storage of tags, their structure, and organization, varies based on
14//! the metadata/tagging format.
15//!
16//! The [`Tag`] structure represents a single tag, and abstracts over the differences in tagging
17//! formats. `Tag` is a composition of a mandatory raw tag, and an optional standard tag.
18//!
19//! ## Raw Tags
20//!
21//! A [`RawTag`] stores a tag in a data format that matches as closely as possible to the format of
22//! the tag as it was written. The data format depends on the tagging format, and the writer of the
23//! tag.
24//!
25//! A `RawTag` consists of a mandatory key-value pair. For most tagging formats, this is sufficient
26//! to faithfully represent the original tag, however, for some more structured tagging formats, a
27//! set of additional key-value pairs ([`RawTagSubField`]) may be populated.
28//!
29//! The meaning of the tag can be derived from its key, however, the key may be named differently
30//! based on the underlying tagging format and the writer of the tag.
31//!
32//! Raw tags can be ignored by most tag consumers. Instead, standard tags should be preferred.
33//!
34//! ## Standard Tags
35//!
36//! A [`StandardTag`] is a parsed representation of a tag. Unlike a raw tag, a standard tag has a
37//! well-defined data type and meaning.
38//!
39//! A metadata reader will assign a `StandardTag` to a `Tag` if it is able to identify the meaning
40//! of the `RawTag`, and parse its value. If the `RawTag` maps to multiple `StandardTag`s, then
41//! the `Tag` (along with the `RawTag`) will be duplicated for each `StandardTag` with each instance
42//! being assigned one `StandardTag`.
43//!
44//! An end-user should prefer consuming standard tags over raw tags.
45//!
46//! ## Storage Efficiency
47//!
48//! In many cases, the value of a `RawTag` will be the same as the `StandardTag`. Since a value may
49//! be large, duplicating it could be wasteful. For this reason, string and binary data values are
50//! stored using an [`Arc`].
51
52use std::borrow::Cow;
53use std::collections::VecDeque;
54use std::convert::From;
55use std::fmt;
56use std::num::NonZeroU8;
57use std::sync::Arc;
58
59use crate::common::{FourCc, Limit};
60use crate::errors::Result;
61use crate::io::MediaSourceStream;
62use crate::units::Time;
63
64/// A `MetadataId` is a unique identifier used to identify a specific metadata format.
65#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
66pub struct MetadataId(u32);
67
68impl MetadataId {
69    /// Create a new metadata ID from a FourCC.
70    pub const fn new(cc: FourCc) -> MetadataId {
71        // A FourCc always only contains ASCII characters. Therefore, the upper bits are always 0.
72        Self(0x8000_0000 | u32::from_be_bytes(cc.get()))
73    }
74}
75
76impl From<FourCc> for MetadataId {
77    fn from(value: FourCc) -> Self {
78        MetadataId::new(value)
79    }
80}
81
82impl fmt::Display for MetadataId {
83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        write!(f, "{:#x}", self.0)
85    }
86}
87
88/// Null metadata format
89pub const METADATA_ID_NULL: MetadataId = MetadataId(0x0);
90
91/// Basic information about a metadata format.
92#[derive(Copy, Clone, Debug)]
93pub struct MetadataInfo {
94    /// The `MetadataType` identifier.
95    pub metadata: MetadataId,
96    /// A short ASCII-only string identifying the format.
97    pub short_name: &'static str,
98    /// A longer, more descriptive, string identifying the format.
99    pub long_name: &'static str,
100}
101
102/// A common set of options that all metadata readers use.
103#[non_exhaustive]
104#[derive(Copy, Clone, Debug, Default)]
105pub struct MetadataOptions {
106    /// The maximum size in bytes that a tag may occupy in memory once decoded. Tags exceeding this
107    /// limit will be skipped by the demuxer. Take note that tags in memory are stored as UTF-8 and
108    /// therefore may occupy more than one byte per character.
109    ///
110    /// Default: `Limit::Default` (a reasonable limit chosen by the reader)
111    pub limit_tag_bytes: Limit,
112
113    /// The maximum size in bytes that a visual (picture) may occupy.
114    ///
115    /// Default: `Limit::Default` (a reasonable limit chosen by the reader)
116    pub limit_visual_bytes: Limit,
117}
118
119impl MetadataOptions {
120    /// The maximum size in bytes that a tag may occupy in memory once decoded. Tags exceeding this
121    /// limit will be skipped by the demuxer. Take note that tags in memory are stored as UTF-8 and
122    /// therefore may occupy more than one byte per character.
123    ///
124    /// Default: `Limit::Default` (a reasonable limit chosen by the reader)
125    pub fn limit_tag_bytes(mut self, limit: Limit) -> Self {
126        self.limit_tag_bytes = limit;
127        self
128    }
129
130    /// The maximum size in bytes that a visual (picture) may occupy.
131    ///
132    /// Default: `Limit::Default` (a reasonable limit chosen by the reader)
133    pub fn limit_visual_bytes(mut self, limit: Limit) -> Self {
134        self.limit_visual_bytes = limit;
135        self
136    }
137}
138
139/// `StandardVisualKey` is an enumeration providing standardized keys for common visual dispositions.
140/// A demuxer may assign a `StandardVisualKey` to a `Visual` if the disposition of the attached
141/// visual is known and can be mapped to a standard key.
142///
143/// The visual types listed here are derived from, though do not entirely cover, the ID3v2 APIC
144/// frame specification.
145#[non_exhaustive]
146#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
147pub enum StandardVisualKey {
148    FileIcon,
149    OtherIcon,
150    FrontCover,
151    BackCover,
152    Leaflet,
153    Media,
154    LeadArtistPerformerSoloist,
155    ArtistPerformer,
156    Conductor,
157    BandOrchestra,
158    Composer,
159    Lyricist,
160    RecordingLocation,
161    RecordingSession,
162    Performance,
163    ScreenCapture,
164    Illustration,
165    BandArtistLogo,
166    PublisherStudioLogo,
167    Other,
168}
169
170/// A content advisory.
171#[derive(Clone, Debug, PartialEq, Eq, Hash)]
172pub enum ContentAdvisory {
173    /// The content is not explicit.
174    None,
175    /// The content is explicit.
176    Explicit,
177    /// The content has been cleaned/censored.
178    Censored,
179}
180
181/// A standard tag is an enumeration of well-defined and well-known tags with parsed values.
182#[non_exhaustive]
183#[derive(Clone, Debug, PartialEq, Eq, Hash)]
184pub enum StandardTag {
185    AccurateRipCount(Arc<String>),
186    AccurateRipCountAllOffsets(Arc<String>),
187    AccurateRipCountWithOffset(Arc<String>),
188    AccurateRipCrc(Arc<String>),
189    AccurateRipDiscId(Arc<String>),
190    AccurateRipId(Arc<String>),
191    AccurateRipOffset(Arc<String>),
192    AccurateRipResult(Arc<String>),
193    AccurateRipTotal(Arc<String>),
194    AcoustIdFingerprint(Arc<String>),
195    AcoustIdId(Arc<String>),
196    Actor(Arc<String>),
197    Album(Arc<String>),
198    AlbumArtist(Arc<String>),
199    Arranger(Arc<String>),
200    ArtDirector(Arc<String>),
201    Artist(Arc<String>),
202    AssistantDirector(Arc<String>),
203    Author(Arc<String>),
204    Bpm(u64),
205    CdToc(Arc<String>),
206    CdTrackIndex(u8),
207    ChapterTitle(Arc<String>),
208    Choregrapher(Arc<String>),
209    Cinematographer(Arc<String>),
210    CollectionTitle(Arc<String>),
211    Comment(Arc<String>),
212    CompilationFlag(bool),
213    Composer(Arc<String>),
214    Conductor(Arc<String>),
215    ContentAdvisory(ContentAdvisory),
216    ContentRating(Arc<String>),
217    ContentType(Arc<String>),
218    Coproducer(Arc<String>),
219    Copyright(Arc<String>),
220    CostumeDesigner(Arc<String>),
221    CueToolsDbDiscConfidence(Arc<String>),
222    CueToolsDbTrackConfidence(Arc<String>),
223    Description(Arc<String>),
224    DigitizedDate(Arc<String>),
225    Director(Arc<String>),
226    DiscNumber(u64),
227    DiscSubtitle(Arc<String>),
228    DiscTotal(u64),
229    Distributor(Arc<String>),
230    EditedBy(Arc<String>),
231    EditionTitle(Arc<String>),
232    EncodedBy(Arc<String>),
233    Encoder(Arc<String>),
234    EncoderSettings(Arc<String>),
235    EncodingDate(Arc<String>),
236    Engineer(Arc<String>),
237    Ensemble(Arc<String>),
238    ExecutiveProducer(Arc<String>),
239    Genre(Arc<String>),
240    Grouping(Arc<String>),
241    IdentAsin(Arc<String>),
242    IdentBarcode(Arc<String>),
243    IdentCatalogNumber(Arc<String>),
244    IdentEanUpn(Arc<String>),
245    IdentIsbn(Arc<String>),
246    IdentIsrc(Arc<String>),
247    IdentLccn(Arc<String>),
248    IdentPn(Arc<String>),
249    IdentPodcast(Arc<String>),
250    IdentUpc(Arc<String>),
251    ImdbTitleId(Arc<String>),
252    InitialKey(Arc<String>),
253    InternetRadioName(Arc<String>),
254    InternetRadioOwner(Arc<String>),
255    Keywords(Arc<String>),
256    Label(Arc<String>),
257    LabelCode(Arc<String>),
258    Language(Arc<String>),
259    License(Arc<String>),
260    Lyricist(Arc<String>),
261    Lyrics(Arc<String>),
262    Measure(Arc<String>),
263    MediaFormat(Arc<String>),
264    MixDj(Arc<String>),
265    MixEngineer(Arc<String>),
266    Mood(Arc<String>),
267    MovementName(Arc<String>),
268    MovementNumber(u64),
269    MovementTotal(u64),
270    MovieTitle(Arc<String>),
271    Mp3GainAlbumMinMax(Arc<String>),
272    Mp3GainMinMax(Arc<String>),
273    Mp3GainUndo(Arc<String>),
274    MusicBrainzAlbumArtistId(Arc<String>),
275    MusicBrainzAlbumId(Arc<String>),
276    MusicBrainzArtistId(Arc<String>),
277    MusicBrainzDiscId(Arc<String>),
278    MusicBrainzGenreId(Arc<String>),
279    MusicBrainzLabelId(Arc<String>),
280    MusicBrainzOriginalAlbumId(Arc<String>),
281    MusicBrainzOriginalArtistId(Arc<String>),
282    MusicBrainzRecordingId(Arc<String>),
283    MusicBrainzReleaseGroupId(Arc<String>),
284    MusicBrainzReleaseStatus(Arc<String>),
285    MusicBrainzReleaseTrackId(Arc<String>),
286    MusicBrainzReleaseType(Arc<String>),
287    MusicBrainzTrackId(Arc<String>),
288    MusicBrainzTrmId(Arc<String>),
289    MusicBrainzWorkId(Arc<String>),
290    Narrator(Arc<String>),
291    Opus(Arc<String>),
292    OpusNumber(u64),
293    OriginalAlbum(Arc<String>),
294    OriginalArtist(Arc<String>),
295    OriginalFile(Arc<String>),
296    OriginalLyricist(Arc<String>),
297    OriginalRecordingDate(Arc<String>),
298    OriginalRecordingTime(Arc<String>),
299    OriginalRecordingYear(u16),
300    OriginalReleaseDate(Arc<String>),
301    OriginalReleaseTime(Arc<String>),
302    OriginalReleaseYear(u16),
303    OriginalWriter(Arc<String>),
304    Owner(Arc<String>),
305    Part(Arc<String>),
306    PartNumber(u64),
307    PartTitle(Arc<String>),
308    PartTotal(u64),
309    Performer(Arc<String>),
310    Period(Arc<String>),
311    PlayCounter(u64),
312    PodcastCategory(Arc<String>),
313    PodcastDescription(Arc<String>),
314    PodcastFlag(bool),
315    PodcastKeywords(Arc<String>),
316    Producer(Arc<String>),
317    ProductionCopyright(Arc<String>),
318    ProductionDesigner(Arc<String>),
319    ProductionStudio(Arc<String>),
320    PurchaseDate(Arc<String>),
321    Rating(u32), // In PPM.
322    RecordingDate(Arc<String>),
323    RecordingLocation(Arc<String>),
324    RecordingTime(Arc<String>),
325    RecordingYear(u16),
326    ReleaseCountry(Arc<String>),
327    ReleaseDate(Arc<String>),
328    ReleaseTime(Arc<String>),
329    ReleaseYear(u16),
330    Remixer(Arc<String>),
331    ReplayGainAlbumGain(Arc<String>),
332    ReplayGainAlbumPeak(Arc<String>),
333    ReplayGainAlbumRange(Arc<String>),
334    ReplayGainReferenceLoudness(Arc<String>),
335    ReplayGainTrackGain(Arc<String>),
336    ReplayGainTrackPeak(Arc<String>),
337    ReplayGainTrackRange(Arc<String>),
338    ScreenplayAuthor(Arc<String>),
339    Script(Arc<String>),
340    Soloist(Arc<String>),
341    SortAlbum(Arc<String>),
342    SortAlbumArtist(Arc<String>),
343    SortArtist(Arc<String>),
344    SortCollectionTitle(Arc<String>),
345    SortComposer(Arc<String>),
346    SortEditionTitle(Arc<String>),
347    SortMovieTitle(Arc<String>),
348    SortOpusTitle(Arc<String>),
349    SortPartTitle(Arc<String>),
350    SortTrackTitle(Arc<String>),
351    SortTvEpisodeTitle(Arc<String>),
352    SortTvSeasonTitle(Arc<String>),
353    SortTvSeriesTitle(Arc<String>),
354    SortVolumeTitle(Arc<String>),
355    Subject(Arc<String>),
356    Summary(Arc<String>),
357    Synopsis(Arc<String>),
358    TaggingDate(Arc<String>),
359    TermsOfUse(Arc<String>),
360    Thanks(Arc<String>),
361    TmdbMovieId(Arc<String>),
362    TmdbSeriesId(Arc<String>),
363    TrackNumber(u64),
364    TrackSubtitle(Arc<String>),
365    TrackTitle(Arc<String>),
366    TrackTotal(u64),
367    Tuning(Arc<String>),
368    TvdbEpisodeId(Arc<String>),
369    TvdbMovieId(Arc<String>),
370    TvdbSeriesId(Arc<String>),
371    TvEpisodeNumber(u64),
372    TvEpisodeTitle(Arc<String>),
373    TvEpisodeTotal(u64),
374    TvNetwork(Arc<String>),
375    TvSeasonNumber(u64),
376    TvSeasonTitle(Arc<String>),
377    TvSeasonTotal(u64),
378    TvSeriesTitle(Arc<String>),
379    Url(Arc<String>),
380    UrlArtist(Arc<String>),
381    UrlCopyright(Arc<String>),
382    UrlInternetRadio(Arc<String>),
383    UrlLabel(Arc<String>),
384    UrlOfficial(Arc<String>),
385    UrlPayment(Arc<String>),
386    UrlPodcast(Arc<String>),
387    UrlPurchase(Arc<String>),
388    UrlSource(Arc<String>),
389    Version(Arc<String>),
390    VolumeNumber(u64),
391    VolumeTitle(Arc<String>),
392    VolumeTotal(u64),
393    Work(Arc<String>),
394    Writer(Arc<String>),
395    WrittenDate(Arc<String>),
396}
397
398/// The value of a [`RawTag`].
399///
400/// Note: The data types in this enumeration are an abstraction. Depending on the particular tagging
401/// format, the actual data type of a specific tag may have a lesser width or different encoding
402/// than the data type stored here.
403#[non_exhaustive]
404#[derive(Clone, Debug, PartialEq)]
405pub enum RawValue {
406    /// A binary buffer.
407    Binary(Arc<Box<[u8]>>),
408    /// A boolean value.
409    Boolean(bool),
410    /// A flag or indicator. A flag carries no data, but the presence of the tag has an implicit
411    /// meaning.
412    Flag,
413    /// A floating point number.
414    Float(f64),
415    /// A signed integer.
416    SignedInt(i64),
417    /// A string.
418    String(Arc<String>),
419    /// A list of strings.
420    StringList(Arc<Vec<String>>),
421    /// An unsigned integer.
422    UnsignedInt(u64),
423}
424
425macro_rules! impl_from_for_value {
426    ($value:ident, $from:ty, $conv:expr) => {
427        impl From<$from> for RawValue {
428            fn from($value: $from) -> Self {
429                $conv
430            }
431        }
432    };
433}
434
435impl_from_for_value!(v, &[u8], RawValue::Binary(Arc::new(Box::from(v))));
436impl_from_for_value!(v, Box<[u8]>, RawValue::Binary(Arc::new(v)));
437impl_from_for_value!(v, Arc<Box<[u8]>>, RawValue::Binary(v));
438impl_from_for_value!(v, bool, RawValue::Boolean(v));
439impl_from_for_value!(v, f32, RawValue::Float(f64::from(v)));
440impl_from_for_value!(v, f64, RawValue::Float(v));
441impl_from_for_value!(v, i8, RawValue::SignedInt(i64::from(v)));
442impl_from_for_value!(v, i16, RawValue::SignedInt(i64::from(v)));
443impl_from_for_value!(v, i32, RawValue::SignedInt(i64::from(v)));
444impl_from_for_value!(v, i64, RawValue::SignedInt(v));
445impl_from_for_value!(v, u8, RawValue::UnsignedInt(u64::from(v)));
446impl_from_for_value!(v, u16, RawValue::UnsignedInt(u64::from(v)));
447impl_from_for_value!(v, u32, RawValue::UnsignedInt(u64::from(v)));
448impl_from_for_value!(v, u64, RawValue::UnsignedInt(v));
449impl_from_for_value!(v, &str, RawValue::String(Arc::new(v.to_string())));
450impl_from_for_value!(v, String, RawValue::String(Arc::new(v)));
451impl_from_for_value!(v, Arc<String>, RawValue::String(v));
452impl_from_for_value!(v, Cow<'_, str>, RawValue::String(Arc::new(v.into_owned())));
453impl_from_for_value!(v, Vec<String>, RawValue::StringList(Arc::new(v)));
454
455fn buffer_to_hex_string(buf: &[u8]) -> String {
456    let mut output = String::with_capacity(5 * buf.len());
457
458    for ch in buf {
459        let u = (ch & 0xf0) >> 4;
460        let l = ch & 0x0f;
461        output.push_str("\\0x");
462        output.push(if u < 10 { (b'0' + u) as char } else { (b'a' + u - 10) as char });
463        output.push(if l < 10 { (b'0' + l) as char } else { (b'a' + l - 10) as char });
464    }
465
466    output
467}
468
469impl fmt::Display for RawValue {
470    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
471        // Implement default formatters for each type.
472        match self {
473            RawValue::Binary(buf) => f.write_str(&buffer_to_hex_string(buf)),
474            RawValue::Boolean(boolean) => fmt::Display::fmt(boolean, f),
475            RawValue::Flag => write!(f, "<flag>"),
476            RawValue::Float(float) => fmt::Display::fmt(float, f),
477            RawValue::SignedInt(int) => fmt::Display::fmt(int, f),
478            RawValue::String(string) => fmt::Display::fmt(string, f),
479            RawValue::StringList(list) => fmt::Display::fmt(&list.join("\n"), f),
480            RawValue::UnsignedInt(uint) => fmt::Display::fmt(uint, f),
481        }
482    }
483}
484
485/// A key-value pair of supplementary data that can be attached to a raw tag.
486#[derive(Clone, Debug)]
487pub struct RawTagSubField {
488    /// The name of the sub-field.
489    pub field: String,
490    /// The value of the sub-field.
491    pub value: RawValue,
492}
493
494impl RawTagSubField {
495    /// Create a new sub-field from the provided field and value. Consumes the inputs.
496    pub fn new<F, V>(field: F, value: V) -> Self
497    where
498        F: Into<String>,
499        V: Into<RawValue>,
500    {
501        RawTagSubField { field: field.into(), value: value.into() }
502    }
503}
504
505/// A raw tag represents a tag in a data format that matches, as closely as possible, to the data
506/// format that the tag was written in.
507#[derive(Clone, Debug)]
508pub struct RawTag {
509    /// The name of the tag's key.
510    pub key: String,
511    /// The value of the tag.
512    pub value: RawValue,
513    /// The tag's sub-fields, if any.
514    pub sub_fields: Option<Box<[RawTagSubField]>>,
515}
516
517impl RawTag {
518    /// Create a new raw tag from the provided key and value, with no sub-fields. Consumes the
519    /// inputs.
520    pub fn new<K, V>(key: K, value: V) -> Self
521    where
522        K: Into<String>,
523        V: Into<RawValue>,
524    {
525        RawTag { key: key.into(), value: value.into(), sub_fields: None }
526    }
527
528    /// Create a new raw tag with sub-fields from the provided key, value, and sub-fields. Consumes
529    /// the inputs.
530    pub fn new_with_sub_fields<K, V>(key: K, value: V, sub_fields: Box<[RawTagSubField]>) -> Self
531    where
532        K: Into<String>,
533        V: Into<RawValue>,
534    {
535        RawTag { key: key.into(), value: value.into(), sub_fields: Some(sub_fields) }
536    }
537}
538
539/// A tag encapsulates a single piece of metadata.
540#[derive(Clone, Debug)]
541pub struct Tag {
542    /// The raw tag.
543    pub raw: RawTag,
544    /// An optional standard tag.
545    pub std: Option<StandardTag>,
546}
547
548impl Tag {
549    /// Create a new tag from a raw tag. Consumes the inputs.
550    pub fn new(raw: RawTag) -> Self {
551        Tag { raw, std: None }
552    }
553
554    /// Create a new tag from a raw tag with a standard tag. Consumes the inputs.
555    pub fn new_std(raw: RawTag, std: StandardTag) -> Self {
556        Tag { raw, std: Some(std) }
557    }
558
559    /// Create a new tag from its constituent parts: a key, value, and optional standard tag.
560    /// Consumes the inputs.
561    pub fn new_from_parts<K, V>(key: K, value: V, std: Option<StandardTag>) -> Self
562    where
563        K: Into<String>,
564        V: Into<RawValue>,
565    {
566        Tag { raw: RawTag { key: key.into(), value: value.into(), sub_fields: None }, std }
567    }
568
569    /// Returns `true` if the tag was recognized as a well-known tag and has a standard tag
570    /// assigned.
571    pub fn has_std_tag(&self) -> bool {
572        self.std.is_some()
573    }
574}
575
576/// A 2-dimensional (width and height) size type.
577#[derive(Copy, Clone, Debug, Default)]
578pub struct Size {
579    /// The width in pixels.
580    pub width: u32,
581    /// The height in pixels.
582    pub height: u32,
583}
584
585#[derive(Copy, Clone, Debug, PartialEq, Eq)]
586/// A color model describes how a color is represented.
587#[non_exhaustive]
588pub enum ColorModel {
589    /// Grayscale (1 channel: `Y`), of the indicated bit depth.
590    Y(NonZeroU8),
591    /// Grayscale with alpha (2 channels: `Y`, `A`), of the indicated bit depth.
592    YA(NonZeroU8),
593    /// RGB (3 channels: `R`,`G`,`B`), of the indicated bit depth.
594    RGB(NonZeroU8),
595    /// RGBA (4 channels: `R`,`G`,`B`,`A`), of the indicated bit depth.
596    RGBA(NonZeroU8),
597    /// CMYK (4 channels: `C`,`M`,`Y`,`K`), of the indicated bit depth.
598    CMYK(NonZeroU8),
599}
600
601impl ColorModel {
602    /// Gets the bits/pixel.
603    pub fn bits_per_pixel(&self) -> u32 {
604        match self {
605            ColorModel::Y(bits) => u32::from(bits.get()),
606            ColorModel::YA(bits) => 2 * u32::from(bits.get()),
607            ColorModel::RGB(bits) => 3 * u32::from(bits.get()),
608            ColorModel::RGBA(bits) => 4 * u32::from(bits.get()),
609            ColorModel::CMYK(bits) => 4 * u32::from(bits.get()),
610        }
611    }
612
613    /// Returns if the color model contains an alpha channel.
614    pub fn has_alpha_channel(&self) -> bool {
615        matches!(self, ColorModel::YA(_) | ColorModel::RGBA(_))
616    }
617}
618
619/// A description of the color palette for indexed color mode.
620#[derive(Copy, Clone, Debug, PartialEq, Eq)]
621pub struct ColorPaletteInfo {
622    /// The number of bits per pixel used to index the palette.
623    pub bits_per_pixel: NonZeroU8,
624    /// The color model of the entries in the palette.
625    pub color_model: ColorModel,
626}
627
628/// Indicates how colors are represented in the image.
629#[derive(Copy, Clone, Debug, PartialEq, Eq)]
630pub enum ColorMode {
631    /// Direct colour mode. Each pixel in the image stores the value of each color model primary.
632    ///
633    /// For example, in the RGB color model, each pixel will store a value for the red, green, and
634    /// blue color primaries.
635    Direct(ColorModel),
636    /// Indexed colour mode. Each pixel in the image stores an index into a color map (the palette)
637    /// that stores the actual color.
638    Indexed(ColorPaletteInfo),
639}
640
641/// A `Visual` is any 2 dimensional graphic.
642#[derive(Clone, Debug)]
643pub struct Visual {
644    /// The Media Type (MIME Type) used to encode the `Visual`.
645    pub media_type: Option<String>,
646    /// The dimensions of the `Visual`.
647    ///
648    /// Note: This value may not be accurate as it comes from metadata, not the embedded graphic
649    /// itself. Consider it only a hint.
650    pub dimensions: Option<Size>,
651    /// The color mode of the `Visual`.
652    ///
653    /// Note: This value may not be accurate as it comes from metadata, not the embedded graphic
654    /// itself. Consider it only a hint.
655    pub color_mode: Option<ColorMode>,
656    /// The usage and/or content of the `Visual`.
657    pub usage: Option<StandardVisualKey>,
658    /// Any tags associated with the `Visual`.
659    pub tags: Vec<Tag>,
660    /// The data of the `Visual`, encoded as per `media_type`.
661    pub data: Box<[u8]>,
662}
663
664/// A group of chapters and/or other chapter groups.
665#[derive(Clone, Debug)]
666pub struct ChapterGroup {
667    /// A list of chapters and/or chapter groups.
668    pub items: Vec<ChapterGroupItem>,
669    /// The tags associated with the group of chapters.
670    pub tags: Vec<Tag>,
671    /// The visuals associated with the group of chapters.
672    pub visuals: Vec<Visual>,
673}
674
675/// A chapter is a labelled section of a piece of media with a defined start time.
676#[derive(Clone, Debug)]
677pub struct Chapter {
678    /// The offset from the beginning of the media to the start of the chapter.
679    pub start_time: Time,
680    /// The offset from the beginning of the media to the end of the chapter.
681    pub end_time: Option<Time>,
682    /// The byte position from the beginning of the media source to the first byte of the first
683    /// frame in the chapter.
684    pub start_byte: Option<u64>,
685    /// The byte position from the beginning of the media source to the first byte of the frame
686    /// following the end of the chapter.
687    pub end_byte: Option<u64>,
688    /// The tags associated with the chapter.
689    pub tags: Vec<Tag>,
690    /// The visuals associated with the chapter.
691    pub visuals: Vec<Visual>,
692}
693
694/// A chapter group item is either a chapter or chapter group.
695#[derive(Clone, Debug)]
696pub enum ChapterGroupItem {
697    /// The item is a chapter group.
698    Group(ChapterGroup),
699    /// The item is a chapter.
700    Chapter(Chapter),
701}
702
703/// A container of metadata tags and pictures.
704#[derive(Clone, Debug, Default)]
705pub struct MetadataContainer {
706    /// Key-value pairs of metadata.
707    pub tags: Vec<Tag>,
708    /// Attached pictures.
709    pub visuals: Vec<Visual>,
710}
711
712/// Container for metadata associated with a specific track. A [`MetadataContainer`] wrapper
713/// associating it with a track ID.
714#[derive(Clone, Debug, Default)]
715pub struct PerTrackMetadata {
716    /// The ID of the track the metadata is associated with.
717    pub track_id: u64,
718    /// Track-specific metadata.
719    pub metadata: MetadataContainer,
720}
721
722/// A metadata revision is a container for a single discrete version of media metadata.
723///
724/// A metadata revision contains what a user typically associates with metadata: tags, pictures,
725/// etc., for the media as a whole and, optionally, for each contained track.
726#[derive(Clone, Debug)]
727pub struct MetadataRevision {
728    /// Information about the source of this revision of metadata.
729    pub info: MetadataInfo,
730    /// Media-level, global, metadata.
731    ///
732    /// The vast majority of metadata is media-level metadata.
733    pub media: MetadataContainer,
734    /// Per-track metadata.
735    pub per_track: Vec<PerTrackMetadata>,
736}
737
738/// A builder for [`MetadataRevision`].
739#[derive(Clone, Debug)]
740pub struct MetadataBuilder {
741    revision: MetadataRevision,
742}
743
744impl MetadataBuilder {
745    /// Instantiate a new `MetadataBuilder`.
746    pub fn new(info: MetadataInfo) -> Self {
747        let revision =
748            MetadataRevision { info, media: Default::default(), per_track: Default::default() };
749        MetadataBuilder { revision }
750    }
751
752    /// Add a media-level `Tag` to the metadata.
753    pub fn add_tag(&mut self, tag: Tag) -> &mut Self {
754        self.revision.media.tags.push(tag);
755        self
756    }
757
758    /// Add a media-level `Visual` to the metadata.
759    pub fn add_visual(&mut self, visual: Visual) -> &mut Self {
760        self.revision.media.visuals.push(visual);
761        self
762    }
763
764    /// Add track-specific metadata.
765    pub fn add_track(&mut self, per_track: PerTrackMetadata) -> &mut Self {
766        self.revision.per_track.push(per_track);
767        self
768    }
769
770    /// Build and return the metadata revision.
771    pub fn build(self) -> MetadataRevision {
772        self.revision
773    }
774}
775
776/// A builder for [`PerTrackMetadata`].
777#[derive(Clone, Debug)]
778pub struct PerTrackMetadataBuilder {
779    per_track: PerTrackMetadata,
780}
781
782impl PerTrackMetadataBuilder {
783    /// Instantiate a new `MetadataBuilder`.
784    pub fn new(track_id: u64) -> Self {
785        PerTrackMetadataBuilder {
786            per_track: PerTrackMetadata { track_id, metadata: Default::default() },
787        }
788    }
789
790    /// Add a `Tag` to the per-track metadata.
791    pub fn add_tag(&mut self, tag: Tag) -> &mut Self {
792        self.per_track.metadata.tags.push(tag);
793        self
794    }
795
796    /// Add a `Visual` to the per-track metadata.
797    pub fn add_visual(&mut self, visual: Visual) -> &mut Self {
798        self.per_track.metadata.visuals.push(visual);
799        self
800    }
801
802    /// Build and return the track-specific metadata.
803    pub fn build(self) -> PerTrackMetadata {
804        self.per_track
805    }
806}
807
808/// A reference to the metadata inside of a [`MetadataLog`].
809#[derive(Debug)]
810pub struct Metadata<'a> {
811    revisions: &'a mut VecDeque<MetadataRevision>,
812}
813
814impl Metadata<'_> {
815    /// Returns `true` if the current metadata revision is the newest, `false` otherwise.
816    pub fn is_latest(&self) -> bool {
817        self.revisions.len() <= 1
818    }
819
820    /// Gets an immutable reference to the current, and therefore oldest, revision of the metadata.
821    pub fn current(&self) -> Option<&MetadataRevision> {
822        self.revisions.front()
823    }
824
825    /// Skips to, and gets an immutable reference to the latest, and therefore newest, revision of
826    /// the metadata.
827    pub fn skip_to_latest(&mut self) -> Option<&MetadataRevision> {
828        loop {
829            if self.pop().is_none() {
830                break;
831            }
832        }
833        self.current()
834    }
835
836    /// If there are newer `Metadata` revisions, advances the `MetadataLog` by discarding the
837    /// current revision and replacing it with the next revision, returning the discarded
838    /// `Metadata`. When there are no newer revisions, `None` is returned. As such, `pop` will never
839    /// completely empty the log.
840    pub fn pop(&mut self) -> Option<MetadataRevision> {
841        if self.revisions.len() > 1 { self.revisions.pop_front() } else { None }
842    }
843}
844
845/// A queue for time-ordered [`MetadataRevision`]s.
846#[derive(Clone, Debug, Default)]
847pub struct MetadataLog {
848    revisions: VecDeque<MetadataRevision>,
849}
850
851impl MetadataLog {
852    /// Returns a reference to the metadata revisions inside the log.
853    pub fn metadata(&mut self) -> Metadata<'_> {
854        Metadata { revisions: &mut self.revisions }
855    }
856
857    /// Push a new metadata revision to the end of the log.
858    pub fn push(&mut self, rev: MetadataRevision) {
859        self.revisions.push_back(rev);
860    }
861
862    /// Moves all metadata revisions from another metadata log to the end of this log.
863    pub fn append(&mut self, other: &mut MetadataLog) {
864        self.revisions.append(&mut other.revisions);
865    }
866
867    /// Push a metadata revision to the front of the log.
868    pub fn push_front(&mut self, rev: MetadataRevision) {
869        self.revisions.push_front(rev);
870    }
871
872    /// Moves all metadata revisions from another metadata log to the front of this log.
873    pub fn append_front(&mut self, other: &mut MetadataLog) {
874        // Maintain the relative ordering.
875        while let Some(revision) = other.revisions.pop_back() {
876            self.revisions.push_front(revision)
877        }
878    }
879}
880
881/// Enumeration of types of metadata side data.
882#[non_exhaustive]
883pub enum MetadataSideData {
884    /// Chapter information.
885    Chapters(ChapterGroup),
886}
887
888/// The decoded contents of read metadata.
889pub struct MetadataBuffer {
890    /// The revision of metadata containing tags, visuals, and vendor-specific metadata buffers.
891    pub revision: MetadataRevision,
892    /// Additional pieces of data stored in the metadata, but not part of a metadata revision. These
893    /// pieces of data are usually passed to a format reader to support its function.
894    pub side_data: Vec<MetadataSideData>,
895}
896
897/// A `MetadataReader` reads and decodes metadata and produces a revision of that decoded metadata.
898pub trait MetadataReader: Send + Sync {
899    /// Get basic information about the metadata format.
900    fn metadata_info(&self) -> &MetadataInfo;
901
902    /// Read all metadata and return it if successful.
903    fn read_all(&mut self) -> Result<MetadataBuffer>;
904
905    /// Consumes the `MetadataReader` and returns the underlying media source stream
906    fn into_inner<'s>(self: Box<Self>) -> MediaSourceStream<'s>
907    where
908        Self: 's;
909}
910
911/// IDs for well-known metadata formats.
912pub mod well_known {
913    use super::MetadataId;
914
915    // Xiph tags
916
917    /// Vorbis Comment
918    pub const METADATA_ID_VORBIS_COMMENT: MetadataId = MetadataId(0x100);
919    /// FLAC tags
920    pub const METADATA_ID_FLAC: MetadataId = MetadataId(0x101);
921
922    // ID3 tags
923
924    /// ID3v1
925    pub const METADATA_ID_ID3V1: MetadataId = MetadataId(0x200);
926    /// ID3v2
927    pub const METADATA_ID_ID3V2: MetadataId = MetadataId(0x201);
928
929    // APE tags
930
931    /// APEv1
932    pub const METADATA_ID_APEV1: MetadataId = MetadataId(0x300);
933    /// APEv2
934    pub const METADATA_ID_APEV2: MetadataId = MetadataId(0x301);
935
936    // Format-native tags
937
938    /// RIFF tags
939    pub const METADATA_ID_WAVE: MetadataId = MetadataId(0x400);
940    /// RIFF tags
941    pub const METADATA_ID_AIFF: MetadataId = MetadataId(0x401);
942    /// EXIF
943    pub const METADATA_ID_EXIF: MetadataId = MetadataId(0x402);
944    /// Matroska tags
945    pub const METADATA_ID_MATROSKA: MetadataId = MetadataId(0x403);
946    /// ISOMP4 tags
947    pub const METADATA_ID_ISOMP4: MetadataId = MetadataId(0x404);
948}