mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-11-10 06:34:18 +00:00
MP4: Create DataType
enum, replacing constants
This commit is contained in:
parent
637052978b
commit
9eb35ae1b9
6 changed files with 336 additions and 95 deletions
|
@ -1,5 +1,6 @@
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::macros::err;
|
use crate::macros::err;
|
||||||
|
use crate::mp4::ilst::data_type::DataType;
|
||||||
use crate::mp4::AtomIdent;
|
use crate::mp4::AtomIdent;
|
||||||
use crate::picture::Picture;
|
use crate::picture::Picture;
|
||||||
|
|
||||||
|
@ -213,7 +214,10 @@ impl<'a> Atom<'a> {
|
||||||
pub(crate) fn unknown_implicit(ident: AtomIdent<'_>, data: Vec<u8>) -> Self {
|
pub(crate) fn unknown_implicit(ident: AtomIdent<'_>, data: Vec<u8>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ident: ident.into_owned(),
|
ident: ident.into_owned(),
|
||||||
data: AtomDataStorage::Single(AtomData::Unknown { code: 0, data }),
|
data: AtomDataStorage::Single(AtomData::Unknown {
|
||||||
|
code: DataType::Reserved,
|
||||||
|
data,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,8 +251,7 @@ impl Debug for Atom<'_> {
|
||||||
/// NOTES:
|
/// NOTES:
|
||||||
///
|
///
|
||||||
/// * This only covers the most common data types.
|
/// * This only covers the most common data types.
|
||||||
/// See the list of [well-known data types](https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW34)
|
/// See the list of [DataType] for all known types.
|
||||||
/// for codes.
|
|
||||||
/// * There are only two variants for integers, which
|
/// * There are only two variants for integers, which
|
||||||
/// will come from codes `21` and `22`. All other integer
|
/// will come from codes `21` and `22`. All other integer
|
||||||
/// types will be stored as [`AtomData::Unknown`], refer
|
/// types will be stored as [`AtomData::Unknown`], refer
|
||||||
|
@ -290,51 +293,52 @@ pub enum AtomData {
|
||||||
/// variant.
|
/// variant.
|
||||||
Unknown {
|
Unknown {
|
||||||
/// The code, or type of the item
|
/// The code, or type of the item
|
||||||
code: u32,
|
code: DataType,
|
||||||
/// The binary data of the atom
|
/// The binary data of the atom
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The parental advisory rating
|
impl AtomData {
|
||||||
///
|
/// Get the [`DataType`] of the atom
|
||||||
/// See also:
|
|
||||||
/// <https://docs.mp3tag.de/mapping/#itunesadvisory>
|
|
||||||
/// <https://exiftool.org/TagNames/QuickTime.html>
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum AdvisoryRating {
|
|
||||||
/// *Inoffensive*/*None* (0)
|
|
||||||
Inoffensive,
|
|
||||||
/// *Explicit* (1 or 4)
|
|
||||||
///
|
///
|
||||||
/// In the past Apple used the value 4 for explicit content
|
/// Note that for [`AtomData::Picture`], the type is determined by the picture's MIME type.
|
||||||
/// that has later been replaced by 1. Both values are considered
|
/// If the MIME type is unknown (or unset), the data type will be [`DataType::Reserved`].
|
||||||
/// as valid when reading but only the newer value 1 is written.
|
///
|
||||||
Explicit,
|
/// # Examples
|
||||||
/// *Clean*/*Edited* (2)
|
///
|
||||||
Clean,
|
/// ```rust
|
||||||
}
|
/// use lofty::mp4::{AtomData, DataType};
|
||||||
|
/// use lofty::picture::{MimeType, Picture, PictureType};
|
||||||
impl AdvisoryRating {
|
///
|
||||||
/// Returns the rating as it appears in the `rtng` atom
|
/// let data = AtomData::UTF8(String::from("foo"));
|
||||||
pub fn as_u8(&self) -> u8 {
|
/// assert_eq!(data.data_type(), DataType::Utf8);
|
||||||
|
///
|
||||||
|
/// let data = AtomData::SignedInteger(42);
|
||||||
|
/// assert_eq!(data.data_type(), DataType::BeSignedInteger);
|
||||||
|
///
|
||||||
|
/// let data = AtomData::Picture(Picture::new_unchecked(
|
||||||
|
/// PictureType::CoverFront,
|
||||||
|
/// Some(MimeType::Jpeg),
|
||||||
|
/// None,
|
||||||
|
/// Vec::new(),
|
||||||
|
/// ));
|
||||||
|
/// assert_eq!(data.data_type(), DataType::Jpeg);
|
||||||
|
/// ```
|
||||||
|
pub fn data_type(&self) -> DataType {
|
||||||
match self {
|
match self {
|
||||||
AdvisoryRating::Inoffensive => 0,
|
AtomData::UTF8(_) => DataType::Utf8,
|
||||||
AdvisoryRating::Explicit => 1,
|
AtomData::UTF16(_) => DataType::Utf16,
|
||||||
AdvisoryRating::Clean => 2,
|
AtomData::SignedInteger(_) | AtomData::Bool(_) => DataType::BeSignedInteger,
|
||||||
}
|
AtomData::UnsignedInteger(_) => DataType::BeUnsignedInteger,
|
||||||
}
|
AtomData::Picture(p) => {
|
||||||
}
|
let Some(mime) = p.mime_type() else {
|
||||||
|
return DataType::Reserved;
|
||||||
impl TryFrom<u8> for AdvisoryRating {
|
};
|
||||||
type Error = u8;
|
|
||||||
|
DataType::from(mime)
|
||||||
fn try_from(input: u8) -> std::result::Result<Self, Self::Error> {
|
},
|
||||||
match input {
|
AtomData::Unknown { code, .. } => *code,
|
||||||
0 => Ok(Self::Inoffensive),
|
|
||||||
1 | 4 => Ok(Self::Explicit),
|
|
||||||
2 => Ok(Self::Clean),
|
|
||||||
value => Err(value),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
200
lofty/src/mp4/ilst/data_type.rs
Normal file
200
lofty/src/mp4/ilst/data_type.rs
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
use crate::picture::MimeType;
|
||||||
|
|
||||||
|
/// The [well known] basic data types
|
||||||
|
///
|
||||||
|
/// This should cover all the data types you'll encounter in an MP4 file.
|
||||||
|
///
|
||||||
|
/// [well known]: https://developer.apple.com/documentation/quicktime-file-format/well-known_types
|
||||||
|
// OLD LINKS:
|
||||||
|
// * https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum DataType {
|
||||||
|
/// Reserved for use where no type needs to be indicated
|
||||||
|
Reserved = 0,
|
||||||
|
/// UTF-8 string without any count or NULL terminator
|
||||||
|
Utf8 = 1,
|
||||||
|
/// A big-endian UTF-16 string
|
||||||
|
Utf16 = 2,
|
||||||
|
/// Deprecated unless it is needed for special Japanese characters
|
||||||
|
SJis = 3,
|
||||||
|
/// The UTF-8 variant storage of a string for sorting only
|
||||||
|
Utf8Sort = 4,
|
||||||
|
/// The UTF-16 variant storage of a string for sorting only
|
||||||
|
Utf16Sort = 5,
|
||||||
|
/// **DEPRECATED** A GIF image
|
||||||
|
Gif = 12,
|
||||||
|
/// A JPEG in a JFIF wrapper
|
||||||
|
Jpeg = 13,
|
||||||
|
/// A PNG in a PNG wrapper
|
||||||
|
Png = 14,
|
||||||
|
/// A big-endian signed integer in 1,2,3 or 4 bytes
|
||||||
|
BeSignedInteger = 21,
|
||||||
|
/// A big-endian unsigned integer in 1,2,3 or 4 bytes; size of value determines integer size
|
||||||
|
BeUnsignedInteger = 22,
|
||||||
|
/// A big-endian 32-bit floating point value (IEEE754)
|
||||||
|
BeFloat32 = 23,
|
||||||
|
/// A big-endian 64-bit floating point value (IEEE754)
|
||||||
|
BeFloat64 = 24,
|
||||||
|
/// Windows bitmap format graphics
|
||||||
|
Bmp = 27,
|
||||||
|
/// A QuickTime metadata atom
|
||||||
|
QuicktimeMetadata = 28,
|
||||||
|
/// An 8-bit signed integer
|
||||||
|
Signed8BitInteger = 65,
|
||||||
|
/// A big-endian 16-bit signed integer
|
||||||
|
Be16BitSignedInteger = 66,
|
||||||
|
/// A big-endian 32-bit signed integer
|
||||||
|
Be32BitSignedInteger = 67,
|
||||||
|
/// A block of data representing a two dimensional (2D) point with 32-bit big-endian floating point x and y coordinates. It has the structure:
|
||||||
|
///
|
||||||
|
/// ```c
|
||||||
|
/// struct {
|
||||||
|
/// BEFloat32 x;
|
||||||
|
/// BEFloat32 y;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
BePointF32 = 70,
|
||||||
|
/// A block of data representing 2D dimensions with 32-bit big-endian floating point width and height. It has the structure:
|
||||||
|
///
|
||||||
|
/// ```c
|
||||||
|
/// struct {
|
||||||
|
/// BEFloat32 width;
|
||||||
|
/// BEFloat32 height;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
BeDimensionsF32 = 71,
|
||||||
|
/// A block of data representing a 2D rectangle with 32-bit big-endian floating point x and y coordinates and a 32-bit big-endian floating point width and height size. It has the structure:
|
||||||
|
///
|
||||||
|
/// ```c
|
||||||
|
/// struct {
|
||||||
|
/// BEFloat32 x;
|
||||||
|
/// BEFloat32 y;
|
||||||
|
/// BEFloat32 width;
|
||||||
|
/// BEFloat32 height;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// or the equivalent structure:
|
||||||
|
///
|
||||||
|
/// ```c
|
||||||
|
/// struct {
|
||||||
|
/// PointF32 origin;
|
||||||
|
/// DimensionsF32 size;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
BeRectF32 = 72,
|
||||||
|
/// A big-endian 64-bit signed integer
|
||||||
|
Be64BitSignedInteger = 74,
|
||||||
|
/// An 8-bit unsigned integer
|
||||||
|
Unsigned8BitInteger = 75,
|
||||||
|
/// A big-endian 16-bit unsigned integer
|
||||||
|
Be16BitUnsignedInteger = 76,
|
||||||
|
/// A big-endian 32-bit unsigned integer
|
||||||
|
Be32BitUnsignedInteger = 77,
|
||||||
|
/// A big-endian 64-bit unsigned integer
|
||||||
|
Be64BitUnsignedInteger = 78,
|
||||||
|
/// A block of data representing a 3x3 transformation matrix. It has the structure:
|
||||||
|
///
|
||||||
|
/// ```c
|
||||||
|
/// struct {
|
||||||
|
/// BEFloat64 matrix[3][3];
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
AffineTransformF64 = 79,
|
||||||
|
/// Some other data type
|
||||||
|
Other(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for DataType {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => DataType::Reserved,
|
||||||
|
1 => DataType::Utf8,
|
||||||
|
2 => DataType::Utf16,
|
||||||
|
3 => DataType::SJis,
|
||||||
|
4 => DataType::Utf8Sort,
|
||||||
|
5 => DataType::Utf16Sort,
|
||||||
|
12 => DataType::Gif,
|
||||||
|
13 => DataType::Jpeg,
|
||||||
|
14 => DataType::Png,
|
||||||
|
21 => DataType::BeSignedInteger,
|
||||||
|
22 => DataType::BeUnsignedInteger,
|
||||||
|
23 => DataType::BeFloat32,
|
||||||
|
24 => DataType::BeFloat64,
|
||||||
|
27 => DataType::Bmp,
|
||||||
|
28 => DataType::QuicktimeMetadata,
|
||||||
|
65 => DataType::Signed8BitInteger,
|
||||||
|
66 => DataType::Be16BitSignedInteger,
|
||||||
|
67 => DataType::Be32BitSignedInteger,
|
||||||
|
70 => DataType::BePointF32,
|
||||||
|
71 => DataType::BeDimensionsF32,
|
||||||
|
72 => DataType::BeRectF32,
|
||||||
|
74 => DataType::Be64BitSignedInteger,
|
||||||
|
75 => DataType::Unsigned8BitInteger,
|
||||||
|
76 => DataType::Be16BitUnsignedInteger,
|
||||||
|
77 => DataType::Be32BitUnsignedInteger,
|
||||||
|
78 => DataType::Be64BitUnsignedInteger,
|
||||||
|
79 => DataType::AffineTransformF64,
|
||||||
|
other => DataType::Other(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DataType> for u32 {
|
||||||
|
fn from(value: DataType) -> Self {
|
||||||
|
match value {
|
||||||
|
DataType::Reserved => 0,
|
||||||
|
DataType::Utf8 => 1,
|
||||||
|
DataType::Utf16 => 2,
|
||||||
|
DataType::SJis => 3,
|
||||||
|
DataType::Utf8Sort => 4,
|
||||||
|
DataType::Utf16Sort => 5,
|
||||||
|
DataType::Gif => 12,
|
||||||
|
DataType::Jpeg => 13,
|
||||||
|
DataType::Png => 14,
|
||||||
|
DataType::BeSignedInteger => 21,
|
||||||
|
DataType::BeUnsignedInteger => 22,
|
||||||
|
DataType::BeFloat32 => 23,
|
||||||
|
DataType::BeFloat64 => 24,
|
||||||
|
DataType::Bmp => 27,
|
||||||
|
DataType::QuicktimeMetadata => 28,
|
||||||
|
DataType::Signed8BitInteger => 65,
|
||||||
|
DataType::Be16BitSignedInteger => 66,
|
||||||
|
DataType::Be32BitSignedInteger => 67,
|
||||||
|
DataType::BePointF32 => 70,
|
||||||
|
DataType::BeDimensionsF32 => 71,
|
||||||
|
DataType::BeRectF32 => 72,
|
||||||
|
DataType::Be64BitSignedInteger => 74,
|
||||||
|
DataType::Unsigned8BitInteger => 75,
|
||||||
|
DataType::Be16BitUnsignedInteger => 76,
|
||||||
|
DataType::Be32BitUnsignedInteger => 77,
|
||||||
|
DataType::Be64BitUnsignedInteger => 78,
|
||||||
|
DataType::AffineTransformF64 => 79,
|
||||||
|
DataType::Other(other) => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MimeType> for DataType {
|
||||||
|
fn from(value: MimeType) -> Self {
|
||||||
|
DataType::from(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&MimeType> for DataType {
|
||||||
|
fn from(value: &MimeType) -> Self {
|
||||||
|
match value {
|
||||||
|
MimeType::Gif => DataType::Gif,
|
||||||
|
MimeType::Jpeg => DataType::Jpeg,
|
||||||
|
MimeType::Png => DataType::Png,
|
||||||
|
MimeType::Bmp => DataType::Bmp,
|
||||||
|
_ => DataType::Reserved,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataType {
|
||||||
|
/// A data type can only occupy 24 bits
|
||||||
|
pub const MAX: u32 = 16_777_215;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
pub(super) mod advisory_rating;
|
pub(super) mod advisory_rating;
|
||||||
pub(super) mod atom;
|
pub(super) mod atom;
|
||||||
pub(super) mod constants;
|
pub(super) mod constants;
|
||||||
|
pub(super) mod data_type;
|
||||||
pub(super) mod read;
|
pub(super) mod read;
|
||||||
mod r#ref;
|
mod r#ref;
|
||||||
pub(crate) mod write;
|
pub(crate) mod write;
|
||||||
|
@ -16,8 +17,9 @@ use crate::tag::{
|
||||||
};
|
};
|
||||||
use crate::util::flag_item;
|
use crate::util::flag_item;
|
||||||
use crate::util::io::{FileLike, Length, Truncate};
|
use crate::util::io::{FileLike, Length, Truncate};
|
||||||
use atom::{AdvisoryRating, Atom, AtomData};
|
|
||||||
use advisory_rating::AdvisoryRating;
|
use advisory_rating::AdvisoryRating;
|
||||||
|
use atom::{Atom, AtomData};
|
||||||
|
use data_type::DataType;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
@ -374,7 +376,10 @@ impl Ilst {
|
||||||
fn extract_number(&self, fourcc: [u8; 4], expected_size: usize) -> Option<u16> {
|
fn extract_number(&self, fourcc: [u8; 4], expected_size: usize) -> Option<u16> {
|
||||||
if let Some(atom) = self.get(&AtomIdent::Fourcc(fourcc)) {
|
if let Some(atom) = self.get(&AtomIdent::Fourcc(fourcc)) {
|
||||||
match atom.data().next() {
|
match atom.data().next() {
|
||||||
Some(AtomData::Unknown { code: 0, data }) if data.len() >= expected_size => {
|
Some(AtomData::Unknown {
|
||||||
|
code: DataType::Reserved,
|
||||||
|
data,
|
||||||
|
}) if data.len() >= expected_size => {
|
||||||
return Some(u16::from_be_bytes([
|
return Some(u16::from_be_bytes([
|
||||||
data[expected_size - 2],
|
data[expected_size - 2],
|
||||||
data[expected_size - 1],
|
data[expected_size - 1],
|
||||||
|
@ -603,7 +608,10 @@ impl SplitTag for Ilst {
|
||||||
ItemValue::Text(text)
|
ItemValue::Text(text)
|
||||||
},
|
},
|
||||||
// We have to special case track/disc numbers since they are stored together
|
// We have to special case track/disc numbers since they are stored together
|
||||||
AtomData::Unknown { code: 0, data } if Vec::len(data) >= 6 => {
|
AtomData::Unknown {
|
||||||
|
code: DataType::Reserved,
|
||||||
|
data,
|
||||||
|
} if Vec::len(data) >= 6 => {
|
||||||
if let AtomIdent::Fourcc(ref fourcc) = ident {
|
if let AtomIdent::Fourcc(ref fourcc) = ident {
|
||||||
match fourcc {
|
match fourcc {
|
||||||
b"trkn" => {
|
b"trkn" => {
|
||||||
|
@ -686,7 +694,7 @@ impl MergeTag for SplitTagRemainder {
|
||||||
tag.atoms.push(Atom {
|
tag.atoms.push(Atom {
|
||||||
ident: AtomIdent::Fourcc(ident),
|
ident: AtomIdent::Fourcc(ident),
|
||||||
data: AtomDataStorage::Single(AtomData::Unknown {
|
data: AtomDataStorage::Single(AtomData::Unknown {
|
||||||
code: 0,
|
code: DataType::Reserved,
|
||||||
data: vec![0, 0, current[0], current[1], total[0], total[1], 0, 0],
|
data: vec![0, 0, current[0], current[1], total[0], total[1], 0, 0],
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
@ -802,13 +810,13 @@ mod tests {
|
||||||
use crate::mp4::ilst::atom::AtomDataStorage;
|
use crate::mp4::ilst::atom::AtomDataStorage;
|
||||||
use crate::mp4::ilst::TITLE;
|
use crate::mp4::ilst::TITLE;
|
||||||
use crate::mp4::read::AtomReader;
|
use crate::mp4::read::AtomReader;
|
||||||
use crate::mp4::{AdvisoryRating, Atom, AtomData, AtomIdent, Ilst, Mp4File};
|
use crate::mp4::{AdvisoryRating, Atom, AtomData, AtomIdent, DataType, Ilst, Mp4File};
|
||||||
|
use crate::picture::{MimeType, Picture, PictureType};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::tag::utils::test_utils;
|
use crate::tag::utils::test_utils;
|
||||||
use crate::tag::utils::test_utils::read_path;
|
use crate::tag::utils::test_utils::read_path;
|
||||||
use crate::tag::{ItemValue, Tag, TagItem, TagType};
|
use crate::tag::{ItemValue, Tag, TagItem, TagType};
|
||||||
|
|
||||||
use crate::picture::{MimeType, Picture, PictureType};
|
|
||||||
use std::io::{Cursor, Read as _, Seek as _, Write as _};
|
use std::io::{Cursor, Read as _, Seek as _, Write as _};
|
||||||
|
|
||||||
fn read_ilst(path: &str, parse_mode: ParsingMode) -> Ilst {
|
fn read_ilst(path: &str, parse_mode: ParsingMode) -> Ilst {
|
||||||
|
@ -853,7 +861,7 @@ mod tests {
|
||||||
expected_tag.insert(Atom::new(
|
expected_tag.insert(Atom::new(
|
||||||
AtomIdent::Fourcc(*b"trkn"),
|
AtomIdent::Fourcc(*b"trkn"),
|
||||||
AtomData::Unknown {
|
AtomData::Unknown {
|
||||||
code: 0,
|
code: DataType::Reserved,
|
||||||
data: vec![0, 0, 0, 1, 0, 0, 0, 0],
|
data: vec![0, 0, 0, 1, 0, 0, 0, 0],
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
@ -862,7 +870,7 @@ mod tests {
|
||||||
expected_tag.insert(Atom::new(
|
expected_tag.insert(Atom::new(
|
||||||
AtomIdent::Fourcc(*b"disk"),
|
AtomIdent::Fourcc(*b"disk"),
|
||||||
AtomData::Unknown {
|
AtomData::Unknown {
|
||||||
code: 0,
|
code: DataType::Reserved,
|
||||||
data: vec![0, 0, 0, 1, 0, 2],
|
data: vec![0, 0, 0, 1, 0, 2],
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
@ -997,7 +1005,7 @@ mod tests {
|
||||||
&ilst,
|
&ilst,
|
||||||
*b"trkn",
|
*b"trkn",
|
||||||
&AtomData::Unknown {
|
&AtomData::Unknown {
|
||||||
code: 0,
|
code: DataType::Reserved,
|
||||||
data: vec![0, 0, 0, 1, 0, 0, 0, 0],
|
data: vec![0, 0, 0, 1, 0, 0, 0, 0],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1005,7 +1013,7 @@ mod tests {
|
||||||
&ilst,
|
&ilst,
|
||||||
*b"disk",
|
*b"disk",
|
||||||
&AtomData::Unknown {
|
&AtomData::Unknown {
|
||||||
code: 0,
|
code: DataType::Reserved,
|
||||||
data: vec![0, 0, 0, 1, 0, 2, 0, 0],
|
data: vec![0, 0, 0, 1, 0, 2, 0, 0],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1024,7 +1032,7 @@ mod tests {
|
||||||
&ilst,
|
&ilst,
|
||||||
*b"plID",
|
*b"plID",
|
||||||
&AtomData::Unknown {
|
&AtomData::Unknown {
|
||||||
code: 21,
|
code: DataType::BeSignedInteger,
|
||||||
data: 88888_u64.to_be_bytes().to_vec(),
|
data: 88888_u64.to_be_bytes().to_vec(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1321,7 +1329,7 @@ mod tests {
|
||||||
let atom = Atom::new(
|
let atom = Atom::new(
|
||||||
AtomIdent::Fourcc(*b"SMTH"),
|
AtomIdent::Fourcc(*b"SMTH"),
|
||||||
AtomData::Unknown {
|
AtomData::Unknown {
|
||||||
code: 0,
|
code: DataType::Reserved,
|
||||||
data: b"Meaningless Data".to_vec(),
|
data: b"Meaningless Data".to_vec(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use super::constants::{
|
use super::constants::WELL_KNOWN_TYPE_SET;
|
||||||
BE_SIGNED_INTEGER, BE_UNSIGNED_INTEGER, BMP, JPEG, PNG, RESERVED, UTF16, UTF8,
|
use super::data_type::DataType;
|
||||||
};
|
|
||||||
use super::{Atom, AtomData, AtomIdent, Ilst};
|
use super::{Atom, AtomData, AtomIdent, Ilst};
|
||||||
use crate::config::{ParseOptions, ParsingMode};
|
use crate::config::{ParseOptions, ParsingMode};
|
||||||
use crate::error::{LoftyError, Result};
|
use crate::error::{LoftyError, Result};
|
||||||
use crate::id3::v1::constants::GENRES;
|
use crate::id3::v1::constants::GENRES;
|
||||||
use crate::macros::{err, try_vec};
|
use crate::macros::{err, try_vec};
|
||||||
use crate::mp4::atom_info::AtomInfo;
|
use crate::mp4::atom_info::{AtomInfo, ATOM_HEADER_LEN};
|
||||||
use crate::mp4::ilst::atom::AtomDataStorage;
|
use crate::mp4::ilst::atom::AtomDataStorage;
|
||||||
use crate::mp4::read::{skip_unneeded, AtomReader};
|
use crate::mp4::read::{skip_unneeded, AtomReader};
|
||||||
use crate::picture::{MimeType, Picture, PictureType};
|
use crate::picture::{MimeType, Picture, PictureType};
|
||||||
|
@ -204,7 +203,7 @@ fn parse_data_inner<R>(
|
||||||
reader: &mut AtomReader<R>,
|
reader: &mut AtomReader<R>,
|
||||||
parsing_mode: ParsingMode,
|
parsing_mode: ParsingMode,
|
||||||
atom_info: &AtomInfo,
|
atom_info: &AtomInfo,
|
||||||
) -> Result<Option<Vec<(u32, Vec<u8>)>>>
|
) -> Result<Option<Vec<(DataType, Vec<u8>)>>>
|
||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
{
|
{
|
||||||
|
@ -237,13 +236,14 @@ where
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't care about the version
|
let Some(data_type) = parse_type_indicator(reader, parsing_mode)? else {
|
||||||
let _version = reader.read_u8()?;
|
log::warn!("Skipping atom with unknown type set");
|
||||||
|
let remaining_atom_len = next_atom.len - (ATOM_HEADER_LEN + 1);
|
||||||
|
|
||||||
let mut flags = [0; 3];
|
reader.seek(SeekFrom::Current(remaining_atom_len as i64))?;
|
||||||
reader.read_exact(&mut flags)?;
|
pos += remaining_atom_len;
|
||||||
|
continue;
|
||||||
let flags = u32::from_be_bytes([0, flags[0], flags[1], flags[2]]);
|
};
|
||||||
|
|
||||||
// We don't care about the locale
|
// We don't care about the locale
|
||||||
reader.seek(SeekFrom::Current(4))?;
|
reader.seek(SeekFrom::Current(4))?;
|
||||||
|
@ -254,7 +254,7 @@ where
|
||||||
if content_len > 0 {
|
if content_len > 0 {
|
||||||
let mut content = try_vec![0; content_len];
|
let mut content = try_vec![0; content_len];
|
||||||
reader.read_exact(&mut content)?;
|
reader.read_exact(&mut content)?;
|
||||||
ret.push((flags, content));
|
ret.push((data_type, content));
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Skipping empty \"data\" atom");
|
log::warn!("Skipping empty \"data\" atom");
|
||||||
}
|
}
|
||||||
|
@ -268,7 +268,9 @@ where
|
||||||
"Skipping unexpected atom {actual_ident:?}, expected {expected_ident:?}",
|
"Skipping unexpected atom {actual_ident:?}, expected {expected_ident:?}",
|
||||||
actual_ident = next_atom.ident,
|
actual_ident = next_atom.ident,
|
||||||
expected_ident = DATA_ATOM_IDENT
|
expected_ident = DATA_ATOM_IDENT
|
||||||
)
|
);
|
||||||
|
|
||||||
|
reader.seek(SeekFrom::Current((next_atom.len - 16) as i64))?;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -280,6 +282,29 @@ where
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_type_indicator<R>(
|
||||||
|
reader: &mut AtomReader<R>,
|
||||||
|
parsing_mode: ParsingMode,
|
||||||
|
) -> Result<Option<DataType>>
|
||||||
|
where
|
||||||
|
R: Read + Seek,
|
||||||
|
{
|
||||||
|
// The type indicator is formed of four bytes split between two fields. The first byte indicates
|
||||||
|
// the set of types from which the type is drawn. The second through fourth byte forms the second
|
||||||
|
// field and its interpretation depends upon the value in the first field.
|
||||||
|
|
||||||
|
let type_set = reader.read_u8()?;
|
||||||
|
if type_set != WELL_KNOWN_TYPE_SET {
|
||||||
|
if parsing_mode == ParsingMode::Strict {
|
||||||
|
err!(BadAtom("Unknown type set in data atom"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(DataType::from(reader.read_u24()?)))
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_uint(bytes: &[u8]) -> Result<u32> {
|
fn parse_uint(bytes: &[u8]) -> Result<u32> {
|
||||||
Ok(match bytes.len() {
|
Ok(match bytes.len() {
|
||||||
1 => u32::from(bytes[0]),
|
1 => u32::from(bytes[0]),
|
||||||
|
@ -317,15 +342,15 @@ where
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
|
|
||||||
let len = atom_data.len();
|
let len = atom_data.len();
|
||||||
for (flags, value) in atom_data {
|
for (data_type, value) in atom_data {
|
||||||
let mime_type = match flags {
|
let mime_type = match data_type {
|
||||||
// Type 0 is implicit
|
// Type 0 is implicit
|
||||||
RESERVED => None,
|
DataType::Reserved => None,
|
||||||
// GIF is deprecated
|
// GIF is deprecated
|
||||||
12 => Some(MimeType::Gif),
|
DataType::Gif => Some(MimeType::Gif),
|
||||||
JPEG => Some(MimeType::Jpeg),
|
DataType::Jpeg => Some(MimeType::Jpeg),
|
||||||
PNG => Some(MimeType::Png),
|
DataType::Png => Some(MimeType::Png),
|
||||||
BMP => Some(MimeType::Bmp),
|
DataType::Bmp => Some(MimeType::Bmp),
|
||||||
_ => err!(BadAtom("\"covr\" atom has an unknown type")),
|
_ => err!(BadAtom("\"covr\" atom has an unknown type")),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -357,13 +382,13 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interpret_atom_content(flags: u32, content: Vec<u8>) -> Result<AtomData> {
|
fn interpret_atom_content(flags: DataType, content: Vec<u8>) -> Result<AtomData> {
|
||||||
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35
|
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35
|
||||||
Ok(match flags {
|
Ok(match flags {
|
||||||
UTF8 => AtomData::UTF8(utf8_decode(content)?),
|
DataType::Utf8 => AtomData::UTF8(utf8_decode(content)?),
|
||||||
UTF16 => AtomData::UTF16(utf16_decode_bytes(&content, u16::from_be_bytes)?),
|
DataType::Utf16 => AtomData::UTF16(utf16_decode_bytes(&content, u16::from_be_bytes)?),
|
||||||
BE_SIGNED_INTEGER => AtomData::SignedInteger(parse_int(&content)?),
|
DataType::BeSignedInteger => AtomData::SignedInteger(parse_int(&content)?),
|
||||||
BE_UNSIGNED_INTEGER => AtomData::UnsignedInteger(parse_uint(&content)?),
|
DataType::BeUnsignedInteger => AtomData::UnsignedInteger(parse_uint(&content)?),
|
||||||
code => AtomData::Unknown {
|
code => AtomData::Unknown {
|
||||||
code,
|
code,
|
||||||
data: content,
|
data: content,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use super::data_type::DataType;
|
||||||
use super::r#ref::IlstRef;
|
use super::r#ref::IlstRef;
|
||||||
use crate::config::{ParseOptions, WriteOptions};
|
use crate::config::{ParseOptions, WriteOptions};
|
||||||
use crate::error::{FileEncodingError, LoftyError, Result};
|
use crate::error::{FileEncodingError, LoftyError, Result};
|
||||||
|
@ -14,9 +15,6 @@ use crate::util::io::{FileLike, Length, Truncate};
|
||||||
|
|
||||||
use std::io::{Cursor, Seek, SeekFrom, Write};
|
use std::io::{Cursor, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
use crate::mp4::constants::{
|
|
||||||
BE_SIGNED_INTEGER, BE_UNSIGNED_INTEGER, BMP, JPEG, PNG, RESERVED, UTF16, UTF8,
|
|
||||||
};
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
// A "full" atom is a traditional length + identifier, followed by a version (1) and flags (3)
|
// A "full" atom is a traditional length + identifier, followed by a version (1) and flags (3)
|
||||||
|
@ -678,8 +676,8 @@ where
|
||||||
{
|
{
|
||||||
for value in data {
|
for value in data {
|
||||||
match value {
|
match value {
|
||||||
AtomData::UTF8(text) => write_data(UTF8, text.as_bytes(), writer)?,
|
AtomData::UTF8(text) => write_data(DataType::Utf8, text.as_bytes(), writer)?,
|
||||||
AtomData::UTF16(text) => write_data(UTF16, text.as_bytes(), writer)?,
|
AtomData::UTF16(text) => write_data(DataType::Utf16, text.as_bytes(), writer)?,
|
||||||
AtomData::Picture(ref pic) => write_picture(pic, writer)?,
|
AtomData::Picture(ref pic) => write_picture(pic, writer)?,
|
||||||
AtomData::SignedInteger(int) => write_signed_int(*int, writer)?,
|
AtomData::SignedInteger(int) => write_signed_int(*int, writer)?,
|
||||||
AtomData::UnsignedInteger(uint) => write_unsigned_int(*uint, writer)?,
|
AtomData::UnsignedInteger(uint) => write_unsigned_int(*uint, writer)?,
|
||||||
|
@ -692,7 +690,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_signed_int(int: i32, writer: &mut AtomWriterCompanion<'_>) -> Result<()> {
|
fn write_signed_int(int: i32, writer: &mut AtomWriterCompanion<'_>) -> Result<()> {
|
||||||
write_int(BE_SIGNED_INTEGER, int.to_be_bytes(), 4, writer)
|
write_int(DataType::BeSignedInteger, int.to_be_bytes(), 4, writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bytes_to_occupy_uint(uint: u32) -> usize {
|
fn bytes_to_occupy_uint(uint: u32) -> usize {
|
||||||
|
@ -710,7 +708,7 @@ fn bytes_to_occupy_uint(uint: u32) -> usize {
|
||||||
fn write_unsigned_int(uint: u32, writer: &mut AtomWriterCompanion<'_>) -> Result<()> {
|
fn write_unsigned_int(uint: u32, writer: &mut AtomWriterCompanion<'_>) -> Result<()> {
|
||||||
let bytes_needed = bytes_to_occupy_uint(uint);
|
let bytes_needed = bytes_to_occupy_uint(uint);
|
||||||
write_int(
|
write_int(
|
||||||
BE_UNSIGNED_INTEGER,
|
DataType::BeUnsignedInteger,
|
||||||
uint.to_be_bytes(),
|
uint.to_be_bytes(),
|
||||||
bytes_needed,
|
bytes_needed,
|
||||||
writer,
|
writer,
|
||||||
|
@ -718,7 +716,7 @@ fn write_unsigned_int(uint: u32, writer: &mut AtomWriterCompanion<'_>) -> Result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_int(
|
fn write_int(
|
||||||
flags: u32,
|
flags: DataType,
|
||||||
bytes: [u8; 4],
|
bytes: [u8; 4],
|
||||||
bytes_needed: usize,
|
bytes_needed: usize,
|
||||||
writer: &mut AtomWriterCompanion<'_>,
|
writer: &mut AtomWriterCompanion<'_>,
|
||||||
|
@ -728,18 +726,23 @@ fn write_int(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_bool(b: bool, writer: &mut AtomWriterCompanion<'_>) -> Result<()> {
|
fn write_bool(b: bool, writer: &mut AtomWriterCompanion<'_>) -> Result<()> {
|
||||||
write_int(BE_SIGNED_INTEGER, i32::from(b).to_be_bytes(), 1, writer)
|
write_int(
|
||||||
|
DataType::BeSignedInteger,
|
||||||
|
i32::from(b).to_be_bytes(),
|
||||||
|
1,
|
||||||
|
writer,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_picture(picture: &Picture, writer: &mut AtomWriterCompanion<'_>) -> Result<()> {
|
fn write_picture(picture: &Picture, writer: &mut AtomWriterCompanion<'_>) -> Result<()> {
|
||||||
match picture.mime_type {
|
match picture.mime_type {
|
||||||
// GIF is deprecated
|
// GIF is deprecated
|
||||||
Some(MimeType::Gif) => write_data(12, &picture.data, writer),
|
Some(MimeType::Gif) => write_data(DataType::Gif, &picture.data, writer),
|
||||||
Some(MimeType::Jpeg) => write_data(JPEG, &picture.data, writer),
|
Some(MimeType::Jpeg) => write_data(DataType::Jpeg, &picture.data, writer),
|
||||||
Some(MimeType::Png) => write_data(PNG, &picture.data, writer),
|
Some(MimeType::Png) => write_data(DataType::Png, &picture.data, writer),
|
||||||
Some(MimeType::Bmp) => write_data(BMP, &picture.data, writer),
|
Some(MimeType::Bmp) => write_data(DataType::Bmp, &picture.data, writer),
|
||||||
// We'll assume implicit (0) was the intended type
|
// We'll assume implicit (0) was the intended type
|
||||||
None => write_data(RESERVED, &picture.data, writer),
|
None => write_data(DataType::Reserved, &picture.data, writer),
|
||||||
_ => Err(FileEncodingError::new(
|
_ => Err(FileEncodingError::new(
|
||||||
FileType::Mp4,
|
FileType::Mp4,
|
||||||
"Attempted to write an unsupported picture format",
|
"Attempted to write an unsupported picture format",
|
||||||
|
@ -748,8 +751,8 @@ fn write_picture(picture: &Picture, writer: &mut AtomWriterCompanion<'_>) -> Res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data(flags: u32, data: &[u8], writer: &mut AtomWriterCompanion<'_>) -> Result<()> {
|
fn write_data(flags: DataType, data: &[u8], writer: &mut AtomWriterCompanion<'_>) -> Result<()> {
|
||||||
if flags > 16_777_215 {
|
if u32::from(flags) > DataType::MAX {
|
||||||
return Err(FileEncodingError::new(
|
return Err(FileEncodingError::new(
|
||||||
FileType::Mp4,
|
FileType::Mp4,
|
||||||
"Attempted to write a code that cannot fit in 24 bits",
|
"Attempted to write a code that cannot fit in 24 bits",
|
||||||
|
|
|
@ -23,8 +23,9 @@ pub mod constants {
|
||||||
|
|
||||||
pub use crate::mp4::properties::{AudioObjectType, Mp4Codec, Mp4Properties};
|
pub use crate::mp4::properties::{AudioObjectType, Mp4Codec, Mp4Properties};
|
||||||
pub use atom_info::AtomIdent;
|
pub use atom_info::AtomIdent;
|
||||||
pub use ilst::atom::{AdvisoryRating, Atom, AtomData};
|
|
||||||
pub use ilst::advisory_rating::AdvisoryRating;
|
pub use ilst::advisory_rating::AdvisoryRating;
|
||||||
|
pub use ilst::atom::{Atom, AtomData};
|
||||||
|
pub use ilst::data_type::DataType;
|
||||||
pub use ilst::Ilst;
|
pub use ilst::Ilst;
|
||||||
|
|
||||||
pub(crate) use properties::SAMPLE_RATES;
|
pub(crate) use properties::SAMPLE_RATES;
|
||||||
|
|
Loading…
Reference in a new issue