use bitflags::bitflags;
use byteorder::{BE, LE, ReadBytesExt};
use std::io;
use std::io::{Read, Seek};
use std::num::NonZero;
use std::ops::Deref;
use strum::Display;
use crate::Parse;
use crate::ascii_str::{self, AsciiArray, AsciiStr};
use crate::bit_reader::BitReader;
use crate::bits::Bitset;
use crate::num::*;
#[derive(Debug, thiserror::Error)]
pub enum ParseError {
#[error("IO: {0}")]
Io(#[from] io::Error),
#[error("The metadata block type is 127, which is forbidden")]
ForbiddenBlockType,
#[error("Block size {1} is invalid for block type {0}")]
InvalidBlockSize(BlockType, U24),
#[error("A reserved block type was used")]
ReservedBlockType,
#[error(
"{0} is not contained within the valid range `4..=32`: The streaminfo block indicated an invalid bits_per_channel"
)]
InvalidBitsPerSample(U5),
#[error("{0} is an invalid maximum block size")]
InvalidMaxBlockSize(u16),
#[error("{0} is an invalid minimum block size")]
InvalidMinBlockSize(u16),
#[error("{0}")]
InvalidUtf8String(#[from] std::string::FromUtf8Error),
#[error("{0}")]
InvalidUtf8(#[from] std::str::Utf8Error),
#[error("{0}")]
ParseInt(#[from] std::num::ParseIntError),
#[error("Invalid vorbis comment")]
InvalidVorbisComment,
#[error("Vorbis comment key contains invalid ASCII")]
CommentKeyNotAscii,
#[error("Invalid cuesheet")]
InvalidCuesheet,
#[error("Invalid cuesheet track")]
InvalidCuesheetTrack,
#[error("Invalid cuesheet track index points")]
InvalidCuesheetIndexPoints,
#[error("Invalid cuesheet track index point")]
InvalidCuesheetIndexPoint,
#[error("Invalid channel mask")]
InvalidChannelMask,
#[error("Non-ASCII ISRC in cuesheet track")]
NonAscii(#[from] ascii_str::NonAscii),
#[error("The lengths coded within the metadata block exceed the block length")]
BlockExceededLength,
}
#[derive(Debug, Clone, Copy, Eq, Display, PartialEq)]
#[repr(u8)]
pub enum BlockType {
Streaminfo = 0,
Padding,
Application,
SeekTable,
VorbisComment,
Cuesheet,
Picture,
Forbidden = 127,
}
impl TryFrom<u8> for BlockType {
type Error = ParseError;
#[inline]
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
0 => Self::Streaminfo,
1 => Self::Padding,
2 => Self::Application,
3 => Self::SeekTable,
4 => Self::VorbisComment,
5 => Self::Cuesheet,
6 => Self::Picture,
127 => Self::Forbidden,
_ => return Err(ParseError::ReservedBlockType),
})
}
}
impl From<BlockType> for u8 {
#[inline]
fn from(value: BlockType) -> Self {
match value {
BlockType::Streaminfo => 0,
BlockType::Padding => 1,
BlockType::Application => 2,
BlockType::SeekTable => 3,
BlockType::VorbisComment => 4,
BlockType::Cuesheet => 5,
BlockType::Picture => 6,
BlockType::Forbidden => 127,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct BlockHeader {
pub is_final: bool,
pub kind: BlockType,
pub size: U24,
}
impl BlockHeader {
#[inline]
pub fn block_type_num_from_u8(byte: u8) -> Result<BlockType, ParseError> {
BlockType::try_from(byte.get_bit_range_msb(1, 7))
}
}
impl Parse<()> for BlockHeader {
type Error = ParseError;
fn parse_from_reader<R: Read>(reader: &mut BitReader<R>, _opt: ()) -> Result<Self, ParseError> {
let kind = reader.read_u8()?;
let size = U24::new(reader.read_u24::<BE>()?).expect("this is a valid u24");
let is_final = kind.get_bit_msb(0);
let kind = Self::block_type_num_from_u8(kind)?;
if kind == BlockType::Forbidden {
return Err(ParseError::ForbiddenBlockType);
}
Ok(Self {
is_final,
kind,
size,
})
}
}
#[derive(Clone)]
pub struct ReservedData(pub Vec<u8>);
impl std::fmt::Debug for ReservedData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ReservedData").finish_non_exhaustive()
}
}
#[derive(Debug, Clone)]
pub enum BlockData {
Streaminfo(Streaminfo),
Padding,
Application(Application),
SeekTable(SeekTable),
VorbisComment(VorbisComment),
Cuesheet(Cuesheet),
Picture(Picture),
}
#[derive(Debug)]
pub struct Block {
pub header: BlockHeader,
pub data: BlockData,
}
impl Parse<()> for Block {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
_opt: (),
) -> Result<Self, ParseError> {
let header = BlockHeader::parse_from_reader(reader, ())?;
let invalid_block_size = Err(ParseError::InvalidBlockSize(header.kind, header.size));
let data = match header.kind {
BlockType::Forbidden => return Err(ParseError::ForbiddenBlockType),
BlockType::Streaminfo if header.size < 4 => return invalid_block_size,
BlockType::Streaminfo if header.size != 34 => return invalid_block_size,
BlockType::VorbisComment if header.size < 8 => return invalid_block_size,
BlockType::Cuesheet if header.size < 432 => return invalid_block_size,
BlockType::Picture if header.size < 32 => return invalid_block_size,
BlockType::Padding if header.size == 0 => BlockData::Padding,
BlockType::Padding => {
reader.seek_relative(i64::from(header.size.inner()))?;
BlockData::Padding
}
BlockType::Streaminfo => {
BlockData::Streaminfo(Streaminfo::parse_from_reader(reader, ())?)
}
BlockType::Application => {
BlockData::Application(Application::parse_from_reader(reader, header.size)?)
}
BlockType::SeekTable => {
BlockData::SeekTable(SeekTable::parse_from_reader(reader, header.size)?)
}
BlockType::VorbisComment => {
BlockData::VorbisComment(VorbisComment::parse_from_reader(reader, ())?)
}
BlockType::Cuesheet => BlockData::Cuesheet(Cuesheet::parse_from_reader(reader, ())?),
BlockType::Picture => {
BlockData::Picture(Picture::parse_from_reader(reader, header.size.inner())?)
}
};
Ok(Self { header, data })
}
}
pub struct BlockIter<'a, R: Read + Seek> {
reader: &'a mut BitReader<R>,
ended: bool,
}
impl<'a, R: Read + Seek> BlockIter<'a, R> {
pub fn new(reader: &'a mut BitReader<R>) -> Self {
Self {
reader,
ended: false,
}
}
}
impl<'a, R: Read + Seek> Iterator for BlockIter<'a, R> {
type Item = Result<Block, ParseError>;
fn next(&mut self) -> Option<Self::Item> {
if self.ended {
return None;
}
match Block::parse_from_reader(self.reader, ()) {
Err(e) => {
self.ended = true;
Some(Err(e))
}
Ok(block) => {
if block.header.is_final {
self.ended = true;
}
Some(Ok(block))
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Streaminfo {
pub min_block_size_excluding_final: u16,
pub max_block_size: u16,
pub min_frame_size: U24,
pub max_frame_size: U24,
pub sample_rate: U20,
pub num_channels: U3,
pub bits_per_sample: U5,
pub num_samples: Option<NonZeroU36>,
pub md5sum: Option<NonZero<u128>>,
}
impl Streaminfo {
pub const LARGEST_POSSIBLE_BLOCK_SIZE: u16 = u16::MAX;
#[inline]
pub fn num_channels(&self) -> usize {
self.num_channels.inner() as usize + 1
}
}
impl Parse<()> for Streaminfo {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
_opt: (),
) -> Result<Self, ParseError> {
let min_block_size_excluding_final = reader.read_u16::<BE>()?;
let max_block_size = reader.read_u16::<BE>()?;
let min_frame_size = U24::new(reader.read_u24::<BE>()?).expect("Should be valid u24");
let max_frame_size = U24::new(reader.read_u24::<BE>()?).expect("Should be valid u24");
let (sample_rate, num_channels, bits_per_sample, num_samples) = {
let bytes = reader.read_u64::<BE>()?;
let sample_rate =
U20::new(bytes.get_bit_range_msb(0, 20) as u32).expect("Should be a valid u20");
let num_channels =
U3::new(bytes.get_bit_range_msb(20, 3) as u8).expect("Should be a valid u3");
let bits_per_sample =
U5::new(bytes.get_bit_range_msb(23, 5) as u8 + 1).expect("Should be a valid u5");
let num_samples = NonZeroU36::new(
U36::new(bytes.get_bit_range_msb(28, 36)).expect("Should be a valid u36"),
);
(sample_rate, num_channels, bits_per_sample, num_samples)
};
let md5sum = NonZero::new(reader.read_u128::<BE>()?);
if min_block_size_excluding_final < 16 {
return Err(ParseError::InvalidMinBlockSize(
min_block_size_excluding_final,
));
}
if max_block_size < 16 {
return Err(ParseError::InvalidMaxBlockSize(max_block_size));
}
if !(4..=32).contains(&bits_per_sample.inner()) {
return Err(ParseError::InvalidBitsPerSample(bits_per_sample));
}
Ok(Self {
min_block_size_excluding_final,
max_block_size,
min_frame_size,
max_frame_size,
sample_rate,
num_channels,
bits_per_sample,
num_samples,
md5sum,
})
}
}
#[derive(Debug, Clone)]
pub struct Application {
pub id: u32,
pub data: Vec<u8>,
}
impl Parse<U24> for Application {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
block_size: U24,
) -> Result<Self, ParseError> {
let mut data = vec![0u8; block_size.inner().saturating_sub(4) as usize];
let id = reader.read_u32::<BE>()?;
reader.read_exact(&mut data)?;
Ok(Self { id, data })
}
}
#[derive(Debug, Clone)]
pub struct SeekTable(pub Vec<SeekPoint>);
impl Parse<U24> for SeekTable {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
block_size: U24,
) -> Result<Self, ParseError> {
let n_seek_points = block_size.inner() / 18;
let mut seek_points = Vec::with_capacity(n_seek_points as usize);
for _ in 0..n_seek_points {
seek_points.push(SeekPoint::parse_from_reader(reader, ())?);
}
Ok(Self(seek_points))
}
}
#[derive(Debug, Clone, Copy)]
pub struct SeekPoint {
pub sample_idx: u64,
pub offset: u64,
pub sample_count: u16,
}
impl Parse<()> for SeekPoint {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
_opt: (),
) -> Result<Self, ParseError> {
let sample_idx = reader.read_u64::<BE>()?;
let offset = reader.read_u64::<BE>()?;
let sample_count = reader.read_u16::<BE>()?;
Ok(Self {
sample_idx,
offset,
sample_count,
})
}
}
impl SeekPoint {
pub const fn is_placeholder(self) -> bool {
self.sample_idx == u64::MAX
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ChannelMask: u32 {
const FRONT_LEFT = 0x1;
const FRONT_RIGHT = 0x2;
const FRONT_CENTER = 0x4;
const LOW_FREQUENCY_EFFECTS = 0x8;
const BACK_LEFT = 0x10;
const BACK_RIGHT = 0x20;
const FRONT_LEFT_OF_CENTER = 0x40;
const FRONT_RIGHT_OF_CENTER = 0x80;
const BACK_CENTER = 0x100;
const SIDE_LEFT = 0x200;
const SIDE_RIGHT = 0x400;
const TOP_CENTER = 0x800;
const TOP_FRONT_LEFT = 0x1000;
const TOP_FRONT_CENTER = 0x2000;
const TOP_FRONT_RIGHT = 0x4000;
const TOP_REAR_LEFT = 0x8000;
const TOP_REAR_CENTER = 0x10_000;
const TOP_REAR_RIGHT = 0x20_000;
}
}
impl ChannelMask {
pub fn from_hex(hex: &str) -> Result<Self, std::num::ParseIntError> {
Ok(Self::from_bits_truncate(u32::from_str_radix(hex, 16)?))
}
}
#[derive(Debug, Clone)]
#[must_use]
pub struct VorbisComment {
pub vendor: String,
pub user_comment_list: Vec<VorbisUserComment>,
}
impl VorbisComment {
pub fn get_channel_mask(&self) -> Option<Result<ChannelMask, ParseError>> {
self.user_comment_list
.iter()
.find(|c| c.key.as_str() == "WAVEFORMATEXTENSIBLE_CHANNEL_MASK")
.map(|c| {
let value = c.value.get(2..).ok_or(ParseError::InvalidChannelMask);
value.and_then(|x| ChannelMask::from_hex(x).map_err(ParseError::from))
})
}
}
impl Parse<()> for VorbisComment {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
_opt: (),
) -> Result<Self, ParseError> {
let vendor_length = reader.read_u32::<LE>()?;
let vendor = {
let mut buf = vec![0u8; vendor_length as usize];
reader.read_exact(&mut buf)?;
String::from_utf8(buf)?
};
let user_comment_count = reader.read_u32::<LE>()?;
let mut user_comment_list = Vec::with_capacity(user_comment_count as usize);
for _ in 0..user_comment_count {
user_comment_list.push(VorbisUserComment::parse_from_reader(reader, ())?);
}
Ok(Self {
vendor,
user_comment_list,
})
}
}
#[derive(Debug, Clone)]
pub struct VorbisUserComment {
pub key: Box<AsciiStr>,
pub value: String,
}
impl VorbisUserComment {
pub fn new(key: &AsciiStr, value: impl Into<String>) -> Option<Self> {
let key: Box<_> = key.into();
let value = value.into();
Self::key_is_valid(&key).then_some(Self { key, value })
}
#[inline]
pub fn key_is_valid(key: &AsciiStr) -> bool {
key.bytes().all(|byte| byte != b'=')
}
}
impl Parse<()> for VorbisUserComment {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
_opt: (),
) -> Result<Self, ParseError> {
let length = reader.read_u32::<LE>()?;
let mut buf = vec![0u8; length as usize];
reader.read_exact(&mut buf)?;
let (key, value) = buf
.iter()
.position(|&x| x == b'=')
.map(|pos| buf.split_at(pos))
.map(|(key, value)| (key, &value[1..]))
.ok_or(ParseError::InvalidVorbisComment)?;
let key = Box::from(AsciiStr::from_bytes(key).ok_or(ParseError::CommentKeyNotAscii)?);
let value = str::from_utf8(value)?.to_string();
Ok(Self { key, value })
}
}
#[derive(Debug, Clone)]
pub struct Cuesheet {
pub media_catalog_number: AsciiArray<128>,
pub lead_in_samples: u64,
pub is_cdda: bool,
pub tracks: Vec<CuesheetTrack>,
}
impl Cuesheet {
#[inline]
pub const fn is_valid(&self) -> bool {
if let Some(x) = self.tracks.as_slice().last() {
x.is_lead_out(self.is_cdda)
} else {
false
}
}
}
impl Parse<()> for Cuesheet {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
_opt: (),
) -> Result<Self, ParseError> {
let media_catalog_number = {
let mut n = [0u8; 128];
reader.read_exact(&mut n)?;
let mut n = AsciiArray::try_from(n)?;
n.rtrim(b"\0");
n
};
let lead_in_samples = reader.read_u64::<BE>()?;
let is_cdda = {
let cdda = reader.read_u8()?;
cdda.get_bit_msb(0)
};
reader.seek_relative(258)?;
let track_count = reader.read_u8()?;
let mut tracks = Vec::with_capacity(track_count.into());
for _ in 0..track_count {
tracks.push(CuesheetTrack::parse_from_reader(reader, is_cdda)?);
}
let cuesheet = Self {
media_catalog_number,
lead_in_samples,
is_cdda,
tracks,
};
cuesheet
.is_valid()
.then_some(cuesheet)
.ok_or(ParseError::InvalidCuesheet)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CuesheetTrack {
pub offset: u64,
pub number: NonZero<u8>,
pub isrc: Option<AsciiArray<12>>,
pub is_audio: bool,
pub pre_emphasis: bool,
pub index_points: CuesheetTrackIndexPoints,
}
impl CuesheetTrack {
#[inline]
pub const fn is_lead_out(&self, cdda: bool) -> bool {
!cdda && self.number.get() == 255 || self.number.get() == 170
}
#[inline]
pub const fn is_valid(&self, cdda: bool) -> bool {
let is_number_valid = match (cdda, self.number.get()) {
(true, 1..=99 | 170) => true,
(false, 1..=255) => true,
_ => false,
};
let is_offset_valid = !cdda || (self.offset.is_multiple_of(588));
let is_index_point_count_valid = if cdda {
self.index_points.len <= 100 || self.is_lead_out(cdda)
} else {
true
};
let is_isrc_valid = match self.isrc {
None => true,
Some(isrc) => {
let mut valid = true;
let mut i = 0usize;
while valid && i < 12 {
valid = valid && isrc.get(i) != 0 && isrc.get(i).is_ascii_alphanumeric();
i += 1;
}
valid
}
};
is_offset_valid
&& is_number_valid
&& is_index_point_count_valid
&& is_isrc_valid
&& self.index_points.is_valid(cdda)
}
}
impl Parse<bool> for CuesheetTrack {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
is_cdda: bool,
) -> Result<Self, ParseError> {
let offset = reader.read_u64::<BE>()?;
let number = NonZero::new(reader.read_u8()?).ok_or(ParseError::InvalidCuesheetTrack)?;
let isrc = {
let mut isrc = [0u8; 12];
reader.read_exact(&mut isrc)?;
if isrc.iter().all(|&x| x == 0) {
None
} else {
Some(AsciiArray::try_from(isrc)?)
}
};
let (is_audio, pre_emphasis) = {
let flags = reader.read_u8()?;
let is_audio = flags.get_bit_msb(0);
let pre_emphasis = flags.get_bit_msb(1);
(is_audio, pre_emphasis)
};
reader.seek_relative(13)?;
let index_points = CuesheetTrackIndexPoints::parse_from_reader(reader, is_cdda)?;
let x = Self {
offset,
number,
isrc,
is_audio,
pre_emphasis,
index_points,
};
if x.is_valid(is_cdda) {
Ok(x)
} else {
Err(ParseError::InvalidCuesheetTrack)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CuesheetTrackIndexPoints {
pub len: u8,
pub index_points: [CuesheetTrackIndexPoint; u8::MAX as usize],
}
impl Deref for CuesheetTrackIndexPoints {
type Target = [CuesheetTrackIndexPoint];
#[inline]
fn deref(&self) -> &Self::Target {
self.index_points
.split_at_checked(self.len as usize)
.unwrap()
.0
}
}
impl CuesheetTrackIndexPoints {
#[inline]
pub const fn is_valid_cdda(&self) -> bool {
let mut valid = true;
let mut i = 0usize;
while valid && i < self.len as usize {
let point = self.index_points[i];
if i == 0 {
valid = valid && matches!(point.number, 0 | 1);
} else {
let prev = self.index_points[i - 1];
valid = valid && point.number > prev.number;
}
valid = valid && point.is_valid_cdda();
i += 1;
}
valid
}
#[inline]
pub const fn is_valid(&self, cdda: bool) -> bool {
let is_unique = {
let mut valid = true;
let mut i = 1usize;
while valid && i < self.len as usize {
let point = self.index_points[i];
let prev = self.index_points[i - 1];
valid = valid && point.number != prev.number;
i += 1;
}
valid
};
(!cdda || self.is_valid_cdda()) && is_unique
}
}
impl Parse<bool> for CuesheetTrackIndexPoints {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
is_cdda: bool,
) -> Result<Self, ParseError> {
let len = reader.read_u8()?;
let mut index_points = [CuesheetTrackIndexPoint::default(); u8::MAX as usize];
for i in 0..len {
index_points[i as usize] = CuesheetTrackIndexPoint::parse_from_reader(reader, is_cdda)?;
}
let x = Self { len, index_points };
if x.is_valid(is_cdda) {
Ok(x)
} else {
Err(ParseError::InvalidCuesheetIndexPoints)
}
}
}
#[derive(Debug, PartialEq, Clone, Copy, Eq, Default)]
pub struct CuesheetTrackIndexPoint {
pub offset: u64,
pub number: u8,
}
impl CuesheetTrackIndexPoint {
#[inline]
pub const fn is_valid_cdda(self) -> bool {
self.offset.is_multiple_of(588)
}
}
impl Parse<bool> for CuesheetTrackIndexPoint {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
is_cdda: bool,
) -> Result<Self, ParseError> {
let offset = reader.read_u64::<BE>()?;
let number = reader.read_u8()?;
reader.seek_relative(3)?;
let x = Self { offset, number };
let is_valid = !is_cdda || x.is_valid_cdda();
if is_valid {
Ok(x)
} else {
Err(ParseError::InvalidCuesheetIndexPoint)
}
}
}
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum PictureType {
Other = 0,
PngFileIcon,
FileIcon,
FrontCover,
BackCover,
LinerNotes,
MediaLabel,
LeadArtist,
Artist,
Conductor,
Band,
Composer,
Lyricist,
RecordingLocation,
Recording,
Performance,
Movie,
BrightColoredFish,
Illustration,
ArtistLogotype,
Publisher,
Reserved(u32),
}
impl PictureType {
pub const fn from_repr(num: u32) -> Self {
use PictureType::*;
match num {
0 => Other,
1 => PngFileIcon,
2 => FileIcon,
3 => FrontCover,
4 => BackCover,
5 => LinerNotes,
6 => MediaLabel,
7 => LeadArtist,
8 => Artist,
9 => Conductor,
10 => Band,
11 => Composer,
12 => Lyricist,
13 => RecordingLocation,
14 => Recording,
15 => Performance,
16 => Movie,
17 => BrightColoredFish,
18 => Illustration,
19 => ArtistLogotype,
20 => Publisher,
21.. => Reserved(num),
}
}
}
#[derive(Clone)]
pub enum PictureData {
Bytes(Vec<u8>),
Uri(Vec<u8>),
}
impl std::fmt::Debug for PictureData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bytes(_) => f.debug_tuple("Bytes").finish_non_exhaustive(),
Self::Uri(_) => f.debug_tuple("Uri").finish_non_exhaustive(),
}
}
}
#[derive(Debug, Clone)]
pub struct Picture {
pub kind: PictureType,
pub media_type: AsciiArray<255>,
pub description: String,
pub width: u32,
pub height: u32,
pub color_depth: u32,
pub color_count: Option<NonZero<u32>>,
pub data: PictureData,
}
impl Parse<u32> for Picture {
type Error = ParseError;
fn parse_from_reader<R: Read + Seek>(
reader: &mut BitReader<R>,
max_length: u32,
) -> Result<Self, ParseError> {
let mut total_length = 0u32;
let mut inc_total_length = |len: u32| -> Result<(), ParseError> {
total_length += len;
if total_length > max_length {
Err(ParseError::BlockExceededLength)
} else {
Ok(())
}
};
let kind = PictureType::from_repr(reader.read_u32::<BE>()?);
let media_type = {
let length = reader.read_u32::<BE>()?;
inc_total_length(length)?;
let mut media_type = [0u8; 255];
reader.read_exact(&mut media_type[..length as usize])?;
AsciiArray::from_array(media_type, length as usize)?
};
let description = {
let length = reader.read_u32::<BE>()?;
inc_total_length(length)?;
let mut description = vec![0u8; length as usize];
reader.read_exact(&mut description)?;
String::from_utf8(description)?
};
let width = reader.read_u32::<BE>()?;
let height = reader.read_u32::<BE>()?;
let color_depth = reader.read_u32::<BE>()?;
let color_count = NonZero::new(reader.read_u32::<BE>()?);
let data = {
let length = reader.read_u32::<BE>()?;
inc_total_length(length)?;
let mut data = vec![0u8; length as usize];
reader.read_exact(&mut data)?;
match media_type.as_str() {
"-->" => PictureData::Uri(data),
_ => PictureData::Bytes(data),
}
};
Ok(Self {
kind,
media_type,
description,
width,
height,
color_depth,
color_count,
data,
})
}
}
#[cfg(test)]
mod tests {
use crate::MAGIC;
use super::*;
const FLAC_BIN: &[u8] = include_bytes!("../tests/audio/binaural-with-picture.flac");
#[test]
fn new_vorbis_user_comment() {
let _mask = VorbisUserComment::new(
AsciiStr::from_str("WAVEFORMATEXTENSIBLE_CHANNEL_MASK").unwrap(),
"0x8",
)
.unwrap();
}
#[test]
fn get_channel_mask() {
let mask = VorbisUserComment::new(
AsciiStr::from_str("WAVEFORMATEXTENSIBLE_CHANNEL_MASK").unwrap(),
"0x8",
)
.unwrap();
let block = VorbisComment {
vendor: String::from("Tgirl hooters"),
user_comment_list: vec![mask],
};
let channel_mask = block.get_channel_mask().unwrap().unwrap();
assert_eq!(channel_mask, ChannelMask::LOW_FREQUENCY_EFFECTS);
}
#[test]
fn block_header_is_final() {
let header = dbg!(BlockHeader::parse_bytes(&[0b1000_0000, 0x00, 0x00, 0x00], ()).unwrap());
assert!(header.is_final);
let header = dbg!(BlockHeader::parse_bytes(&[0b0000_0000, 0x00, 0x00, 0x00], ()).unwrap());
assert!(!header.is_final);
}
#[test]
fn block_header_kind() {
let bytes = [
0b0000_0100, 0x00,
0x00,
0x22, ];
let header = BlockHeader::parse_bytes(&bytes, ()).unwrap();
assert_eq!(header.kind, BlockType::VorbisComment);
}
fn parse_block<R: Read + Seek>(reader: &mut BitReader<R>, mut cb: impl FnMut(Block)) {
loop {
let block = Block::parse_from_reader(reader, ()).unwrap();
let is_final = block.header.is_final;
println!("{:#?}", block);
cb(block);
if is_final {
break;
}
}
}
#[test]
fn test_parse_block() {
let mut flac = BitReader::new(io::Cursor::new(FLAC_BIN));
let mut magic = [0u8; size_of_val(&MAGIC)];
flac.read_exact(&mut magic).unwrap();
assert_eq!(magic, MAGIC);
parse_block(&mut flac, |_block| {});
}
}