mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-13 14:12:31 +00:00
added frame sync search for MP3 reading
The first MP3 frame behind metadata blocks is found by searching for frame sync bits. This skips junk bytes between any metadata blocks and the first MP3 frame.
This commit is contained in:
parent
0e8ad5759c
commit
47a28402db
3 changed files with 47 additions and 11 deletions
|
@ -1,4 +1,4 @@
|
|||
use super::header::{verify_frame_sync, Header, XingHeader};
|
||||
use super::header::{search_for_frame_sync, Header, XingHeader};
|
||||
use super::{Mp3File, Mp3Properties};
|
||||
use crate::ape::constants::APE_PREAMBLE;
|
||||
#[cfg(feature = "ape")]
|
||||
|
@ -12,7 +12,7 @@ use crate::id3::{find_id3v1, find_lyrics3v2, ID3FindResults};
|
|||
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
use byteorder::ReadBytesExt;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
pub(super) fn read_from<R>(
|
||||
reader: &mut R,
|
||||
|
@ -80,17 +80,30 @@ where
|
|||
continue;
|
||||
}
|
||||
},
|
||||
_ if verify_frame_sync([header[0], header[1]]) => {
|
||||
let start = reader.seek(SeekFrom::Current(0))? - 4;
|
||||
let header = Header::read(u32::from_be_bytes(header))?;
|
||||
// metadata blocks might be followed by junk bytes before the first MP3 frame begins
|
||||
_ => {
|
||||
// seek back the length of the temporary header buffer
|
||||
// so that all bytes are included in the search for a frame sync
|
||||
let start_of_search_area =
|
||||
reader.seek(SeekFrom::Current(-1 * header.len() as i64))?;
|
||||
if let Some(first_mp3_frame_start_relative) = search_for_frame_sync(reader)? {
|
||||
let first_mp3_frame_start_absolute =
|
||||
start_of_search_area + first_mp3_frame_start_relative;
|
||||
|
||||
file.first_frame_offset = Some(start);
|
||||
// read the first four bytes of the found frame
|
||||
reader.seek(SeekFrom::Start(first_mp3_frame_start_absolute))?;
|
||||
let header = Header::read(reader.read_u32::<BigEndian>()?)?;
|
||||
|
||||
file.first_frame_offset = Some(first_mp3_frame_start_absolute);
|
||||
first_frame_header = Some(header);
|
||||
|
||||
// We have found the first frame
|
||||
break;
|
||||
} else {
|
||||
// the search for sync bits was unsuccessful
|
||||
return Err(LoftyError::Mp3("File contains an invalid frame"));
|
||||
}
|
||||
},
|
||||
_ => return Err(LoftyError::Mp3("File contains an invalid frame")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
BIN
tests/files/assets/b.mp3
Normal file
BIN
tests/files/assets/b.mp3
Normal file
Binary file not shown.
|
@ -1,5 +1,5 @@
|
|||
use crate::{set_artist, temp_file, verify_artist};
|
||||
use lofty::{FileType, ItemKey, ItemValue, TagItem, TagType};
|
||||
use lofty::{Accessor, FileType, ItemKey, ItemValue, TagItem, TagType};
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
|
||||
#[test]
|
||||
|
@ -19,6 +19,29 @@ fn read() {
|
|||
crate::verify_artist!(file, tag, TagType::Ape, "Baz artist", 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_with_junk_bytes_between_frames() {
|
||||
// Read a file that includes an ID3v2.3 data block followed by four bytes of junk data (0x20)
|
||||
let file = lofty::read_from_path("tests/files/assets/b.mp3", true).unwrap();
|
||||
|
||||
// note that the file contains ID3v2 and ID3v1 data
|
||||
assert_eq!(file.file_type(), &FileType::MP3);
|
||||
|
||||
let id3v2_tag = &file.tags()[0];
|
||||
assert_eq!(id3v2_tag.artist(), Some("artist test"));
|
||||
assert_eq!(id3v2_tag.album(), Some("album test"));
|
||||
assert_eq!(id3v2_tag.title(), Some("title test"));
|
||||
assert_eq!(
|
||||
id3v2_tag.get_string(&ItemKey::EncoderSettings),
|
||||
Some("Lavf58.62.100")
|
||||
);
|
||||
|
||||
let id3v1_tag = &file.tags()[1];
|
||||
assert_eq!(id3v1_tag.artist(), Some("artist test"));
|
||||
assert_eq!(id3v1_tag.album(), Some("album test"));
|
||||
assert_eq!(id3v1_tag.title(), Some("title test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write() {
|
||||
let mut file = temp_file!("tests/files/assets/a.mp3");
|
||||
|
|
Loading…
Reference in a new issue