Skip to content

Commit

Permalink
meta: Ignore bytes of id3v2 padding.
Browse files Browse the repository at this point in the history
Addresses #124.
  • Loading branch information
pdeljanov committed May 30, 2022
1 parent da1ef11 commit a47da84
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 21 deletions.
14 changes: 14 additions & 0 deletions symphonia-core/src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,20 @@ pub trait SeekBuffered {
}
}

impl<'b, F: FiniteStream> FiniteStream for &'b mut F {
fn byte_len(&self) -> u64 {
(**self).byte_len()
}

fn bytes_read(&self) -> u64 {
(**self).bytes_read()
}

fn bytes_available(&self) -> u64 {
(**self).bytes_available()
}
}

/// A `FiniteStream` is a stream that has a known length in bytes.
pub trait FiniteStream {
/// Returns the length of the the stream in bytes.
Expand Down
6 changes: 3 additions & 3 deletions symphonia-core/src/io/scoped_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ impl<B: ReadBytes> ScopedStream<B> {
ScopedStream { start: inner.pos(), inner, len, read: 0 }
}

/// Returns an immutable reference to the inner `ByteStream`.
/// Returns an immutable reference to the inner stream.
pub fn inner(&self) -> &B {
&self.inner
}

/// Returns a mutable reference to the inner `ByteStream`.
/// Returns a mutable reference to the inner stream.
pub fn inner_mut(&mut self) -> &mut B {
&mut self.inner
}
Expand All @@ -45,7 +45,7 @@ impl<B: ReadBytes> ScopedStream<B> {
self.inner.ignore_bytes(self.len - self.read)
}

/// Convert the `ScopedStream` to the inner `ByteStream`.
/// Convert the `ScopedStream` to the inner stream.
pub fn into_inner(self) -> B {
self.inner
}
Expand Down
6 changes: 3 additions & 3 deletions symphonia-metadata/src/id3v2/frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ pub fn read_id3v2p2_frame<B: ReadBytes>(reader: &mut B) -> Result<FrameResult> {
// As per the specification, padding should be all 0s, but there are some tags which don't
// obey the specification.
if id != [0, 0, 0] {
warn!("padding bytes not zero.");
warn!("padding bytes not zero");
}

return Ok(FrameResult::Padding);
Expand Down Expand Up @@ -474,7 +474,7 @@ pub fn read_id3v2p3_frame<B: ReadBytes>(reader: &mut B) -> Result<FrameResult> {
// As per the specification, padding should be all 0s, but there are some tags which don't
// obey the specification.
if id != [0, 0, 0, 0] {
warn!("padding bytes not zero.");
warn!("padding bytes not zero");
}

return Ok(FrameResult::Padding);
Expand Down Expand Up @@ -539,7 +539,7 @@ pub fn read_id3v2p4_frame<B: ReadBytes + FiniteStream>(reader: &mut B) -> Result
// As per the specification, padding should be all 0s, but there are some tags which don't
// obey the specification.
if id != [0, 0, 0, 0] {
warn!("padding bytes not zero.");
warn!("padding bytes not zero");
}

return Ok(FrameResult::Padding);
Expand Down
38 changes: 23 additions & 15 deletions symphonia-metadata/src/id3v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,15 +273,15 @@ fn read_id3v2p4_extended_header<B: ReadBytes>(reader: &mut B) -> Result<Extended
}

fn read_id3v2_body<B: ReadBytes + FiniteStream>(
mut reader: B,
reader: &mut B,
header: &Header,
metadata: &mut MetadataBuilder,
) -> Result<()> {
// If there is an extended header, read and parse it based on the major version of the tag.
if header.has_extended_header {
let extended = match header.major_version {
3 => read_id3v2p3_extended_header(&mut reader)?,
4 => read_id3v2p4_extended_header(&mut reader)?,
3 => read_id3v2p3_extended_header(reader)?,
4 => read_id3v2p4_extended_header(reader)?,
_ => unreachable!(),
};
trace!("{:#?}", &extended);
Expand All @@ -296,9 +296,9 @@ fn read_id3v2_body<B: ReadBytes + FiniteStream>(
loop {
// Read frames based on the major version of the tag.
let frame = match header.major_version {
2 => read_id3v2p2_frame(&mut reader),
3 => read_id3v2p3_frame(&mut reader),
4 => read_id3v2p4_frame(&mut reader),
2 => read_id3v2p2_frame(reader),
3 => read_id3v2p3_frame(reader),
4 => read_id3v2p4_frame(reader),
_ => break,
}?;

Expand Down Expand Up @@ -342,21 +342,29 @@ pub fn read_id3v2<B: ReadBytes>(reader: &mut B, metadata: &mut MetadataBuilder)
// Read the (sorta) version agnostic tag header.
let header = read_id3v2_header(reader)?;

// The header specified the byte length of the contents of the ID3v2 tag (excluding the header),
// use a scoped reader to ensure we don't exceed that length, and to determine if there are no
// more frames left to parse.
let scoped = ScopedStream::new(reader, u64::from(header.size));

// If the unsynchronisation flag is set in the header, all tag data must be passed through the
// unsynchronisation decoder before being read for verions < 4 of ID3v2.
if header.unsynchronisation && header.major_version < 4 {
read_id3v2_body(UnsyncStream::new(scoped), &header, metadata)
let mut scoped = if header.unsynchronisation && header.major_version < 4 {
let mut unsync = UnsyncStream::new(ScopedStream::new(reader, u64::from(header.size)));

read_id3v2_body(&mut unsync, &header, metadata)?;

unsync.into_inner()
}
// Otherwise, read the data as-is. Individual frames may be unsynchronised for major versions
// >= 4.
else {
read_id3v2_body(scoped, &header, metadata)
}
let mut scoped = ScopedStream::new(reader, u64::from(header.size));

read_id3v2_body(&mut scoped, &header, metadata)?;

scoped
};

// Ignore any remaining data in the tag.
scoped.ignore()?;

Ok(())
}

pub mod util {
Expand Down
5 changes: 5 additions & 0 deletions symphonia-metadata/src/id3v2/unsync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ impl<B: ReadBytes + FiniteStream> UnsyncStream<B> {
pub fn new(inner: B) -> Self {
UnsyncStream { inner, byte: 0 }
}

/// Convert the `UnsyncStream` to the inner stream.
pub fn into_inner(self) -> B {
self.inner
}
}

impl<B: ReadBytes + FiniteStream> FiniteStream for UnsyncStream<B> {
Expand Down

0 comments on commit a47da84

Please sign in to comment.