Add property reading for opus

This commit is contained in:
Serial 2021-07-21 16:24:42 -04:00
parent 660a447c50
commit 23f9566f26
6 changed files with 130 additions and 11 deletions

View file

@ -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());

View 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)
})
}

View file

@ -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);
}

View 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())
}

View file

@ -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))?;

View file

@ -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")]