MP4: Check if audio streams are DRM protected

Credit to `Lukáš Lalinský` for finding the location of the undocumented `drms` atom.
Source: <https://mail.kde.org/pipermail/taglib-devel/2011-March/001886.html>
This commit is contained in:
Serial 2023-12-03 15:57:27 -05:00 committed by Alex
parent 558d7cc2f7
commit 25785701bd
3 changed files with 43 additions and 9 deletions

View file

@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- **MP4**: Check if audio streams are DRM protected, exposed as `Mp4Properties::is_drm_protected()` ([PR](https://github.com/Serial-ATA/lofty-rs/pull/297))
### Changed
- **ID3v1**: Renamed `GENRES[14]` to `"R&B"` (Previously `"Rhythm & Blues"`) ([PR](https://github.com/Serial-ATA/lofty-rs/pull/296))

View file

@ -141,6 +141,7 @@ pub struct Mp4Properties {
pub(crate) sample_rate: u32,
pub(crate) bit_depth: Option<u8>,
pub(crate) channels: u8,
pub(crate) drm_protected: bool,
}
impl From<Mp4Properties> for FileProperties {
@ -201,6 +202,11 @@ impl Mp4Properties {
pub fn audio_object_type(&self) -> Option<AudioObjectType> {
self.extended_audio_object_type
}
/// Whether or not the file is DRM protected
pub fn is_drm_protected(&self) -> bool {
self.drm_protected
}
}
pub(super) fn read_properties<R>(
@ -316,18 +322,21 @@ where
let mut stsd_reader = AtomReader::new(&mut cursor, parse_mode)?;
// Skipping 8 bytes
// Skipping 4 bytes
// Version (1)
// Flags (3)
// Number of entries (4)
stsd_reader.seek(SeekFrom::Current(8))?;
stsd_reader.seek(SeekFrom::Current(4))?;
let num_sample_entries = stsd_reader.read_u32()?;
let Some(atom) = AtomInfo::read(&mut stsd_reader, stsd.len() as u64, parse_mode)?
else {
err!(BadAtom("Expected sample entry atom in `stsd` atom"))
};
for _ in 0..num_sample_entries {
let Some(atom) = stsd_reader.next()? else {
err!(BadAtom("Expected sample entry atom in `stsd` atom"))
};
let AtomIdent::Fourcc(ref fourcc) = atom.ident else {
err!(BadAtom("Expected fourcc atom in `stsd` atom"))
};
if let AtomIdent::Fourcc(ref fourcc) = atom.ident {
match fourcc {
b"mp4a" => mp4a_properties(&mut stsd_reader, &mut properties)?,
b"alac" => alac_properties(&mut stsd_reader, &mut properties)?,
@ -335,7 +344,21 @@ where
// Maybe do these?
// TODO: dops (opus)
// TODO: wave (https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-134202)
_ => {},
// Special case to detect encrypted files
b"drms" => {
properties.drm_protected = true;
skip_unneeded(reader, atom.extended, atom.len)?;
continue;
},
_ => {
log::warn!(
"Found unsupported sample entry: {:?}",
fourcc.escape_ascii().to_string()
);
skip_unneeded(reader, atom.extended, atom.len)?;
continue;
},
}
// We do the mdat check up here, so we have access to the entire file
@ -349,6 +372,10 @@ where
(u128::from(mdat_length(reader)? * 8) / duration_millis) as u32;
}
}
// We only want to read the properties of the first stream
// that we can actually recognize
break;
}
}
}

View file

@ -241,6 +241,7 @@ mod tests {
sample_rate: 48000,
bit_depth: None,
channels: 2,
drm_protected: false,
};
const MP4_ALAC_PROPERTIES: Mp4Properties = Mp4Properties {
@ -252,6 +253,7 @@ mod tests {
sample_rate: 48000,
bit_depth: Some(16),
channels: 2,
drm_protected: false,
};
const MP4_ALS_PROPERTIES: Mp4Properties = Mp4Properties {
@ -263,6 +265,7 @@ mod tests {
sample_rate: 48000,
bit_depth: None,
channels: 2,
drm_protected: false,
};
const MP4_FLAC_PROPERTIES: Mp4Properties = Mp4Properties {
@ -274,6 +277,7 @@ mod tests {
sample_rate: 48000,
bit_depth: Some(16),
channels: 2,
drm_protected: false,
};
const MPC_SV5_PROPERTIES: MpcSv4to6Properties = MpcSv4to6Properties {