mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-13 22:22:31 +00:00
Add property reading for opus
This commit is contained in:
parent
660a447c50
commit
23f9566f26
6 changed files with 130 additions and 11 deletions
|
@ -5,7 +5,9 @@ use ogg_pager::Page;
|
|||
use crate::{LoftyError, Result};
|
||||
|
||||
pub(crate) mod constants;
|
||||
mod opus;
|
||||
pub(crate) mod read;
|
||||
mod vorbis;
|
||||
pub(crate) mod write;
|
||||
|
||||
pub fn page_from_packet(packet: &mut [u8]) -> Result<Vec<Page>> {
|
||||
|
@ -55,7 +57,7 @@ pub(self) fn reach_metadata<T>(mut data: T, sig: &[u8]) -> Result<()>
|
|||
where
|
||||
T: Read + Seek,
|
||||
{
|
||||
let first_page = Page::read(&mut data)?;
|
||||
let first_page = Page::read(&mut data, false)?;
|
||||
|
||||
let head = first_page.content;
|
||||
let (ident, head) = head.split_at(sig.len());
|
||||
|
|
75
src/components/logic/ogg/opus.rs
Normal file
75
src/components/logic/ogg/opus.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
use crate::{FileProperties, Result, LoftyError};
|
||||
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use ogg_pager::Page;
|
||||
use std::time::Duration;
|
||||
|
||||
pub(in crate::components) fn read_properties<R>(
|
||||
data: &mut R,
|
||||
first_page: Page,
|
||||
stream_len: u64,
|
||||
) -> Result<FileProperties>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
let first_page_abgp = first_page.abgp as i64;
|
||||
|
||||
let mut cursor = Cursor::new(&*first_page.content);
|
||||
|
||||
// Skip identification header and version
|
||||
cursor.seek(SeekFrom::Start(11))?;
|
||||
|
||||
let channels = cursor.read_u8()?;
|
||||
let pre_skip = cursor.read_u16::<LittleEndian>()?;
|
||||
let sample_rate = cursor.read_u32::<LittleEndian>()?;
|
||||
|
||||
let _first_comment_page = Page::read(data, true)?;
|
||||
|
||||
// Skip over the metadata packet
|
||||
loop {
|
||||
let page = Page::read(data, true)?;
|
||||
|
||||
if page.header_type != 1 {
|
||||
data.seek(SeekFrom::Start(page.start as u64))?;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract the identification and metadata packet length from the total
|
||||
let audio_size = stream_len - data.seek(SeekFrom::Current(0))?;
|
||||
|
||||
let next_page = Page::read(data, true)?;
|
||||
|
||||
// Find the last page
|
||||
let mut pages: Vec<Page> = vec![next_page];
|
||||
|
||||
let last_page = loop {
|
||||
if let Ok(current) = Page::read(data, true) {
|
||||
pages.push(current)
|
||||
} else {
|
||||
// Safe to unwrap since the Vec starts off with a Page
|
||||
break pages.pop().unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
let last_page_abgp = last_page.abgp as i64;
|
||||
|
||||
let frame_count = last_page_abgp - first_page_abgp - pre_skip as i64;
|
||||
|
||||
if frame_count < 0 {
|
||||
return Err(LoftyError::InvalidData("OGG file contains incorrect PCM values"))
|
||||
}
|
||||
|
||||
let length = frame_count * 1000 / 48000;
|
||||
let duration = Duration::from_millis(length as u64);
|
||||
let bitrate = (audio_size * 8 / length) as u32;
|
||||
|
||||
Ok(FileProperties {
|
||||
duration,
|
||||
bitrate: Some(bitrate),
|
||||
sample_rate: Some(sample_rate),
|
||||
channels: Some(channels)
|
||||
})
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
use super::{is_metadata, reach_metadata};
|
||||
use crate::{LoftyError, OggFormat, Picture, Result};
|
||||
use crate::components::logic::ogg::constants::OPUSHEAD;
|
||||
use crate::{FileProperties, LoftyError, OggFormat, Picture, Result};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Read, Seek};
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
use crate::components::logic::ogg::{opus, vorbis};
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use ogg_pager::Page;
|
||||
use unicase::UniCase;
|
||||
|
@ -15,6 +17,29 @@ pub type OGGTags = (
|
|||
OggFormat,
|
||||
);
|
||||
|
||||
fn read_properties<R>(data: &mut R, header_sig: &[u8]) -> Result<FileProperties>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
let stream_len = {
|
||||
let current = data.seek(SeekFrom::Current(0))?;
|
||||
let end = data.seek(SeekFrom::End(0))?;
|
||||
data.seek(SeekFrom::Start(current))?;
|
||||
|
||||
end - current
|
||||
};
|
||||
|
||||
let first_page = Page::read(data, false)?;
|
||||
|
||||
let properties = if header_sig == OPUSHEAD {
|
||||
opus::read_properties(data, first_page, stream_len)?
|
||||
} else {
|
||||
vorbis::read_properties(data, first_page, stream_len)?
|
||||
};
|
||||
|
||||
Ok(properties)
|
||||
}
|
||||
|
||||
pub(crate) fn read_from<T>(
|
||||
mut data: T,
|
||||
header_sig: &[u8],
|
||||
|
@ -26,14 +51,14 @@ where
|
|||
{
|
||||
reach_metadata(&mut data, header_sig)?;
|
||||
|
||||
let md_page = Page::read(&mut data)?;
|
||||
let md_page = Page::read(&mut data, false)?;
|
||||
is_metadata(&md_page, comment_sig)?;
|
||||
|
||||
let mut md_pages: Vec<u8> = Vec::new();
|
||||
|
||||
md_pages.extend(md_page.content[comment_sig.len()..].iter());
|
||||
|
||||
while let Ok(page) = Page::read(&mut data) {
|
||||
while let Ok(page) = Page::read(&mut data, false) {
|
||||
if md_pages.len() > 125_829_120 {
|
||||
return Err(LoftyError::TooMuchData);
|
||||
}
|
||||
|
|
17
src/components/logic/ogg/vorbis.rs
Normal file
17
src/components/logic/ogg/vorbis.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use crate::{FileProperties, Result};
|
||||
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use ogg_pager::Page;
|
||||
|
||||
pub(in crate::components) fn read_properties<R>(
|
||||
data: &mut R,
|
||||
first_page: Page,
|
||||
stream_len: u64,
|
||||
) -> Result<FileProperties>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
Ok(FileProperties::default())
|
||||
}
|
|
@ -88,7 +88,7 @@ fn vorbis_write(
|
|||
c.seek(SeekFrom::End(0))?;
|
||||
|
||||
loop {
|
||||
let p = Page::read(&mut data)?;
|
||||
let p = Page::read(&mut data, false)?;
|
||||
|
||||
if p.header_type != 1 {
|
||||
data.seek(SeekFrom::Start(p.start as u64))?;
|
||||
|
@ -187,14 +187,14 @@ fn vorbis_write(
|
|||
}
|
||||
|
||||
fn write_to(mut data: &mut File, pages: &mut [Page], sig: &[u8]) -> Result<()> {
|
||||
let first_page = Page::read(&mut data)?;
|
||||
let first_page = Page::read(&mut data, false)?;
|
||||
|
||||
let ser = first_page.serial;
|
||||
|
||||
let mut writer = Vec::new();
|
||||
writer.write_all(&*first_page.as_bytes())?;
|
||||
|
||||
let first_md_page = Page::read(&mut data)?;
|
||||
let first_md_page = Page::read(&mut data, false)?;
|
||||
is_metadata(&first_md_page, sig)?;
|
||||
|
||||
#[cfg(feature = "format-vorbis")]
|
||||
|
@ -208,7 +208,7 @@ fn write_to(mut data: &mut File, pages: &mut [Page], sig: &[u8]) -> Result<()> {
|
|||
let mut remaining = Vec::new();
|
||||
|
||||
loop {
|
||||
let p = Page::read(&mut data)?;
|
||||
let p = Page::read(&mut data, false)?;
|
||||
|
||||
if p.header_type != 1 {
|
||||
data.seek(SeekFrom::Start(p.start as u64))?;
|
||||
|
|
|
@ -161,7 +161,7 @@ impl TryFrom<metaflac::Tag> for OggTag {
|
|||
},
|
||||
properties: FileProperties::default(), // TODO
|
||||
_format: TagType::Ogg(OggFormat::Flac),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Err(LoftyError::InvalidData(
|
||||
|
@ -414,7 +414,7 @@ impl AudioTagWrite for OggTag {
|
|||
|
||||
#[cfg(any(feature = "format-opus", feature = "format-vorbis"))]
|
||||
{
|
||||
let p = ogg_pager::Page::read(file)?;
|
||||
let p = ogg_pager::Page::read(file, false)?;
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
|
||||
#[cfg(feature = "format-opus")]
|
||||
|
|
Loading…
Reference in a new issue