symphonia_core/
meta.rs

1// Symphonia
2// Copyright (c) 2019-2022 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
10use std::borrow::Cow;
11use std::collections::VecDeque;
12use std::convert::From;
13use std::fmt;
14use std::num::NonZeroU32;
15
16use crate::errors::Result;
17use crate::io::MediaSourceStream;
18
19/// `Limit` defines an upper-bound on how much of a resource should be allocated when the amount to
20/// be allocated is specified by the media stream, which is untrusted. A limit will place an
21/// upper-bound on this allocation at the risk of breaking potentially valid streams. Limits are
22/// used to prevent denial-of-service attacks.
23///
24/// All limits can be defaulted to a reasonable value specific to the situation. These defaults will
25/// generally not break any normal streams.
26#[derive(Copy, Clone, Debug)]
27pub enum Limit {
28    /// Do not impose any limit.
29    None,
30    /// Use the a reasonable default specified by the `FormatReader` or `Decoder` implementation.
31    Default,
32    /// Specify the upper limit of the resource. Units are case specific.
33    Maximum(usize),
34}
35
36impl Limit {
37    /// Gets the numeric limit of the limit, or default value. If there is no limit, None is
38    /// returned.
39    pub fn limit_or_default(&self, default: usize) -> Option<usize> {
40        match self {
41            Limit::None => None,
42            Limit::Default => Some(default),
43            Limit::Maximum(max) => Some(*max),
44        }
45    }
46}
47
48impl Default for Limit {
49    fn default() -> Self {
50        Limit::Default
51    }
52}
53
54/// `MetadataOptions` is a common set of options that all metadata readers use.
55#[derive(Copy, Clone, Debug, Default)]
56pub struct MetadataOptions {
57    /// The maximum size limit in bytes that a tag may occupy in memory once decoded. Tags exceeding
58    /// this limit will be skipped by the demuxer. Take note that tags in-memory are stored as UTF-8
59    /// and therefore may occupy more than one byte per character.
60    pub limit_metadata_bytes: Limit,
61
62    /// The maximum size limit in bytes that a visual (picture) may occupy.
63    pub limit_visual_bytes: Limit,
64}
65
66/// `StandardVisualKey` is an enumeration providing standardized keys for common visual dispositions.
67/// A demuxer may assign a `StandardVisualKey` to a `Visual` if the disposition of the attached
68/// visual is known and can be mapped to a standard key.
69///
70/// The visual types listed here are derived from, though do not entirely cover, the ID3v2 APIC
71/// frame specification.
72#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
73pub enum StandardVisualKey {
74    FileIcon,
75    OtherIcon,
76    FrontCover,
77    BackCover,
78    Leaflet,
79    Media,
80    LeadArtistPerformerSoloist,
81    ArtistPerformer,
82    Conductor,
83    BandOrchestra,
84    Composer,
85    Lyricist,
86    RecordingLocation,
87    RecordingSession,
88    Performance,
89    ScreenCapture,
90    Illustration,
91    BandArtistLogo,
92    PublisherStudioLogo,
93}
94
95/// `StandardTagKey` is an enumeration providing standardized keys for common tag types.
96/// A tag reader may assign a `StandardTagKey` to a `Tag` if the tag's key is generally
97/// accepted to map to a specific usage.
98#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
99pub enum StandardTagKey {
100    AcoustidFingerprint,
101    AcoustidId,
102    Album,
103    AlbumArtist,
104    Arranger,
105    Artist,
106    Bpm,
107    Comment,
108    Compilation,
109    Composer,
110    Conductor,
111    ContentGroup,
112    Copyright,
113    Date,
114    Description,
115    DiscNumber,
116    DiscSubtitle,
117    DiscTotal,
118    EncodedBy,
119    Encoder,
120    EncoderSettings,
121    EncodingDate,
122    Engineer,
123    Ensemble,
124    Genre,
125    IdentAsin,
126    IdentBarcode,
127    IdentCatalogNumber,
128    IdentEanUpn,
129    IdentIsrc,
130    IdentPn,
131    IdentPodcast,
132    IdentUpc,
133    Label,
134    Language,
135    License,
136    Lyricist,
137    Lyrics,
138    MediaFormat,
139    MixDj,
140    MixEngineer,
141    Mood,
142    MovementName,
143    MovementNumber,
144    MusicBrainzAlbumArtistId,
145    MusicBrainzAlbumId,
146    MusicBrainzArtistId,
147    MusicBrainzDiscId,
148    MusicBrainzGenreId,
149    MusicBrainzLabelId,
150    MusicBrainzOriginalAlbumId,
151    MusicBrainzOriginalArtistId,
152    MusicBrainzRecordingId,
153    MusicBrainzReleaseGroupId,
154    MusicBrainzReleaseStatus,
155    MusicBrainzReleaseTrackId,
156    MusicBrainzReleaseType,
157    MusicBrainzTrackId,
158    MusicBrainzWorkId,
159    Opus,
160    OriginalAlbum,
161    OriginalArtist,
162    OriginalDate,
163    OriginalFile,
164    OriginalWriter,
165    Owner,
166    Part,
167    PartTotal,
168    Performer,
169    Podcast,
170    PodcastCategory,
171    PodcastDescription,
172    PodcastKeywords,
173    Producer,
174    PurchaseDate,
175    Rating,
176    ReleaseCountry,
177    ReleaseDate,
178    Remixer,
179    ReplayGainAlbumGain,
180    ReplayGainAlbumPeak,
181    ReplayGainTrackGain,
182    ReplayGainTrackPeak,
183    Script,
184    SortAlbum,
185    SortAlbumArtist,
186    SortArtist,
187    SortComposer,
188    SortTrackTitle,
189    TaggingDate,
190    TrackNumber,
191    TrackSubtitle,
192    TrackTitle,
193    TrackTotal,
194    TvEpisode,
195    TvEpisodeTitle,
196    TvNetwork,
197    TvSeason,
198    TvShowTitle,
199    Url,
200    UrlArtist,
201    UrlCopyright,
202    UrlInternetRadio,
203    UrlLabel,
204    UrlOfficial,
205    UrlPayment,
206    UrlPodcast,
207    UrlPurchase,
208    UrlSource,
209    Version,
210    Writer,
211}
212
213/// A `Tag` value.
214///
215/// Note: The data types in this enumeration are a generalization. Depending on the particular tag
216/// format, the actual data type a specific tag may have a lesser width or encoding than the data
217/// type in this enumeration.
218#[derive(Clone, Debug)]
219pub enum Value {
220    /// A binary buffer.
221    Binary(Box<[u8]>),
222    /// A boolean value.
223    Boolean(bool),
224    /// A flag or indicator. A flag carries no data, but the presence of the tag has an implicit
225    /// meaning.
226    Flag,
227    /// A floating point number.
228    Float(f64),
229    /// A signed integer.
230    SignedInt(i64),
231    /// A string. This is also the catch-all type for tags with unconventional data types.
232    String(String),
233    /// An unsigned integer.
234    UnsignedInt(u64),
235}
236
237macro_rules! impl_from_for_value {
238    ($value:ident, $from:ty, $conv:expr) => {
239        impl From<$from> for Value {
240            fn from($value: $from) -> Self {
241                $conv
242            }
243        }
244    };
245}
246
247impl_from_for_value!(v, &[u8], Value::Binary(Box::from(v)));
248impl_from_for_value!(v, bool, Value::Boolean(v));
249impl_from_for_value!(v, f32, Value::Float(f64::from(v)));
250impl_from_for_value!(v, f64, Value::Float(v));
251impl_from_for_value!(v, i8, Value::SignedInt(i64::from(v)));
252impl_from_for_value!(v, i16, Value::SignedInt(i64::from(v)));
253impl_from_for_value!(v, i32, Value::SignedInt(i64::from(v)));
254impl_from_for_value!(v, i64, Value::SignedInt(v));
255impl_from_for_value!(v, u8, Value::UnsignedInt(u64::from(v)));
256impl_from_for_value!(v, u16, Value::UnsignedInt(u64::from(v)));
257impl_from_for_value!(v, u32, Value::UnsignedInt(u64::from(v)));
258impl_from_for_value!(v, u64, Value::UnsignedInt(v));
259impl_from_for_value!(v, &str, Value::String(String::from(v)));
260impl_from_for_value!(v, String, Value::String(v));
261impl_from_for_value!(v, Cow<'_, str>, Value::String(String::from(v)));
262
263fn buffer_to_hex_string(buf: &[u8]) -> String {
264    let mut output = String::with_capacity(5 * buf.len());
265
266    for ch in buf {
267        let u = (ch & 0xf0) >> 4;
268        let l = ch & 0x0f;
269        output.push_str("\\0x");
270        output.push(if u < 10 { (b'0' + u) as char } else { (b'a' + u - 10) as char });
271        output.push(if l < 10 { (b'0' + l) as char } else { (b'a' + l - 10) as char });
272    }
273
274    output
275}
276
277impl fmt::Display for Value {
278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279        // Implement default formatters for each type.
280        match self {
281            Value::Binary(ref buf) => f.write_str(&buffer_to_hex_string(buf)),
282            Value::Boolean(boolean) => fmt::Display::fmt(boolean, f),
283            Value::Flag => write!(f, "<flag>"),
284            Value::Float(float) => fmt::Display::fmt(float, f),
285            Value::SignedInt(int) => fmt::Display::fmt(int, f),
286            Value::String(ref string) => fmt::Display::fmt(string, f),
287            Value::UnsignedInt(uint) => fmt::Display::fmt(uint, f),
288        }
289    }
290}
291
292/// A `Tag` encapsulates a key-value pair of metadata.
293#[derive(Clone, Debug)]
294pub struct Tag {
295    /// If the `Tag`'s key string is commonly associated with a typical type, meaning, or purpose,
296    /// then if recognized a `StandardTagKey` will be assigned to this `Tag`.
297    ///
298    /// This is a best effort guess since not all metadata formats have a well defined or specified
299    /// tag mapping. However, it is recommended that consumers prefer `std_key` over `key`, if
300    /// provided.
301    pub std_key: Option<StandardTagKey>,
302    /// A key string indicating the type, meaning, or purpose of the `Tag`s value.
303    ///
304    /// Note: The meaning of `key` is dependant on the underlying metadata format.
305    pub key: String,
306    /// The value of the `Tag`.
307    pub value: Value,
308}
309
310impl Tag {
311    /// Create a new `Tag`.
312    pub fn new(std_key: Option<StandardTagKey>, key: &str, value: Value) -> Tag {
313        Tag { std_key, key: key.to_string(), value }
314    }
315
316    /// Returns true if the `Tag`'s key string was recognized and a `StandardTagKey` was assigned,
317    /// otherwise false is returned.
318    pub fn is_known(&self) -> bool {
319        self.std_key.is_some()
320    }
321}
322
323impl fmt::Display for Tag {
324    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325        match self.std_key {
326            Some(ref std_key) => {
327                write!(f, "{{ std_key={:?}, key=\"{}\", value={} }}", std_key, self.key, self.value)
328            }
329            None => write!(f, "{{ key=\"{}\", value={} }}", self.key, self.value),
330        }
331    }
332}
333
334/// A 2 dimensional (width and height) size type.
335#[derive(Copy, Clone, Debug, Default)]
336pub struct Size {
337    /// The width in pixels.
338    pub width: u32,
339    /// The height in pixels.
340    pub height: u32,
341}
342
343/// `ColorMode` indicates how the color of a pixel is encoded in a `Visual`.
344#[derive(Copy, Clone, Debug)]
345pub enum ColorMode {
346    /// Each pixel in the `Visual` stores its own color information.
347    Discrete,
348    /// Each pixel in the `Visual` stores an index into a color palette containing the color
349    /// information. The value stored by this variant indicates the number of colors in the color
350    /// palette.
351    Indexed(NonZeroU32),
352}
353
354/// A `Visual` is any 2 dimensional graphic.
355#[derive(Clone, Debug)]
356pub struct Visual {
357    /// The Media Type (MIME Type) used to encode the `Visual`.
358    pub media_type: String,
359    /// The dimensions of the `Visual`.
360    ///
361    /// Note: This value may not be accurate as it comes from metadata, not the embedded graphic
362    /// itself. Consider it only a hint.
363    pub dimensions: Option<Size>,
364    /// The number of bits-per-pixel (aka bit-depth) of the unencoded image.
365    ///
366    /// Note: This value may not be accurate as it comes from metadata, not the embedded graphic
367    /// itself. Consider it only a hint.
368    pub bits_per_pixel: Option<NonZeroU32>,
369    /// The color mode of the `Visual`.
370    ///
371    /// Note: This value may not be accurate as it comes from metadata, not the embedded graphic
372    /// itself. Consider it only a hint.
373    pub color_mode: Option<ColorMode>,
374    /// The usage and/or content of the `Visual`.
375    pub usage: Option<StandardVisualKey>,
376    /// Any tags associated with the `Visual`.
377    pub tags: Vec<Tag>,
378    /// The data of the `Visual`, encoded as per `media_type`.
379    pub data: Box<[u8]>,
380}
381
382/// `VendorData` is any binary metadata that is proprietary to a certain application or vendor.
383#[derive(Clone, Debug)]
384pub struct VendorData {
385    /// A text representation of the vendor's application identifier.
386    pub ident: String,
387    /// The vendor data.
388    pub data: Box<[u8]>,
389}
390
391/// `Metadata` is a container for a single discrete revision of metadata information.
392#[derive(Clone, Debug, Default)]
393pub struct MetadataRevision {
394    tags: Vec<Tag>,
395    visuals: Vec<Visual>,
396    vendor_data: Vec<VendorData>,
397}
398
399impl MetadataRevision {
400    /// Gets an immutable slice to the `Tag`s in this revision.
401    ///
402    /// If a tag read from the source contained multiple values, then there will be one `Tag` item
403    /// per value, with each item having the same key and standard key.
404    pub fn tags(&self) -> &[Tag] {
405        &self.tags
406    }
407
408    /// Gets an immutable slice to the `Visual`s in this revision.
409    pub fn visuals(&self) -> &[Visual] {
410        &self.visuals
411    }
412
413    /// Gets an immutable slice to the `VendorData` in this revision.
414    pub fn vendor_data(&self) -> &[VendorData] {
415        &self.vendor_data
416    }
417}
418
419/// `MetadataBuilder` is the builder for `Metadata` revisions.
420#[derive(Clone, Debug, Default)]
421pub struct MetadataBuilder {
422    metadata: MetadataRevision,
423}
424
425impl MetadataBuilder {
426    /// Instantiate a new `MetadataBuilder`.
427    pub fn new() -> Self {
428        MetadataBuilder { metadata: Default::default() }
429    }
430
431    /// Add a `Tag` to the metadata.
432    pub fn add_tag(&mut self, tag: Tag) -> &mut Self {
433        self.metadata.tags.push(tag);
434        self
435    }
436
437    /// Add a `Visual` to the metadata.
438    pub fn add_visual(&mut self, visual: Visual) -> &mut Self {
439        self.metadata.visuals.push(visual);
440        self
441    }
442
443    /// Add `VendorData` to the metadata.
444    pub fn add_vendor_data(&mut self, vendor_data: VendorData) -> &mut Self {
445        self.metadata.vendor_data.push(vendor_data);
446        self
447    }
448
449    /// Yield the constructed `Metadata` revision.
450    pub fn metadata(self) -> MetadataRevision {
451        self.metadata
452    }
453}
454
455/// A reference to the metadata inside of a [MetadataLog].
456#[derive(Debug)]
457pub struct Metadata<'a> {
458    revisions: &'a mut VecDeque<MetadataRevision>,
459}
460
461impl<'a> Metadata<'a> {
462    /// Returns `true` if the current metadata revision is the newest, `false` otherwise.
463    pub fn is_latest(&self) -> bool {
464        self.revisions.len() <= 1
465    }
466
467    /// Gets an immutable reference to the current, and therefore oldest, revision of the metadata.
468    pub fn current(&self) -> Option<&MetadataRevision> {
469        self.revisions.front()
470    }
471
472    /// Skips to, and gets an immutable reference to the latest, and therefore newest, revision of
473    /// the metadata.
474    pub fn skip_to_latest(&mut self) -> Option<&MetadataRevision> {
475        loop {
476            if self.pop().is_none() {
477                break;
478            }
479        }
480        self.current()
481    }
482
483    /// If there are newer `Metadata` revisions, advances the `MetadataLog` by discarding the
484    /// current revision and replacing it with the next revision, returning the discarded
485    /// `Metadata`. When there are no newer revisions, `None` is returned. As such, `pop` will never
486    /// completely empty the log.
487    pub fn pop(&mut self) -> Option<MetadataRevision> {
488        if self.revisions.len() > 1 {
489            self.revisions.pop_front()
490        }
491        else {
492            None
493        }
494    }
495}
496
497/// `MetadataLog` is a container for time-ordered `Metadata` revisions.
498#[derive(Clone, Debug, Default)]
499pub struct MetadataLog {
500    revisions: VecDeque<MetadataRevision>,
501}
502
503impl MetadataLog {
504    /// Returns a reducable reference to the metadata inside the log.
505    pub fn metadata(&mut self) -> Metadata<'_> {
506        Metadata { revisions: &mut self.revisions }
507    }
508
509    /// Pushes a new `Metadata` revision onto the log.
510    pub fn push(&mut self, rev: MetadataRevision) {
511        self.revisions.push_back(rev);
512    }
513}
514
515pub trait MetadataReader: Send + Sync {
516    /// Instantiates the `MetadataReader` with the provided `MetadataOptions`.
517    fn new(options: &MetadataOptions) -> Self
518    where
519        Self: Sized;
520
521    /// Read all metadata and return it if successful.
522    fn read_all(&mut self, reader: &mut MediaSourceStream) -> Result<MetadataRevision>;
523}