From 16497ee5fb0d2f00287ba3444c52b22bc4debdae Mon Sep 17 00:00:00 2001 From: Philip Deljanov Date: Thu, 25 Nov 2021 18:55:51 -0500 Subject: [PATCH] mp3: Try partial decode on main_data underflow. If the bit resevoir does not have enough bits to fully read the main data of both granules, drop the granule(s) associated with the missing bits. In many cases there will be enough data to decode the second granule. Previously, the entire packet would not be decoded, but this improvement will allow more audio to be preserved in the case of an error. --- symphonia-bundle-mp3/src/common.rs | 16 +++++--- symphonia-bundle-mp3/src/layer3/mod.rs | 41 +++++++++++++------ symphonia-bundle-mp3/src/layer3/requantize.rs | 12 ++++-- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/symphonia-bundle-mp3/src/common.rs b/symphonia-bundle-mp3/src/common.rs index b003697d..fcafb475 100644 --- a/symphonia-bundle-mp3/src/common.rs +++ b/symphonia-bundle-mp3/src/common.rs @@ -8,6 +8,8 @@ use symphonia_core::audio::{Channels, Layout, SignalSpec}; use symphonia_core::errors::{Result, decode_error}; +use log::warn; + use super::synthesis; /// The number of audio samples per granule. @@ -355,7 +357,7 @@ impl BitResevoir { pkt_main_data: &[u8], main_data_begin: usize, main_data_size: usize - ) -> Result<()> { + ) -> Result { // The value `main_data_begin` indicates the number of bytes from the previous frames to // reuse. It must always be less than or equal to maximum amount of bytes the resevoir can @@ -377,18 +379,22 @@ impl BitResevoir { self.buf[main_data_begin..main_data_end].copy_from_slice(pkt_main_data); self.len = main_data_end; - Ok(()) + Ok(0) } else { + let underflow = (main_data_begin - self.len) as u32; + // If the offset is greater than the amount of data in the resevoir, then the stream is // malformed. This can occur if the decoder is starting in the middle of a stream. This // is particularly common with online radio streams. In this case, copy the main data - // of the current packet into the resevoir, then return an error since decoding the - // current packet would produce a painful sound. + // of the current packet into the resevoir, then return the number of bytes that are + // missing. self.buf[self.len..self.len + main_data_size].copy_from_slice(pkt_main_data); self.len += main_data_size; - decode_error("mp3: invalid main_data_begin") + warn!("mp3: invalid main_data_begin, underflow by {} bytes", underflow); + + Ok(underflow) } } diff --git a/symphonia-bundle-mp3/src/layer3/mod.rs b/symphonia-bundle-mp3/src/layer3/mod.rs index 1079af42..95e85080 100644 --- a/symphonia-bundle-mp3/src/layer3/mod.rs +++ b/symphonia-bundle-mp3/src/layer3/mod.rs @@ -6,6 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use std::fmt; + use symphonia_core::audio::{AudioBuffer, Signal}; use symphonia_core::errors::{Result, decode_error, Error}; use symphonia_core::io::{ReadBitsLtr, BitReaderLtr, BufReader, ReadBytes}; @@ -165,20 +166,37 @@ impl fmt::Debug for GranuleChannel { /// Reads the main_data portion of a MPEG audio frame from a `BitStream` into `FrameData`. fn read_main_data( header: &FrameHeader, + underflow_bits: u32, frame_data: &mut FrameData, state: &mut State, ) -> Result<()> { let main_data = state.resevoir.bytes_ref(); let mut part2_3_begin = 0; + let mut part2_3_skipped = 0; for gr in 0..header.n_granules() { + // If the resevoir underflowed (i.e., main_data_begin references bits not present in the + // resevoir) then skip the granule(s) the missing bits would belong to. + if part2_3_skipped < underflow_bits { + // Zero the samples in the granule channel(s) and sum the part2/3 bits that were + // skipped. + for ch in 0..header.n_channels() { + requantize::zero(&mut state.samples[gr][ch]); + part2_3_skipped += u32::from(frame_data.granules[gr].channels[ch].part2_3_length); + } + + // Adjust the start position of the next granule in the buffer of main data that is + // available. + if part2_3_skipped > underflow_bits { + part2_3_begin = (part2_3_skipped - underflow_bits) as usize; + } + + // Continue at the next granule. + continue; + } + for ch in 0..header.n_channels() { - // This is an unfortunate workaround for something that should be fixed in BitStreamLtr. - // This code repositions the bitstream exactly at the intended start of the next part2_3 - // data. This is to fix files that overread in the Huffman decoder. - // - // TODO: Implement a rewind on the BitStream to undo the last read. let byte_index = part2_3_begin >> 3; let bit_index = part2_3_begin & 0x7; @@ -188,11 +206,10 @@ fn read_main_data( bs.ignore_bits(bit_index as u32)?; } - // Read the scale factors (part2) and get the number of bits read. For MPEG version 1... + // Read the scale factors (part2) and get the number of bits read. let part2_len = if header.is_mpeg1() { bitstream::read_scale_factors_mpeg1(&mut bs, gr, ch, frame_data) } - // For MPEG version 2... else { bitstream::read_scale_factors_mpeg2( &mut bs, @@ -202,7 +219,7 @@ fn read_main_data( let part2_3_length = u32::from(frame_data.granules[gr].channels[ch].part2_3_length); - // The length part2 must be less than or equal to the part2_3_length. + // The part2 length must be less than or equal to the part2_3_length. if part2_len > part2_3_length { return decode_error("mp3: part2_3_length is not valid"); } @@ -220,8 +237,8 @@ fn read_main_data( ); // Huffman decoding errors are returned as an IO error by the bit reader. IO errors are - // unrecoverable, which is not the case for huffman decoding errors. Convert the IO error - // to a decode error. + // unrecoverable, which is not the case for huffman decoding errors. Convert the IO + // error to a decode error. frame_data.granules[gr].channels[ch].rzero = match huffman_result { Ok(rzero) => rzero, Err(Error::IoError(e)) if e.kind() == std::io::ErrorKind::Other => { @@ -275,14 +292,14 @@ pub fn decode_frame( // Buffer main_data into the bit resevoir. let main_data_len = header.frame_size - side_info_len - if header.has_crc { 2 } else { 0 }; - state.resevoir.fill( + let underflow = state.resevoir.fill( &buf[side_info_len..], frame_data.main_data_begin as usize, main_data_len )?; // Read main_data: scale factors and spectral samples. - if let Err(e) = read_main_data(header, &mut frame_data, state) { + if let Err(e) = read_main_data(header, 8 * underflow, &mut frame_data, state) { // The bit reservoir was likely filled with invalid data. Clear it for the next packet. state.resevoir.clear(); return Err(e); diff --git a/symphonia-bundle-mp3/src/layer3/requantize.rs b/symphonia-bundle-mp3/src/layer3/requantize.rs index 313d687b..636d110a 100755 --- a/symphonia-bundle-mp3/src/layer3/requantize.rs +++ b/symphonia-bundle-mp3/src/layer3/requantize.rs @@ -34,6 +34,14 @@ lazy_static! { }; } +/// Zero a sample buffer. +#[inline(always)] +pub(super) fn zero(buf: &mut [f32; 576]) { + for s in buf.iter_mut() { + *s = 0.0; + } +} + /// Reads the Huffman coded spectral samples for a given channel in a granule from a `BitStream` /// into a provided sample buffer. Returns the number of decoded samples (the starting index of the /// rzero partition). @@ -50,9 +58,7 @@ pub(super) fn read_huffman_samples( // If there are no Huffman code bits, zero all samples and return immediately. if part3_bits == 0 { - for sample in buf.iter_mut() { - *sample = 0.0; - } + zero(buf); return Ok(0); }